File Coverage

blib/lib/Cisco/Reconfig.pm
Criterion Covered Total %
statement 387 424 91.2
branch 206 304 67.7
condition 60 99 60.6
subroutine 42 45 93.3
pod 15 36 41.6
total 710 908 78.1


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