File Coverage

blib/lib/Net/Info.pm
Criterion Covered Total %
statement 24 422 5.6
branch 1 304 0.3
condition 0 99 0.0
subroutine 9 45 20.0
pod 0 36 0.0
total 34 906 3.7


line stmt bran cond sub pod time code
1             package Net::Info;
2              
3             @ISA = qw( Exporter );
4             @EXPORT = qw( readconfig );
5             @EXPORT_OK = qw( readconfig stringconfig $minus_one_indent_rx );
6              
7             $VERSION = '0.102';
8              
9             require Exporter;
10 1     1   68363 use strict;
  1         3  
  1         31  
11 1     1   534 use Text::Tabs;
  1         764  
  1         125  
12 1     1   8 use Carp;
  1         2  
  1         54  
13 1     1   5 use Carp qw( verbose confess );
  1         10  
  1         117  
14 1     1   488 use IO::File;
  1         8730  
  1         116  
15 1     1   9 use Scalar::Util qw( weaken );
  1         2  
  1         136  
16             my $iostrings;
17             our $allow_minus_one_indent = qr/class /;
18             our $allow_plus_one_indent = qr/service-policy |quit$/;
19             our $bad_indent_policy = 'DIE';
20              
21              
22             BEGIN {
23 1     1   76 eval " use IO::String ";
  1     1   223  
  0         0  
  0         0  
24 1 50       183 $iostrings = $@ ? 0 : 1;
25             }
26              
27              
28             my $debug_get = 0;
29             my $debug_mget = 0;
30             my $debug_set = 0;
31             my $debug_context = 0;
32             my $debug_text = 0;
33             my $ddata = $debug_get
34             || $debug_mget
35             || $debug_set
36             || $debug_context
37             || $debug_text
38             || 0; # add debugging data to data structures
39              
40             my $spec = qr{^ };
41             my $text = " text";
42             my $subs = " subs";
43             my $next = " next";
44             my $cntx = " cntx";
45             my $word = " word";
46             my $seqn = " seqn";
47             my $dupl = " dupl";
48             my $debg = " debg";
49             my $bloc = " bloc";
50             my $UNDEFDESC = "! undefined\n";
51             my $undef = bless { $debg => $UNDEFDESC, $text => '' }, __PACKAGE__;
52             my $dseq = "O0000000";
53             our $nonext;
54              
55             my $line;
56             my $fh;
57              
58             use overload
59 1         8 'bool' => \&defined,
60             '""' => \&text,
61 1     1   1331 'fallback' => 1;
  1         987  
62              
63             sub stringconfig {
64 0 0   0 0   Carp::croak 'IO::Strings need to be installed to use "stringconfig"'
65             . ' install it or use "readconfig" instead.' unless $iostrings;
66 0           readconfig(IO::String->new(join("\n", @_)));
67             }
68              
69             sub readconfig {
70 0     0 0   my ($file) = @_;
71 0 0         $fh = ref($file) ? $file : IO::File->new($file, "r");
72 0           $line = <$fh>;
73 0           return rc1(0, 'aaaa', $undef, "! whole enchalada\n");
74             }
75              
76             sub rc1 {
77 0     0 0   my ($indent, $seq, $parent, $dcon) = @_;
78 0           my $config = bless { $bloc => 1 }, __PACKAGE__;
79 0 0         $config->{$debg} = "BLOCK:$dseq:$dcon" if $ddata;
80              
81 0           $config->{$cntx} = $parent;
82 0           weaken $config->{$cntx};
83              
84 0           $dseq ++;
85 0           my ($last, $prev, $ciscobug);
86 0           for ( ; $line; $prev = $line, $line = <$fh> ) {
87 0           $_ = $line;
88 0           s/^( *)//;
89 0           my $in = length($1);
90 0           s/^(no +)//;
91             #新行向右缩进
92 0 0         if ( $in > $indent ) {
    0          
93             #如果存在上下文,则将新行视为上一配置的子节点;
94 0 0         if ( $last ) {
95 0           $last->{$subs} = rc1($in, "$last->{$seqn}aaa", $last, $line);
96 0           undef $last;
97 0 0         redo if $line;
98             }
99             else {
100             #正常缩进不会出现此情况,以下代码用来捕捉特例缩进代码
101 0 0 0       if ( $indent + 1 == $in && $allow_plus_one_indent && $line =~ /^\s*$allow_plus_one_indent/ ) {
      0        
102 0           $indent = $indent + 1;
103 0           redo;
104             }
105 0 0 0       if ( $indent != 0 || ($prev ne "!\n" && $prev !~ /^!.*$/) ) {
      0        
106 0 0         if ( $bad_indent_policy eq 'IGNORE' ) {
    0          
107             # okay then
108             }
109             elsif ( $bad_indent_policy eq 'WARN' ) {
110 0           warn "Unexpected indentation change <$.:$_>";
111             }
112             else {
113 0           confess "Unexpected indentation change <$.:$_>";
114             }
115             }
116 0           $ciscobug = 1;
117 0           $indent = $in;
118             }
119             }
120             #新行想左缩进
121             elsif ( $in < $indent ) {
122             #匹配到异常缩进并且重写缩进为0
123 0 0 0       if ( $ciscobug && $in == 0 ) {
    0 0        
      0        
      0        
124 0           $indent = 0;
125             }
126             #存在上下文环境
127             elsif ( $last && $indent - 1 == $in && $allow_minus_one_indent && $line =~ /^\s*$allow_minus_one_indent/ ) {
128 0 0         confess unless $last->{$seqn};
129 0           $last->{$subs} = rc1($in, "$last->{$seqn}aaa", $last, $line);
130 0           undef $last;
131 0 0         redo if $line;
132             }
133             else {
134 0           return $config;
135             }
136             }
137 0 0         next if /^$/;
138 0 0         next if /^\s*!/;
139 0           my $context = $config;
140 0           my (@x) = split;
141 0           my $owords = @x;
142 0   0       while ( @x && ref $context->{$x[0]} ) {
143 0           $context = $context->{$x[0]};
144 0           shift @x;
145             }
146 0 0         if ( ! @x ) {
    0          
147             # A duplicate line. Not fun.
148             # As far as we know this can only occur as a remark inside
149             # filter list.
150             # Q: what's the point of keeping track of these? Need to be
151             # able to accurately dump filter list definitions
152             #
153             $context->{$dupl} = []
154 0 0         unless $context->{$dupl};
155             my $n = bless {
156             $ddata
157             ? ($debg => "$dseq:DUP:$line",
158 0 0         $word => $context->{$word},)
159             : (),
160             }, __PACKAGE__;
161 0           $dseq ++;
162              
163 0           push(@{ $context->{$dupl} }, $n);
  0            
164 0           $context = $n;
165             }
166             elsif ( defined $context->{$x[0]} ) {
167 0           confess "already $.: '$x[0]' $line";
168             }
169 0           while ( @x ) {
170 0           my $x = shift @x;
171 0 0         confess unless defined $x;
172 0 0         confess unless defined $dseq;
173 0 0         $line = "" unless defined $line;
174 0 0         $context = $context->{$x} = bless {
175             $ddata
176             ? ($debg => "$dseq:$x:$line",
177             $word => $x,)
178             : (),
179             }, __PACKAGE__;
180 0           $dseq ++;
181             }
182 0           $context->{$seqn} = $seq ++;
183 0           $context->{$text} = $line;
184 0 0         confess if $context->{$cntx};
185              
186 0           $context->{$cntx} = $config;
187 0           weaken $context->{$cntx};
188              
189 0 0         unless ( $nonext ) {
190 0 0         if ( $last ) {
191 0           $last->{$next} = $context;
192 0           weaken $last->{$next};
193             }
194             else {
195 0           $config->{$next} = $context;
196 0           weaken $config->{$next};
197             }
198             }
199              
200 0           $last = $context;
201              
202 0 0 0       if ( $line &&
      0        
      0        
203             ($line =~ /(\^C)/ && $line !~ /\^C.*\^C/)
204             ||
205             ($line =~ /banner [a-z\-]+ ((?!\^C).+)/) ) {
206             #
207             # big special case for banners 'cause they don't follow
208             # normal indenting rules
209             #
210 0 0         die unless defined $1;
211 0           my $sep = qr/\Q$1\E/;
212 0           my $sub = $last->{$subs} = bless { $bloc => 1 }, __PACKAGE__;
213 0           $sub->{$cntx} = $last;
214 0           weaken $sub->{$cntx};
215 0           my $subnull = $sub->{''} = bless { $bloc => 1, $dupl => [] }, __PACKAGE__;
216 0           $subnull->{$cntx} = $sub;
217 0           weaken $subnull->{$cntx};
218 0           for ( ;; ) {
219 0           $line = <$fh>;
220 0 0         last unless $line;
221 0 0         my $l = bless {
222             $ddata ? ($debg => "$dseq:DUP:$line") : (),
223             }, __PACKAGE__;
224 0           $dseq ++;
225 0           $l->{$seqn} = $seq ++;
226 0           $l->{$text} = $line;
227 0           $l->{$cntx} = $subnull;
228 0           weaken($l->{$cntx});
229 0           push(@{ $subnull->{$dupl} }, $l);
  0            
230 0 0         last if $line =~ /$sep[\r]?$/;
231             }
232 0 0 0       warn "parse probably failed"
233             unless $line && $line =~ /$sep[\r]?$/;
234             }
235             }
236 0           return $config;
237             }
238              
239             #sub word { $_[0]->{$word} };
240 0     0 0   sub block { $_[0]->{$bloc} }
241 0 0 0 0 0   sub seqn { $_[0]->{$seqn} || $_[0]->endpt->{$seqn} || confess };
242 0 0 0 0 0   sub subs { $_[0]->{$subs} || $_[0]->zoom->{$subs} || $undef };
243 0 0 0 0 0   sub next { $_[0]->{$next} || $_[0]->zoom->{$next} || $undef };
244             #sub undefined { $_[0] eq $undef }
245             #sub defined { $_[0] ne $undef }
246 0 0   0 0   sub defined { $_[0]->{$debg} ? $_[0]->{$debg} ne $UNDEFDESC : 1 }
247              
248             sub destroy {
249 0     0 0   warn "Cisco::Reconfig::destroy is deprecated";
250             }
251              
252             sub single {
253 0     0 0   my ($self) = @_;
254 0 0         return $self if defined $self->{$text};
255 0           my (@p) = grep (! /$spec/o, keys %$self);
256 0 0         return undef if @p > 1;
257 0 0         return $self unless @p;
258 0   0       return $self->{$p[0]}->single || $self;
259             }
260              
261             sub kids {
262 0     0 0   my ($self) = @_;
263 0 0         return $self if ! $self;
264 0           my (@p) = $self->sortit(grep (! /$spec/o, keys %$self));
265 0 0         return $self if ! @p;
266 0           return(map { $self->{$_} } @p);
  0            
267             }
268              
269             sub zoom {
270 0     0 0   my ($self) = @_;
271 0 0         return $self if defined $self->{$text};
272 0           my (@p) = $self->sortit(grep (! /$spec/o, keys %$self));
273 0 0         return $self if @p > 1;
274 0 0         return $self unless @p;
275 0           return $self->{$p[0]}->zoom;
276             }
277              
278             sub endpt {
279 0     0 0   my ($self) = @_;
280 0 0         return $self if ! $self;
281 0           my (@p) = grep (! /$spec/o, keys %$self);
282 0 0 0       return $self if defined($self->{$text}) && ! @p;
283 0 0         confess unless @p;
284 0           return $self->{$p[0]}->endpt;
285             }
286              
287              
288             sub text {
289 0     0 0   my ($self) = @_;
290 0 0         return '' unless $self;
291 0 0         if ( defined $self->{$text} ) {
292             return $debug_text
293             ? $self->{$word} . " " . $self->{$text}
294 0 0         : $self->{$text};
295             }
296 0           my (@p) = $self->sortit(grep (! /$spec/o, keys %$self));
297 0 0         if ( @p > 1 ) {
    0          
298             #
299             # This is nasty because the lines may not be ordered
300             # in the tree-hiearchy used by Cisco::Reconfig
301             #
302 0           my %temp = map { $self->{$_}->sequenced_text(0) } @p;
  0            
303 0           return join('', map { $temp{$_} } sort keys %temp);
  0            
304             }
305             elsif ( $self->{$dupl} ) {
306 0 0         return join('', map { $_->{$word} . " " . $_->{$text} } @{ $self->{$dupl} })
  0            
  0            
307             if $debug_text;
308 0           return join('', map { $_->{$text} } @{ $self->{$dupl} });
  0            
  0            
309             }
310 0 0         confess unless @p;
311 0           return $self->{$p[0]}->text;
312             }
313              
314             sub sequenced_text {
315 0     0 0   my ($self, $all) = @_;
316 0           my @t = ();
317 0 0         if ( defined $self->{$text} ) {
318             push(@t, $debug_text
319             ? ($self->seqn => $self->{$word} . " " . $self->{$text})
320 0 0         : ($self->seqn => $self->{$text}));
321             }
322 0 0         if ( exists $self->{$dupl} ) {
323             push(@t, $debug_text
324 0           ? map { $_->seqn => $_->{$word} . " " . $_->{$text} } @{ $self->{$dupl} }
  0            
325 0 0         : map { $_->seqn => $_->{$text} } @{ $self->{$dupl} });
  0            
  0            
326             }
327 0           my (@p) = $self->sortit(grep (! /$spec/o, keys %$self));
328 0 0         if ( @p ) {
329             #
330             # This is nasty because the lines may not be ordered
331             # in the tree-hiearchy used by Cisco::Reconfig
332             #
333 0           return(@t, map { $self->{$_}->sequenced_text($all) } @p);
  0            
334             }
335             push(@t, $self->{$subs}->sequenced_text($all))
336 0 0 0       if $all && $self->{$subs};
337 0 0         return @t if @t;
338 0 0         confess unless @p;
339 0           return $self->{$p[0]}->sequenced_text($all);
340             }
341              
342             sub alltext {
343 0     0 0   my ($self) = @_;
344 0 0         return '' unless $self;
345 0           my %temp = $self->sequenced_text(1);
346 0           return join('', map { $temp{$_} } sort keys %temp);
  0            
347             }
348              
349             sub chomptext {
350 0     0 0   my ($self) = @_;
351 0           my $t = $self->text;
352 0           chomp($t);
353 0           return $t;
354             }
355              
356             sub returns {
357 0     0 0   my (@o) = @_;
358 0           for my $o ( @o ) {
359 0 0 0       $o .= "\n"
360             if defined($o) && $o !~ /\n$/;
361             }
362 0 0         return $o[0] unless wantarray;
363 0           return @o;
364             }
365              
366             sub openangle {
367 0   0 0 0   my (@l) = grep (defined && / \S /, @_);
368 0           my $x = 0;
369 0           for my $l ( @l ) {
370 0           substr($l, 0, 0) = (' ' x $x ++);
371             }
372 0 0         return $l[0] unless wantarray;
373 0           return @l;
374             }
375              
376             sub closeangle {
377 0   0 0 0   my (@l) = grep (defined && / \S /, @_);
378 0           my $x = $#l;
379 0           for my $l ( @l ) {
380 0           substr($l, 0, 0) = (' ' x $x --);
381             }
382 0 0         return $l[0] unless wantarray;
383 0           return @l;
384             }
385              
386             sub context {
387             defined($_[0]->{$cntx})
388             ? $_[0]->{$cntx}
389 0 0 0 0 0   : $_[0]->endpt->{$cntx}
390             || ($_[0] ? confess "$_[0]" : $undef)
391             };
392              
393             #
394             # interface Loopback7
395             # ip address x y
396             #
397              
398             sub setcontext {
399 0     0 0   my ($self, @extras) = @_;
400 0 0         print STDERR "\nSETCONTEXT\n" if $debug_context;
401 0 0         unless ( $self->block ) {
402 0 0         print STDERR "\nNOT_A_BLOCK $self->{$debg}\n" if $debug_context;
403 0           $self = $self->context;
404             }
405             printf STDERR "\nSELF %sCONTEXT %sCCONTEXT %sEXTRAS$#extras @extras\n",
406             $self->{$debg}, $self->context->{$debg},
407 0 0         $self->context->context->{$debg}
408             if $debug_context;
409 0           my $x = $self->context;
410             return(grep defined,
411             $x->context->setcontext,
412 0 0         trim($x->zoom->{$text}),
413             @extras)
414             if $x;
415 0           return @extras;
416             }
417              
418             sub contextcount {
419 0     0 0   my $self = shift;
420 0           my (@a) = $self->setcontext(@_);
421 0 0         printf STDERR "CONTEXTCOUNT = %d\n", scalar(@a) if $debug_context;
422 0 0         print STDERR map { "CC: $_\n" } @a if $debug_context;
  0            
423 0           return scalar(@a);
424             }
425              
426             sub unsetcontext {
427 0     0 0   my $self = shift;
428 0           return(("exit") x $self->contextcount(@_));
429             }
430              
431             sub teql {
432 0     0 0   my ($self, $b) = @_;
433 0           my $a = $self->text;
434 0           $a =~ s/^\s+/ /g;
435 0           $a =~ s/^ //;
436 0           $a =~ s/ $//;
437 0           chomp($a);
438 0           $b =~ s/^\s+/ /g;
439 0           $b =~ s/^ //;
440 0           $b =~ s/ $//;
441 0           chomp($b);
442 0           return $a eq $b;
443             }
444              
445             sub set {
446 0     0 0   my $self = shift;
447 0           my $new = pop;
448 0           my (@designators) = @_;
449             #my ($self, $designator, $new) = @_;
450 0 0         print STDERR "\nSET\n" if $debug_set;
451 0 0         return undef unless $self;
452 0           my $old;
453             #my @designators;
454 0 0         print STDERR "\nSELF $self->{$debg}" if $debug_set;
455             # move into the block if possible
456 0 0         $self = $self->subs
457             if $self->subs;
458 0 0         print STDERR "\nSELF $self->{$debg}" if $debug_set;
459             #if (ref $designator eq 'ARRAY') {
460             # @designators = @$designator;
461             # $old = $self->get(@designators);
462             # $designator = pop(@designators);
463             #} elsif ($designator) {
464             # $old = $self->get($designator);
465             #} else {
466             # $old = $self;
467             #}
468 0           my $designator;
469 0 0         if ( @designators ) {
470 0           $old = $self->get(@designators);
471 0           $designator = pop(@designators);
472             }
473             else {
474 0           $old = $self;
475             }
476 0 0         print STDERR "\nOLD $old->{$debg}" if $debug_set;
477 0           my (@lines) = expand(grep (/./, split(/\n/, $new)));
478 0 0         if ( $lines[0] =~ /^(\s+)/ ) {
479 0           my $ls = $1;
480 0           my $m = 1;
481 0 0         map { substr($_, 0, length($ls)) eq $ls or $m = 0 } @lines;
  0            
482 0 0         map { substr($_, 0, length($ls)) = '' } @lines
  0            
483             if $m;
484             }
485 0           my $indent = (' ' x $self->contextcount(@designators));
486 0           for $_ ( @lines ) {
487 0           s/(\S)\s+/$1 /g;
488 0           s/\s+$//;
489 0 0         $_ = 'exit' if /^\s*!\s*$/;
490 0           $_ = "$indent$_";
491             }
492 0 0         print STDERR "SET TO {\n@lines\n}\n" if $debug_set;
493 0           my $desig = shift(@lines);
494 0           my @o;
495 0 0         undef $old
496             if ! $old;
497 0 0 0       if ( ! $old ) {
    0          
    0          
498 0 0         print STDERR "NO OLD\n" if $debug_set;
499 0           push(@o, openangle($self->setcontext(@designators)));
500 0           push(@o, $desig);
501             }
502             elsif ( ! $designator && ! looks_like_a_block($desig, @lines) ) {
503 0 0 0       if ( $self->block && $self->context ) {
504 0           unshift(@lines, $desig);
505 0           $old = $self->context;
506 0           undef $desig;
507             }
508             else {
509 0           unshift(@lines, $desig);
510 0 0         print STDERR "IN NASTY BIT\n" if $debug_set;
511             #
512             # this is a messy situation: we've got a random
513             # block of stuff to set inside a random block.
514             # In theorey we could avoid the die, I'll leave
515             # that as an exercise for the reader.
516             #
517 0 0         confess "You cannot set nested configurations with set(undef, \$config) -- use a designator on the set method"
518             if grep (/^$indent\s/, @lines);
519 0           my (@t) = split(/\n/, $self->text);
520 0           my (%t);
521 0           @t{strim(@t)} = @t;
522 0           while ( @lines ) {
523 0           my $l = strim(shift(@lines));
524 0 0         if ( $t{$l} ) {
525 0           delete $t{$l};
526             }
527             else {
528 0           push(@o, "$indent$l");
529             }
530             }
531 0           for my $k ( keys %t ) {
532 0           unshift(@o, iinvert($indent, $k));
533             }
534 0 0         unshift(@o, $self->setcontext)
535             if @o;
536             }
537             }
538             elsif ( $old->teql($desig) ) {
539 0 0         print STDERR "DESIGNATOR EQUAL\n" if $debug_set;
540             # okay
541             }
542             else {
543 0 0         print STDERR "DESIGNATOR DIFERENT\n" if $debug_set;
544 0           push(@o, openangle($self->setcontext(@designators)));
545 0 0         if ( defined $designator ) {
546 0           push(@o, iinvert($indent, $designator));
547             }
548             else {
549 0           push(@o, iinvert($indent, split(/\n/, $self->text)));
550             }
551 0           push(@o, $desig);
552             }
553 0 0         if ( @lines ) {
554 0 0 0       if ( $old && ! @o && $old->subs && $old->subs->next ) {
      0        
      0        
555 0 0         print STDERR "OLD= $old->{$debg}" if $debug_set;
556 0           my $ok = 1;
557 0           my $f = $old->subs->next;
558 0 0         print STDERR "F= $f->{$debg}" if $debug_set;
559 0           for my $l ( @lines ) {
560 0 0         next if $l =~ /^\s*exit\s*$/;
561 0 0         next if $f->teql($l);
562 0 0         print STDERR "LINE DIFF ON $l\n" if $debug_set;
563 0           $ok = 0;
564 0           last;
565             }
566             continue {
567 0           $f = $f->next;
568 0 0         print STDERR "F= $f->{$debg}" if $debug_set;
569             }
570 0 0 0       if ( ! $ok || $f ) {
571 0           push(@o, openangle($self->setcontext(@designators)));
572 0           push(@o, iinvert($indent, $designator));
573 0           push(@o, $desig);
574             }
575             }
576 0 0         push(@o, @lines) if @o;
577             }
578 0           @o = grep (defined, @o);
579 0 0         push(@o, closeangle($self->unsetcontext(@designators)))
580             if @o;
581 0 0         return join('', returns(@o)) unless wantarray;
582 0           return returns(@o);
583             }
584              
585             sub looks_like_a_block {
586 0     0 0   my ($first, @l) = @_;
587 0           my $last = pop(@l);
588 0 0         return 1 if ! defined $last;
589 0 0         return 0 if grep (/^\S/, @l);
590 0 0         return 0 if $first =~ /^\s/;
591 0 0         return 0 if $last =~ /^\s/;
592 0           return 1;
593             }
594              
595             sub iinvert {
596 0     0 0   my ($indent, @l) = @_;
597 0 0         confess unless @l;
598 0           for $_ ( @l ) {
599 0 0         next unless defined;
600 0 0         s/^\s*no /$indent/ or s/^\s*(\S)/${indent}no $1/
601             }
602 0 0         return $l[0] unless wantarray;
603 0           return @l;
604             }
605              
606             sub all {
607 0     0 0   my ($self, $regex) = @_;
608 0           $self = $self->zoom;
609 0 0 0       return(map { $self->{$_} } $self->sortit(grep (/$regex/ && ! /$spec/o, keys %$self)))
  0            
610             if $regex;
611 0           return(map { $self->{$_} } $self->sortit(grep (! /$spec/o, keys %$self)));
  0            
612             }
613              
614             sub get {
615 0     0 0   my ($self, @designators) = @_;
616 0 0 0       return $self->mget(@designators)
617             if wantarray && @designators > 1;
618              
619 0 0         print STDERR "\nGET <@designators> $self->{$debg}" if $debug_get;
620              
621 0 0         return $self unless $self;
622 0           my $zoom = $self->zoom->subs;
623 0 0         $self = $zoom if $zoom;
624              
625 0 0         print STDERR "\nZOOMSUB $self->{$debg}" if $debug_get;
626              
627 0           while ( @designators ) {
628 0           my $designator = shift(@designators);
629             # $self = $self->zoom;
630             # $self = $self->single || $self;
631 0 0         print STDERR "\nDESIGNATOR: $designator. ZOOMED: $self->{$debg}\n"
632             if $debug_get;
633 0           for my $d ( split(' ', $designator) ) {
634 0 0         print STDERR "\nDO WE HAVE A: $d?\n" if $debug_get;
635 0 0         return $undef unless $self->{$d};
636 0           $self = $self->{$d};
637 0 0         print STDERR "\nWE DO: $self->{$debg}\n" if $debug_get;
638             }
639 0 0         last unless @designators;
640 0 0         if ( $self->single ) {
641 0           $self = $self->subs;
642 0 0         print STDERR "\nSINGLETON: $self->{$debg}\n" if $debug_get;
643             }
644             else {
645 0 0         print STDERR "\nNOT SINGLE\n" if $debug_get;
646 0           return $undef;
647             }
648             }
649 0 0         print STDERR "\nDONE\n" if $debug_get;
650 0 0         if ( wantarray ) {
651 0           $self = $self->zoom;
652 0           my (@k) = $self->kids;
653 0 0         return @k if @k;
654 0           return $self;
655             }
656 0           return $self;
657             }
658              
659             sub strim {
660 0     0 0   my (@l) = @_;
661 0           for $_ ( @l ) {
662 0           s/^\s+//;
663 0           s/\s+$//;
664 0           s/\n$//;
665             }
666 0 0         return $l[0] unless wantarray;
667 0           return @l;
668             }
669              
670             sub trim {
671 0     0 0   my (@l) = @_;
672 0           for $_ ( @l ) {
673 0           s/^\s+//;
674 0           s/\s+$//;
675             }
676 0 0         return $l[0] unless wantarray;
677 0           return @l;
678             }
679              
680             sub display {
681 0     0 0   my ($self) = @_;
682 0           my @o;
683 0           push(@o, $self->setcontext);
684             push(@o, trim($self->single->{$text}))
685 0 0 0       if $self->single && $self->single->{$text}
      0        
686             && $self->subs->undefined;
687 0 0         push(@o, "! the whole enchalada")
688             if $self->context->undefined;
689 0           my (@r) = returns(openangle(@o));
690 0 0         return @r if wantarray;
691 0           return join('', @r);
692             }
693              
694             sub callerlevels {
695 0     0 0   my $n = 1;
696 0           1 while caller($n ++);
697 0           return $n;
698             }
699              
700             sub mget {
701 0     0 0   my ($self, @designators) = @_;
702              
703 0           my $cl = callerlevels;
704 0           my @newset;
705 0 0         if ( @designators > 1 ) {
706              
707 0 0         print STDERR "\nGET$cl $designators[0]----------\n" if $debug_mget;
708              
709 0           my (@set) = $self->get(shift @designators);
710 0           for my $item ( @set ) {
711              
712 0 0         print STDERR "\nMGET$cl $item ----------\n" if $debug_mget;
713 0 0         print STDERR "\nMGET$cl $item->{$debg}\n" if $debug_mget;
714              
715 0           my (@got) = $item->mget(@designators);
716              
717 0 0         print STDERR map { "\nRESULTS$cl: $_->{$debg}\n" } @got
  0            
718             if $debug_mget;
719              
720 0           push(@newset, @got);
721             }
722             }
723             else {
724              
725 0 0         print STDERR "\nxGET$cl $designators[0] -------\n" if $debug_mget;
726              
727 0           (@newset) = $self->get(shift @designators);
728              
729 0 0         print STDERR map { "\nxRESULTS$cl: $_->{$debg}\n" } @newset
  0            
730             if $debug_mget;
731              
732             }
733 0           return @newset;
734             }
735              
736             sub sortit {
737 0     0 0   my $self = shift;
738 0           return sort { $self->{$a}->seqn cmp $self->{$b}->seqn } @_;
  0            
739             }
740              
741             1;