File Coverage

blib/lib/Data/Walker.pm
Criterion Covered Total %
statement 261 446 58.5
branch 129 270 47.7
condition 27 93 29.0
subroutine 18 26 69.2
pod 1 19 5.2
total 436 854 51.0


line stmt bran cond sub pod time code
1             #---------------------------------------------------------------------------
2              
3             package Data::Walker;
4              
5             # Copyright (c) 1999,2000 John Nolan. All rights reserved.
6             # This program is free software. You may modify and/or
7             # distribute it under the same terms as Perl itself.
8             # This copyright notice must remain attached to the file.
9             #
10             # You can run this file through either pod2text, pod2man or
11             # pod2html to produce pretty documentation in text, manpage or
12             # html file format (these utilities are part of the
13             # Perl 5 distribution).
14              
15 2     2   339241 use Data::Dumper;
  2         22531  
  2         141  
16 2     2   15 use overload; # We will use overload::StrVal()
  2         4  
  2         11  
17              
18 2     2   81 use vars qw( $VERSION @ISA $AUTOLOAD @EXPORT %EXPORT_TAGS );
  2         10  
  2         203  
19 2     2   16 use vars qw( $WALKER %Config @Commands %Commands @ExportedCommands );
  2         4  
  2         263  
20              
21             require Exporter;
22             @ISA = qw(Exporter);
23              
24             @Commands = qw/ls ll la all lla lal cd pwd
25             print type cat dump show set walk cli/;
26             @ExportedCommands = (@Commands, qw/unwalk/);
27              
28             @EXPORT_OK = @ExportedCommands;
29             %EXPORT_TAGS = ( direct => [ @ExportedCommands ] );
30              
31             push @Commands, qw/chdir/; # chdir is not exported
32              
33 2     2   9 use strict;
  2         4  
  2         13382  
34              
35             $VERSION = '1.05';
36 0     0 1 0 sub Version { $VERSION };
37              
38              
39             ####################################################################
40             # ---{ B E G I N P O D D O C U M E N T A T I O N }--------------
41             #
42              
43             =head1 NAME
44              
45             B - A tool for navigating through Perl data structures
46              
47             =head1 SYNOPSIS
48              
49             Without any explicit objects:
50              
51             use Data::Walker;
52             Data::Walker->cli( $data_structure );
53              
54             Object-style invocation:
55              
56             use Data::Walker;
57             my $w = new Data::Walker;
58             $w->walk( $data_structure );
59             $w->ls("-al");
60             $w->pwd;
61             $w->cli;
62              
63             Importing methods into the current package:
64              
65             use Data::Walker qw(:direct);
66             walk $data_structure;
67             ls "-al";
68             pwd;
69             cli;
70              
71             =head1 DESCRIPTION
72              
73             This module allows you to "walk" an arbitrary Perl data
74             structure in the same way that you can walk a directory tree
75             from a UNIX command line. It reuses familiar unix commands
76             (such as "ls", "cd", "pwd") and applies these to data structures.
77              
78             It has a command-line interface which behaves like a UNIX shell.
79             You can also use object-style sytax to invoke the CLI commands from
80             outside the CLI. Data::Walker objects are encapsulated,
81             so that you can hop into and out of a CLI without losing state,
82             and you can have several Data::Walker objects pointing at
83             different structures.
84              
85             The main functions can also be imported and used directly
86             from within the Perl debugger's CLI.
87              
88             =head1 INSTALLATION
89              
90             To install this package, just into the directory which
91             you created by untarring the package, and type the following:
92              
93             perl Makefile.PL
94             make test
95             make
96             make install
97              
98             This will copy Walker.pm to your perl library directory for
99             use by all perl scripts. You probably must be root to do this,
100             unless you have installed a personal copy of perl or you have
101             write access to a Perl lib directory.
102              
103              
104             =head1 USAGE
105              
106             You open a command-line interface by invoking the cli() function.
107              
108             use Data::Walker;
109             Data::Walker->cli( $data_structure );
110              
111             You can customize certain features, like so:
112              
113             use Data::Walker;
114             $Data::Walker::Config{'skipdoublerefs'} = 0;
115             Data::Walker->cli( $data_structure );
116              
117             If you prefer to use object-style notation, then you
118             can use this syntax to customize the settings.
119             You can invoke the walk() method directly, our you
120             can let the cli() method call walk() implicitly:
121              
122             use Data::Walker;
123             my $w1 = new Data::Walker;
124             $w1->walk( $data_structure );
125             $w1->cli;
126              
127             my $w2 = new Data::Walker;
128             $w2->cli( $data_structure );
129              
130             my $w3 = new Data::Walker( 'skipdoublerefs' => 0 );
131             $w3->walk( $data_structure );
132             $w3->cli();
133            
134             $w3->showrecursion(0);
135             $w3->cli();
136              
137             You can also import most of the functions directly into
138             the current package. This is especially useful from within
139             the debugger (see the example below).
140              
141             use Data::Walker qw(:direct);
142             walk $data_structure;
143             ls "-al";
144             pwd;
145             cli;
146              
147             When you use the :direct pragma and invoke the walk() function,
148             a Data::Walker object is implicitly created, and is available
149             as $Data::Walker::WALKER.
150              
151             Imagine a data structure like so:
152              
153             my $s = {
154              
155             a => [ 10, 20, "thirty" ],
156             b => {
157             "w" => "forty",
158             "x" => "fifty",
159             "y" => 60,
160             "z" => \70,
161             },
162             c => sub { print "I'm a data structure!\n"; },
163             d => 80,
164             };
165             $s->{e} = \$s->{d};
166              
167              
168             Here is a sample CLI session examining this structure ('/>' is the prompt):
169              
170              
171             />
172             /> ls -l
173             a ARRAY (3)
174             b HASH (4)
175             c CODE
176             d scalar 80
177             e SCALAR 80
178             /> cd a
179             /->{a}> ls -al
180             .. HASH (5)
181             . ARRAY (3)
182             0 scalar 10
183             1 scalar 20
184             2 scalar 'thirty'
185             /->{a}> cd ../b
186             /->{b}> ls -al
187             .. HASH (5)
188             . HASH (4)
189             w scalar 'forty'
190             x scalar 'fifty'
191             y scalar 60
192             z SCALAR 70
193             /->{b}> cd ..
194             /> dump b
195             dump--> 'b'
196             $b = {
197             'x' => 'fifty',
198             'y' => 60,
199             'z' => \70,
200             'w' => 'forty'
201             };
202             /> ls -al
203             .. HASH (5)
204             . HASH (5)
205             a ARRAY (3)
206             b HASH (4)
207             c CODE
208             d scalar 80
209             e SCALAR 80
210             /> ! $cur->{d} += 3
211             eval--> $cur->{d} += 3
212             retv--> 83
213             /> ls -al
214             .. HASH (5)
215             . HASH (5)
216             a ARRAY (3)
217             b HASH (4)
218             c CODE
219             d scalar 83
220             e SCALAR 83
221             />
222            
223              
224             Below is a sample debugger session examining this structure.
225              
226             Note that the walk() function returns a reference to the "cursor",
227             which is itself a reference to whatever is the "current directory,"
228             so to speak. The actual Data::Walker object iself is managed
229             implicitly, and is available as $Data::Walker::WALKER.
230             When you are finished, you can undef this object directly,
231             or use the unwalk() function, which does this for you.
232             But if you saved a copy of the cursor, then you will need to
233             undef this on your own.
234              
235              
236             (violet) ~/perl/walker/Data-Walker-0.18 > perl -d sample_db
237              
238             Loading DB routines from perl5db.pl version 1.0401
239             Emacs support available.
240              
241             Enter h or `h h' for help.
242              
243             main::(sample:19): d => 80,
244             DB<1> n
245             main::(sample:22): $s->{e} = \$s->{d};
246             DB<1> n
247             main::(sample:30): 1;
248             DB<1> use Data::Walker qw(:direct)
249              
250             DB<2> $cur = walk $s
251              
252             DB<3> pwd
253             /
254             DB<4> ls
255             a b c d e
256             DB<5> lal
257             .. HASH (5)
258             . HASH (5)
259             a ARRAY (3)
260             b HASH (4)
261             c CODE
262             d scalar 80
263             e SCALAR 80
264             DB<6> cd a
265             /->{a}
266             DB<7> ll
267             0 scalar 10
268             1 scalar 20
269             2 scalar 'thirty'
270             DB<8> cd '../b'
271             /->{b}
272             DB<9> lal
273             .. HASH (5)
274             . HASH (4)
275             w scalar 'forty'
276             x scalar 'fifty'
277             y scalar 60
278             z SCALAR 70
279             DB<10> cd '..'
280             /
281             DB<11> dump b
282             dump--> 'b'
283             $b = {
284             'x' => 'fifty',
285             'y' => 60,
286             'z' => \70,
287             'w' => 'forty'
288             };
289             DB<12> ll
290             a ARRAY (3)
291             b HASH (4)
292             c CODE
293             d scalar 80
294             e SCALAR 80
295             DB<13> $$cur->{d} += 3
296              
297             DB<14> ll
298             a ARRAY (3)
299             b HASH (4)
300             c CODE
301             d scalar 83
302             e SCALAR 83
303             DB<15>
304             DB<16> pwd
305             /
306             DB<17> cli
307             /> cd b
308             /->{b}> ls -l
309             w scalar 'forty'
310             x scalar 'fifty'
311             y scalar 60
312             z SCALAR 70
313             /->{b}> print y
314             60
315             /->{b}> print x
316             fifty
317             /->{b}> exit
318              
319             DB<18> pwd
320             /->{b}
321             DB<19> ll
322             w scalar 'forty'
323             x scalar 'fifty'
324             y scalar 60
325             z SCALAR 70
326             DB<20> unwalk
327              
328             DB<21> undef $cur
329              
330             DB<22>
331              
332              
333             The following commands are available from within the CLI.
334             With these commands, you can navigate around the data
335             structure as if it were a directory tree.
336              
337             cd like UNIX cd
338             ls like UNIX ls (also respects options -a, -l)
339             print prints the item as a scalar
340             dump invokes Data::Dumper
341             set set configuration variables
342             show show configuration variables
343             ! or eval eval arbitrary perl (careful!)
344             help this help message
345             help set lists the available config variables
346              
347              
348             For each session (or object) the following items can be configured:
349              
350             rootname (default: '/' ) displays the root node
351             refname (default: 'ref' ) displays embedded refs
352             scalarname (default: 'scalar') displays simple scalars
353             undefname (default: 'undef' ) displays undefined scalars
354              
355             maxdepth (default: 1 ) maximum dump-depth (Data::Dumper)
356             indent (default: 1 ) amount of indent (Data::Dumper)
357             lscol1width (default: 15 ) column widths for 'ls' displays
358             lscol2width (default: 25 ) column widths for 'ls' displays
359              
360             showrecursion (default: 1 ) note recursion in the prompt
361             showids (default: 0 ) show ref id numbers in ls lists
362             skipdoublerefs (default: 1 ) hop over ref-to-refs during walks
363             skipwarning (default: 1 ) warn when hopping over ref-to-refs
364             truncatescalars (default: 37 ) truncate scalars in 'ls' displays
365             autoprint (default: 1 ) print directory after chdir when not in CLI
366              
367             promptchar (default: '>') customize the session prompt
368             arrowshaft (default: '-') ('-' in '->')
369             arrowhead (default: '>') ('>' in '->')
370              
371             curname (default: 'cur' ) how to refer to the cursor for evals
372             parname (default: 'par' ) how to refer to the parent ref for evals
373              
374              
375             =head1 CHANGES
376              
377             =over 4
378              
379             =item * Version 1.05
380              
381             Patch to the test scripts for compatibility with
382             perl 5.8.0, which stringifies references-to-references
383             differently. In previous versions of perl,
384             references-to-references were stringified
385             as 'SCALAR(0x???)', but perl 5.8.0 stringifies
386             them as 'REF(0x???)'.
387              
388             All versions of perl's 'ref' function return
389             'REF' for references-to-references.
390              
391             =item * Version 1.02-1.04
392              
393             Minor changes to installer tests.
394              
395             =item * Version 1.01
396              
397             Minor changes to the documentation.
398             Added walker_http.pl, which is a library for using
399             Data::Walker together with HTTP::Daemon to view objects
400             with a Web browser. Two example scripts are also included.
401              
402             =item * Version 0.21
403              
404             Minor changes to the documentation
405              
406             =item * Version 0.19-0.20
407              
408             Added new tests and updated the documentation.
409              
410             =item * Version 0.18
411              
412             Completely separated the CLI loop, command-parsing regexes,
413             and the functions which implement the commands. AUTOLOAD is now
414             set up to handle any commands that the CLI can parse (except
415             for eval() ).
416              
417             By using the :direct pragma, you can now import AUTOLOADed functions
418             into the current package, so that you can easily invoke them
419             from the perl debugger.
420              
421             =item * Version 0.16-0.17
422              
423             The Data::Walker objects are now fully encapsulated.
424              
425             NOTE: The walk() function has been separated into two functions,
426             namely walk() and cli(). The usage instructions have changed.
427             Please have a look.
428              
429             =item * Version 0.15
430              
431             Reorganized the installation tests.
432             A few minor changes to the module itself.
433              
434             =item * Version 0.13-0.14
435              
436             Moved some functionality from the CLI-loop
437             into distinct functions.
438              
439             =item * Version 0.12
440              
441             Blessed references to non-hashes are now handled correctly.
442             Modified the output of "ls" commands (looks different).
443             Added new options:
444             showids, lscol2width, scalarname, undefname,
445             skipwarning
446             Numerous internal changes.
447              
448             =item * Version 0.11
449              
450             Fixed some misspellings in the help information.
451             Modified the pretty-print format of scalars.
452             Added some new comments to the source code.
453             Various other small updates.
454              
455             =back
456              
457             =head1 THANKS
458              
459             Thanks to Gurusamy Sarathy for writing Data::Dumper,
460             and to Dominique Dumont for writing Tk::ObjScanner.
461              
462             Thanks to Matthew Persico for sending some ideas on
463             how this module might be useful in the debugger.
464              
465             Thanks to Scott Lindsey for pointing out that this module
466             is useful for reading files created with the Storable module,
467             and for sending a sample script to do this.
468              
469             =head1 AUTHOR
470              
471             John Nolan jpnolan@sonic.net 1999,2000.
472             A copyright statment is contained within the source code itself.
473              
474             =cut
475              
476              
477             #---------------------------------------------------------------------------
478             # Default values - these can be overridden, either when an object
479             # is instantiated or during an interactive session.
480             #
481             %Config = (
482              
483             rootname => '/' , # Any string
484             refname => 'ref', # Any string
485             curname => 'cur', # Any string
486             parname => 'par', # Any string
487             scalarname => 'scalar', # Any string
488             undefname => 'undef', # Any string
489              
490             maxdepth => 1 , # Any integer, gets passed right to Data::Dumper
491             indent => 1 , # 1,2 or 3, gets passed right to Data::Dumper
492             lscol1width => 13 , # Any integer
493             lscol2width => 25 , # Any integer
494              
495             showrecursion => 1 , # Boolean
496             showids => 0 , # Boolean
497             skipdoublerefs => 1 , # Boolean
498             skipwarning => 1 , # Boolean
499             warning => 1 , # Boolean
500             autoprint => '' , # Boolean
501              
502             truncatescalars => 35 , # Truncate to how many chars; use 0 for no truncation
503              
504             promptchar => '>' , # Any string
505             arrowshaft => '-' , # Any string
506             arrowhead => '>' , # Any string
507             );
508              
509             $Config{arrow} = $Config{arrowshaft} . $Config{arrowhead};
510              
511             # Make a list of all UNIX-like commands that we are going to export
512             #
513             #@Commands = qw( ls ll la all lla lal cd chdir pwd print type cat dump show set );
514             @Commands{@Commands} = @Commands;
515              
516             #---------------------------------------------------------------------------
517             # Set up a new Data::Walker object
518             #
519             sub new {
520              
521 2     2 0 423 my $class = shift;
522 2         8 my %ARGS = @_;
523              
524 2         52 my $self = { (%Config) };
525              
526 2         10 bless $self,$class;
527              
528 2         11 foreach (keys %ARGS) {
529              
530 0 0       0 if (exists $Config{$_}) {
531              
532 0         0 $self->{$_} = $ARGS{$_};
533              
534             } else {
535              
536 0         0 print "$_ is not a configuration variable for $class.";
537             }
538             }
539 2         12 return $self;
540              
541             } #End sub new
542              
543              
544             #---------------------------------------------------------------------------
545             # Undef the implicit Data::Walker object
546             #
547             sub unwalk {
548              
549 0     0 0 0 undef($WALKER);
550             }
551              
552              
553             #---------------------------------------------------------------------------
554             # Point a Data::Walker object at a given reference
555             #
556             sub walk {
557              
558             # This code handles both OO invocation as a method and
559             # non-OO invocation
560             #
561 1     1 0 47 my $class = __PACKAGE__;
562 1         2 my ($self,$ref);
563              
564 1 50 33     11 if (defined $_[0] and ref($_[0]) eq $class) {
565              
566 1         3 $self = shift;
567              
568             } else {
569              
570 0         0 $self = $WALKER = new Data::Walker;
571             }
572              
573 1         2 $ref = shift;
574              
575 1 50 33     22 unless (defined $ref and ref $ref) {
576              
577 0         0 print "Parameter to walk is missing, undefined, or is not a reference";
578 0         0 return 0;
579             }
580              
581 1         66 $self->{namepath} = [$self->{rootname}];
582 1         4 $self->{refpath} = [$ref];
583 1         2 $self->{cursor} = $ref;
584              
585 1         2 $self->{prev_namepath} = [];
586 1         3 $self->{prev_refpath} = [];
587 1         2 $self->{tmp_namepath} = [];
588 1         3 $self->{tmp_refpath} = [];
589              
590 1         4 return \$self->{cursor}; # Return a ref to the cursor
591             }
592              
593             #---------------------------------------------------------------------------
594             # Find out what a reference actually points to
595             #
596             sub reftype {
597              
598 267     267 0 322 my ($ref) = @_;
599              
600 267 50       587 return unless ref($ref);
601              
602 267         632 my($realpack, $realtype, $id) =
603             (overload::StrVal($ref) =~ /^(?:(.*)\=)?([^=]*)\(([^\(]*)\)$/);
604              
605             # For some reason, stringified version of a ref-to-ref gives a
606             # type of "SCALAR" rather than "REF". Go figure.
607             #
608 267 50 66     2620 $realtype = 'REF' if $realtype eq 'SCALAR' and ref($$ref);
609              
610 267 100       967 wantarray ? return ($realtype,$realpack,$id) : return $realtype;
611              
612             } #End sub reftype
613              
614              
615              
616             #---------------------------------------------------------------------------
617             # Print out a short string describing the type of thing
618             # this reference is pointing to. Follow ref-to-refs if necessary.
619             #
620             sub printref {
621              
622 154     154 0 3420 my ($self,$ref,$recurse) = @_;
623              
624 154 100       370 $recurse = {} unless defined $recurse;
625              
626 154         237 my ($type, $value) = ("error: type is empty","error: value is empty");
627              
628 154 50       361 if (not defined $ref) {
    100          
629              
630 0         0 $type = $self->{scalarname};
631 0         0 $value = $self->{undefname};
632              
633             } elsif (ref $ref) {
634              
635 101         160 my ($reftype,$refpackage,$id) = reftype($ref);
636              
637 101         165 $type = $reftype;
638 101 100 66     277 $type = $refpackage . "=" . $type if defined($refpackage) and $refpackage ne "";
639 101 100       238 $type .= "($id)" if $self->{showids};
640              
641 101 100       161 if ($reftype eq "REF") {
642              
643             # If this is a ref-to-ref, then recurse until we find
644             # what it ultimately points to.
645             # But stop if we detect a reference loop.
646             #
647 15 50       35 if (exists $recurse->{$ref}) {
648              
649 0         0 my $hops = (scalar keys %$recurse) - $recurse->{$ref};
650 0 0       0 $value = "(recurses in $hops " . ($hops > 1 ? "hops" : "hop") . ")";
651              
652             } else {
653              
654 15         62 $recurse->{$ref} = scalar keys(%$recurse);
655 15         44 my ($nexttype, $nextvalue, $nextid) = $self->printref($$ref,$recurse);
656 15         31 $type .= $self->{arrow} . $nexttype;
657 15         30 $value = $nextvalue;
658             }
659              
660             } else {
661              
662 86         122 $recurse = {};
663              
664 86 100 66     318 if ($reftype eq "HASH") {
    100 66        
    50          
    100          
665              
666 30         86 $value = "(" . scalar keys(%$ref) . ")";
667              
668             } elsif ($reftype eq "ARRAY") {
669              
670 31         69 $value = "(" . scalar @$ref . ")";
671              
672             } elsif ($reftype eq "SCALAR" and not defined($$ref) ) {
673              
674 0         0 $value = $self->{undefname};
675              
676             } elsif ($reftype eq "SCALAR" and defined $$ref ) {
677              
678 12         23 $value = $$ref;
679              
680             } else {
681              
682 13         25 $value = ""; # We decline to displey other data types. :)
683              
684             } #End if ($reftype eq ...)
685              
686             } #End if ($reftype eq "REF")
687              
688              
689             } else {
690              
691             # It's not a reference, so it must actually be a scalar.
692             #
693 53         80 $type = $self->{scalarname};
694 53         56 $value = $ref;
695              
696 53 50 33     272 if ($self->{truncatescalars} > 0 and length($ref) > $self->{truncatescalars} - 2) {
697              
698 0         0 $value = substr($ref,0,$self->{truncatescalars} - 5) . "..." ;
699             }
700              
701             # Quote anything that's not a decimal value.
702             #
703 53 100       208 unless ($value =~ /^(?:0|-?[1-9]\d{0,8})$/) {
704              
705 14         25 $value = '\'' . $value . '\'';
706             }
707              
708             } #End if (not defined $ref) -- elsif (ref $ref)
709              
710              
711 154 100       615 wantarray ? return ($type,$value) : return $type;
712              
713             } #End sub printref
714              
715              
716              
717             #---------------------------------------------------------------------------
718             # This function is used for "chdir'ing" down a reference.
719             #
720             sub down {
721              
722 33     33 0 972 my ($self,$name,$ref,$recurse) = @_;
723              
724             # The hash $recurse contains elements only when
725             # this function is called recursively. This typically
726             # happens when we are chdir'ing down ref-to-refs.
727             #
728 33 100       92 $recurse = {} unless defined $recurse;
729              
730 33 100       94 my $what_is_it = ref($ref) ? reftype($ref) . " reference" : "scalar";
731              
732 33 100       170 unless ($what_is_it =~ /(ARRAY|HASH|REF)/) {
733              
734 5 50       16 print "'$name' is a $what_is_it, can't cd into it.\n"
735             if $self->{warning};
736 5         9 $self->{cursor} = undef; # The caller must handle this
737 5         23 return;
738             }
739              
740 28 100       71 $name = "{$name}" if reftype($self->{refpath}[-1]) eq "HASH";
741 28 50       74 $name = "[$name]" if reftype($self->{refpath}[-1]) eq "ARRAY";
742              
743 28         36 push @{$self->{namepath}}, $name;
  28         72  
744 28         44 push @{$self->{refpath}}, $ref;
  28         53  
745              
746 28         45 $self->{cursor} = $ref;
747              
748             #------------------------------
749             # If the 'skipdoublerefs' config value is set,
750             # and if the reference itself refers to a reference,
751             # then skip it and go down further. This is recursive,
752             # so we will keep skipping until we come to
753             # something which is not a ref-to-ref.
754             #
755             # We need to watch out for reference loops.
756             # Keep track of already-seen references in %$recurse.
757             # Pass $recurse to this function, recursively.
758             #
759 28 100 100     144 if ($self->{skipdoublerefs} and ref($self->{cursor}) eq "REF") {
760              
761             # Remember that we have seen the current reference.
762 4         18 $recurse->{$self->{cursor}} = scalar keys(%$recurse);
763              
764 4 50 33     27 print "Skipping down ref-to-ref.\n" if $self->{skipwarning} and $self->{warning};
765              
766 4 100       7 if (exists $recurse->{ ${$self->{cursor}} }) {
  4         15  
767              
768             #------------------------------
769             # If $recurse->{ ${$self->{cursor}} } exists, then we must have seen it before.
770             # This means we have detected a reference loop.
771             #
772             # The value of $recurse->{$self->{cursor}} is the number of reference-hops
773             # to the current reference, and the value of $recurse->{ ${$self->{cursor}} }
774             # the number of hops to ${$self->{cursor}}, which is a smaller number,
775             # because we saw it before, on a previous hop.
776             #
777             # To get the size of the reference loop, get the number of hops between them,
778             # and add one hop (to count the final hop back to the beginning of the loop).
779             #
780 2         6 my $hops = 1 + $recurse->{$self->{cursor}} - $recurse->{ ${$self->{cursor}} };
  2         6  
781 2 0       8 print
    50          
782             "Reference loop detected: $hops ". ($hops > 1 ? "hops" : "hop") . ".\n"
783             if $self->{warning}
784             ;
785              
786             } else {
787              
788 2         5 $self->down( $self->{refname}, ${$self->{cursor}}, $recurse );
  2         15  
789              
790             #------------------------------
791             # The call to the down() method in the previous line will fail
792             # if the target happens to be a SCALAR or some other item which
793             # we can't cd into. In this case, we need to cd back up,
794             # until the current ref is no longer a ref-to-ref.
795             #
796             # The following lines of code will be executed one time
797             # for each *successful* previous call to the down() method,
798             # which is what we want. We back out just like we came in.
799             #
800 2 50 33     22 if (ref($self->{cursor}) eq 'REF' and scalar @{$self->{refpath}} > 1) {
  0         0  
801              
802 0 0 0     0 print "Skipping up ref-to-ref.\n"
803             if $self->{skipwarning} and $self->{warning};
804 0         0 $self->up();
805             }
806              
807             } #End if (exists $recurse->{ ${$self->{cursor}} })
808              
809             } else {
810              
811             # Intentionally empty
812             #
813             # If 'skipdoublerefs' is not set, then we will be able to cd into
814             # ref-to-refs and run ls from within them.
815              
816             } #End if ($self->{skipdoublerefs} and ref($self->{cursor}) eq "REF")
817              
818 28         101 return $self->{cursor};
819              
820             } #End sub down
821              
822              
823              
824             #---------------------------------------------------------------------------
825             # This function is used for "chdir'ing" up a reference.
826             #
827             sub up {
828              
829 13     13 0 1213 my ($self) = @_;
830              
831 13 100       16 return $self->{refpath}[0] if scalar @{$self->{refpath}} == 1;
  13         51  
832              
833 12         18 my $name = pop @{$self->{namepath}};
  12         26  
834 12         18 pop @{$self->{refpath}};
  12         21  
835              
836             # We don't need to watch out for recursion here,
837             # because we can only go back up the way we came down.
838             #
839 12 100 100     72 if ($self->{skipdoublerefs} and $name eq $self->{refname} and $#{$self->{refpath}} > 0) {
  3   66     15  
840              
841 3 50 33     566 print "Skipping up ref-to-ref.\n" if $self->{skipwarning} and $self->{warning};
842 3         13 $self->up();
843             }
844 12         28 $self->{cursor} = $self->{refpath}[-1];
845              
846 12         45 return $self->{cursor};
847              
848             } #End sub up
849              
850              
851              
852             #---------------------------------------------------------------------------
853 0     0   0 sub DESTROY {
854              
855             # Intentionally empty
856             }
857              
858              
859              
860             #---------------------------------------------------------------------------
861             # This is used for setting configuration variables OR
862             # for invoking UNIX-like shell functions
863             #
864             sub AUTOLOAD {
865              
866             # Grab the name of the function we attempted to invoke
867             #
868 89     89   22866 (my $func = $AUTOLOAD) =~ s/^.*:://;
869              
870 89         122 my $self;
871              
872 89 50 33     483 if (defined $_[0] and ref($_[0]) eq __PACKAGE__) {
    0 0        
873              
874 89         127 $self = shift;
875            
876             } elsif (defined $WALKER and ref($WALKER) eq __PACKAGE__) {
877              
878 0         0 $self = $WALKER;
879 0 0       0 $self->{autoprint} = 1 if $self->{autoprint} eq "";
880              
881             } else {
882              
883 0         0 print "$func (AUTOLOAD): Use the walk() function to assign a target reference.\n";
884 0         0 return "";
885             }
886              
887              
888             # This might be an invocation of a walker function...
889             #
890 89 100       226 if (exists $Commands{$func}) {
891              
892 80 50 33     403 unless (exists $self->{cursor} and ref $self->{cursor}) {
893              
894 0         0 print "$func (AUTOLOAD): No reference! Please use walk() to initialize a reference to a data structure.\n";
895 0         0 return "";
896             }
897            
898 80         191 my $retval = $self->parse_command($func,@_);
899 80         117 chomp $retval;
900              
901 80 50       185 if ($self->{autoprint}) {
902              
903 0         0 print $retval;
904 0 0       0 print $self->walker_pwd if $func =~ /(cd|chdir)/;
905             }
906              
907 80         779 return $retval;
908              
909              
910             # ...or it might be an attempt to set a configuration variable.
911             #
912             } else {
913              
914 9         26 my $msg = $self->walker_set($func,$_[0]);
915 9 50       24 print $msg if ($msg);
916 9         39 return $self->{$func};
917             }
918              
919             }
920              
921             #---------------------------------------------------------------------------
922             # Check the values assigned to configuration variables,
923             # and accept them if they are OK.
924             #
925             sub walker_set {
926              
927 15     15 0 564 my ($self,$key,$value) = @_;
928              
929 15 50       35 return "Attempt to assign to undefined key"
930             unless defined $key;
931 15 50       28 return "Attempt to assign undefined value to key '" . lc($key) . "'"
932             unless defined $value;
933              
934             # Handle empty strings
935 15 50 33     102 $value = '' if $value eq qq/''/ or $value eq q/""/;
936              
937 15 50       65 if ($value =~ /^".*"$/) {
    50          
938 0         0 $value =~ s/^"(.*)"$/$1/;
939              
940             } elsif ($value =~ /^'.*'$/) {
941 0         0 $value =~ s/^'(.*)'$/$1/;
942             }
943              
944 15         21 my $msg = "";
945              
946 15         29 for ($key) {
947              
948             /(truncatescalars|lscol?width|maxdepth)/i
949 15 50       152 and do {
950 0         0 my $key = $1;
951 0 0 0     0 unless ($value =~ /\d+/ and $value >= 0) {
952 0         0 $msg = lc($key) . " must be a positive integer"; last;
  0         0  
953             }
954 0         0 $self->{lc $key} = $value;
955 0         0 last;
956             };
957             /indent/i
958 15 50       40 and do {
959 0 0       0 unless ($value =~ /(1|2|3)/) {
960 0         0 $msg = "indent must be a either 1, 2 or 3"; last;
  0         0  
961             }
962 0         0 $self->{indent} = $value;
963 0         0 last;
964             };
965             /rootname/i
966 15 50       38 and do {
967 0         0 $self->{rootname} = $value;
968 0 0       0 $self->{namepath}[0] = $value if defined $self->{namepath};
969 0 0       0 $self->{prev_namepath}[0] = $value if defined $self->{prev_namepath};
970 0         0 last;
971             };
972             /^arrow$/i
973 15 50       43 and do {
974 0         0 $msg = "Can't modify arrow directly. Instead, modify arrowshaft and arrowhead";
975 0         0 last;
976             };
977              
978             # We check this here, so that we can handle exceptional strings beforehand
979             #
980 15 50       39 unless (exists $Config{$key}) {
981              
982 0         0 $msg = "No such config variable as '" . lc($key) . "'";
983 0         0 return $msg;
984             }
985              
986             # Otherwise, just accept whatever value.
987             #
988 15 50       81 $self->{$key} = $value if exists $self->{$key};
989              
990             } #End for ($key)
991              
992 15         44 $self->{arrow} = $self->{arrowshaft} . $self->{arrowhead};
993              
994 15         34 return $msg;
995              
996             } #End sub walker_set
997              
998              
999             #---------------------------------------------------------------------------
1000             # Implement chdir logic
1001             #
1002             sub walker_chdir {
1003              
1004 26     26 0 60 my ($self,$dirspec) = @_;
1005 26         36 my @temp_refpath = ();
1006 26         35 my @temp_namepath = ();
1007              
1008 26         53 $dirspec =~ s/^\s+//; # Strip leading whitespace
1009 26         40 $dirspec =~ s/\s+$//; # Strip trailing whitespace
1010              
1011             #------------------------------
1012             # Handle cd -
1013             #
1014 26 50       51 if ($dirspec =~ m#^\s*-\s*$#) {
1015              
1016             # Swap swap, fizz fizz.....
1017             #
1018 0         0 @temp_namepath = @{$self->{namepath}};
  0         0  
1019 0         0 @{$self->{namepath}} = @{$self->{prev_namepath}};
  0         0  
  0         0  
1020 0         0 @{$self->{prev_namepath}} = @temp_namepath ;
  0         0  
1021              
1022 0         0 @temp_refpath = @{$self->{refpath}};
  0         0  
1023 0         0 @{$self->{refpath}} = @{$self->{prev_refpath}};
  0         0  
  0         0  
1024 0         0 @{$self->{prev_refpath}} = @temp_refpath ;
  0         0  
1025              
1026             # Use the last ref in the (now) current refpath
1027             #
1028 0         0 $self->{cursor} = $self->{refpath}[-1];
1029              
1030 0         0 return $self->{cursor};
1031              
1032             } else {
1033              
1034             # Remember our current paths into the structure,
1035             # in case we have to abort for some reason.
1036             #
1037 26         34 @temp_refpath = @{$self->{refpath}};
  26         72  
1038 26         51 @temp_namepath = @{$self->{namepath}};
  26         618  
1039              
1040             } #End if ($dirspec =~ m#^\s*-\s*$#) {
1041              
1042             #------------------------------
1043             # Handle dirspec's relative to the root
1044             #
1045 26         63 my $leading_slash = "";
1046              
1047 26 100       87 if ($dirspec =~ m#^/#) {
1048              
1049             # Set the paths back to the beginning
1050 14         15 $#{$self->{namepath}} = 0;
  14         70  
1051 14         15 $#{$self->{refpath}} = 0;
  14         28  
1052              
1053             # Set cursor to the first item in the refpath
1054 14         32 $self->{cursor} = $self->{refpath}[0];
1055              
1056             # Strip any leading '/' chars from $dirspec
1057             #
1058 14         44 $dirspec =~ s#^/+##g;
1059              
1060 14         28 $leading_slash = '/';
1061             }
1062              
1063             #------------------------------
1064             # Handle all other dirspec's
1065             #
1066 26         72 my @dirs = split /\//, $dirspec;
1067              
1068 26         52 foreach (@dirs) {
1069              
1070             # The actual value of $self->{cursor} may be modified within this loop,
1071             # so we have to re-check it each time through
1072             #
1073 31         68 my ($reftype,$refpackage) = reftype($self->{cursor});
1074              
1075 31         50 my $dir = $_;
1076              
1077 31 50       106 if ($dir eq '.') {
    100          
    100          
    50          
    0          
1078              
1079             # Do nothing
1080              
1081             } elsif ($_ eq '..') {
1082              
1083 5         13 $self->up();
1084              
1085             } elsif ($reftype eq "REF") {
1086              
1087 4 100       18 unless ($_ eq $self->{refname}) {
1088              
1089 1 50       4 print "'$leading_slash$dirspec' does not exist. " .
1090             "Type 'cd $self->{refname}' to descend into reference.\n"
1091             if $self->{warning};
1092              
1093 1         4 @{$self->{refpath}} = @temp_refpath;
  1         3  
1094 1         1 @{$self->{namepath}} = @temp_namepath;
  1         4  
1095 1         3 $self->{cursor} = $self->{refpath}[-1];
1096              
1097 1         4 return $self->{cursor};
1098             }
1099              
1100 3         3 $self->down($dir, ${ $self->{cursor} });
  3         9  
1101              
1102             } elsif ($reftype eq "HASH") {
1103              
1104 22 50       63 unless (exists $self->{cursor}->{$dir}) {
1105              
1106 0 0       0 print "No such element as '$leading_slash$dirspec'.\n" if $self->{warning};
1107              
1108 0         0 @{$self->{refpath}} = @temp_refpath;
  0         0  
1109 0         0 @{$self->{namepath}} = @temp_namepath;
  0         0  
1110 0         0 $self->{cursor} = $self->{refpath}[-1];
1111              
1112 0         0 return $self->{cursor};
1113              
1114             } else {
1115              
1116 22         65 $self->down($dir,$self->{cursor}->{$dir});
1117             }
1118              
1119             } elsif ($reftype eq "ARRAY") {
1120              
1121 0 0 0     0 unless ($dir =~ /^\d+$/ and scalar(@{ $self->{cursor} }) > $dir) {
1122              
1123 0 0       0 print "No such element as '$leading_slash$dirspec'.\n" if $self->{warning};
1124 0         0 @{$self->{refpath}} = @temp_refpath;
  0         0  
1125 0         0 @{$self->{namepath}} = @temp_namepath;
  0         0  
1126 0         0 $self->{cursor} = $self->{refpath}[-1];
1127              
1128 0         0 return $self->{cursor};
1129              
1130             } else {
1131              
1132 0         0 $self->down($dir,$self->{cursor}->[$dir]);
1133             }
1134              
1135             } else {
1136              
1137             #------------------------------
1138             # If $self->{cursor} points to a SCALAR, CODE or something else, then the
1139             # 'cd' command is ignored within it. We should never have chdir'ed
1140             # there in the first place, so this message will only be printed
1141             # if the author of this module has made an error. ;)
1142             #
1143 0 0       0 print "Don't know how to chdir from current directory ($reftype) into '$dirspec'.\n"
1144             if $self->{warning};
1145 0         0 @{$self->{refpath}} = @temp_refpath;
  0         0  
1146 0         0 @{$self->{namepath}} = @temp_namepath;
  0         0  
1147 0         0 $self->{cursor} = $self->{refpath}[-1];
1148              
1149             } #End if ($dir eq ...
1150              
1151             #------------------------------
1152             # If the calls to down() or up() have failed for some reason,
1153             # then return to wherever were to begin with.
1154             # Don't even bother to parse the rest of the path,
1155             # just return immediately.
1156             #
1157 30 100       114 if (not defined $self->{cursor}) {
1158              
1159 3         5 @{$self->{refpath}} = @temp_refpath;
  3         7  
1160 3         5 @{$self->{namepath}} = @temp_namepath;
  3         9  
1161 3         1585 $self->{cursor} = $self->{refpath}[-1];
1162              
1163 3         12 return $self->{cursor};
1164             }
1165              
1166             } #End foreach (@dirs)
1167              
1168              
1169             # Looks like we successfully chdir'd from one place into another.
1170             # Save our previous location in the structure into the "prev_" variables.
1171             # The previous previous variables (meta-previous?) are now forgotton.
1172             #
1173 22         31 @{$self->{prev_refpath}} = @temp_refpath;
  22         63  
1174 22         31 @{$self->{prev_namepath}} = @temp_namepath;
  22         102  
1175              
1176             } #End sub walker_chdir
1177              
1178              
1179             #---------------------------------------------------------------------------
1180             # Implement "ls" formatting logic
1181             #
1182             sub walker_ls {
1183              
1184 27     27 0 41 my ($self,$option) = @_;
1185 27         59 my ($reftype,$refpackage) = reftype($self->{cursor});
1186              
1187 27         49 my $retval = "";
1188              
1189 27 100       61 if ($option =~ /l/) {
1190              
1191 20         27 my $dots = "";
1192 20         54 my $format = "%-$self->{lscol1width}s %-$self->{lscol2width}s %s\n";
1193              
1194 20 100       51 if ($option =~ /a/) {
1195              
1196 14         14 my ($type,$value);
1197            
1198 14 100       16 if (scalar @{$self->{namepath}} > 1) {
  14         41  
1199            
1200 7         20 ($type,$value) = $self->printref($self->{refpath}[-2]);
1201 7         26 $dots .= sprintf( $format, '..', $type, $value );
1202 7         24 ($type,$value) = $self->printref($self->{refpath}[-1]);
1203              
1204             } else {
1205              
1206 7         22 ($type,$value) = $self->printref($self->{refpath}[-1]);
1207 7         31 $dots .= sprintf( $format, '..', $type, $value );
1208             }
1209              
1210 14         40 $dots .= sprintf( $format , '.', $type, $value );
1211             }
1212              
1213 20 50       56 if ($reftype eq "REF") {
    100          
    50          
1214              
1215 0         0 $retval .= $dots;
1216 0         0 my ($type,$value) = $self->printref(${ $self->{cursor} });
  0         0  
1217 0         0 $retval .= sprintf( $format, $self->{refname}, $type, $value );
1218              
1219             } elsif ($reftype eq "HASH") {
1220              
1221 10         20 $retval .= $dots;
1222 10         10 foreach (sort keys %{$self->{cursor}}) {
  10         62  
1223              
1224 60         157 my ($type,$value) = $self->printref($self->{cursor}->{$_});
1225 60         251 $retval .= sprintf( $format, $_, $type, $value );
1226             }
1227              
1228             } elsif ($reftype eq "ARRAY") {
1229              
1230 10         18 $retval .= $dots;
1231 10         12 my $i = 0;
1232 10         1049 foreach (@{ $self->{cursor} }) {
  10         28  
1233              
1234 30         63 my ($type,$value) = $self->printref($_);
1235 30         137 $retval .= sprintf( $format, $i++, $type, $value );
1236             }
1237              
1238             } else {
1239              
1240 0         0 $retval .= "Current ref is a ref to " . $reftype .
1241             ", don't know how to emulate ls -l in it.\n";
1242             }
1243              
1244             } else {
1245              
1246 7 100       60 my $dots = ($option =~ /a/) ? "..\t.\t" : "";
1247              
1248 7 50       21 if ($reftype eq "REF") {
    100          
    50          
1249              
1250 0         0 $retval .= $dots . $self->{refname} . "\n";
1251              
1252             } elsif ($reftype eq "HASH") {
1253              
1254 3         6 $retval .= $dots;
1255 3         4 foreach (sort keys %{ $self->{cursor} }) {
  3         36  
1256              
1257 18         27 $retval .= $_. "\t";
1258             }
1259 3         7 $retval .= "\n";
1260              
1261             } elsif ($reftype eq "ARRAY") {
1262              
1263 4         6 $retval .= $dots;
1264 4         4 my $i = 0;
1265 4         5 foreach (@{ $self->{cursor} }) {
  4         8  
1266              
1267 12         23 $retval .= $self->printref($_) . "\t";
1268             }
1269              
1270             } else {
1271              
1272 0         0 $retval .= "Current ref is a $reftype, don't know how to emulate ls in it.\n";
1273             }
1274              
1275             }
1276              
1277 27         94 return $retval;
1278              
1279             } #End sub walker_ls
1280              
1281              
1282             #---------------------------------------------------------------------------
1283             # Implement "cat" formatting logic
1284             #
1285             sub walker_cat {
1286              
1287 21     21 0 39 my ($self,$target) = @_;
1288 21         45 my ($reftype,$refpackage) = reftype($self->{cursor});
1289              
1290 21         35 my $retval = "";
1291              
1292             # Prints "print--> "...
1293 21         60 $retval = "print$self->{arrowshaft}$self->{arrow} '" . $target . "'\n";
1294            
1295 21 50       70 if ($target eq ".") {
    50          
    100          
    50          
1296              
1297 0         0 $retval = $self->{cursor};
1298              
1299             } elsif ($target eq '..') {
1300              
1301 0 0       0 $retval = ${$self->{refpath}[-2]} if (scalar @{$self->{namepath}} > 1);
  0         0  
  0         0  
1302 0 0       0 $retval = ${$self->{refpath}[-1]} if (scalar @{$self->{namepath}} <= 1);
  0         0  
  0         0  
1303              
1304             } elsif ($reftype eq "HASH") {
1305              
1306 15         31 $retval = $self->{cursor}->{$target};
1307              
1308             } elsif ($reftype eq "ARRAY") {
1309              
1310 6         13 $retval = $self->{cursor}->[$target];
1311              
1312             } else {
1313              
1314 0 0       0 print "Current ref is a $reftype, don't know how to print from it."
1315             if $self->{warning};
1316             }
1317              
1318 21         60 return $retval;
1319              
1320             } #End sub walker_cat
1321              
1322              
1323             #---------------------------------------------------------------------------
1324             # Print the current path
1325             #
1326             sub walker_pwd {
1327              
1328 0     0 0 0 my $self = shift;
1329 0         0 return join $self->{arrow},@{$self->{namepath}};
  0         0  
1330             }
1331              
1332              
1333             #---------------------------------------------------------------------------
1334             # Invoke Data::Dumper::dump
1335             #
1336             sub walker_dump {
1337              
1338 0     0 0 0 my ($self,$target) = @_;
1339 0         0 my ($reftype,$refpackage) = reftype($self->{cursor});
1340              
1341 0         0 my $retval = "";
1342              
1343             # Pass config values directly to Data::Dumper
1344             #
1345 0         0 local $Data::Dumper::Indent = $self->{indent};
1346 0         0 local $Data::Dumper::Maxdepth = $self->{maxdepth};
1347              
1348             # Prints "dump--> "...
1349 0         0 $retval .= "dump$self->{arrowshaft}$self->{arrow} '$target'\n";
1350            
1351 0 0       0 if ($target eq ".") {
    0          
    0          
    0          
    0          
1352              
1353 0         0 $retval .= Data::Dumper->Dump( [ $self->{cursor} ] );
1354              
1355             } elsif ($target eq '..') {
1356              
1357 0         0 $retval .= Data::Dumper->Dump([ $self->{refpath}[-2] ],[ $self->{namepath}[-2] ])
1358 0 0       0 if (scalar @{$self->{namepath}} > 1);
1359 0         0 $retval .= Data::Dumper->Dump([ $self->{refpath}[-1] ],[ $self->{namepath}[-1] ])
1360 0 0       0 if (scalar @{$self->{namepath}} <= 1);
1361              
1362             } elsif ($reftype eq "REF") {
1363              
1364 0         0 $retval .= Data::Dumper->Dump( [ ${$self->{cursor}} ], [ $target ] );
  0         0  
1365              
1366             } elsif ($reftype eq "HASH") {
1367              
1368 0         0 $retval .= Data::Dumper->Dump( [ $self->{cursor}->{$target} ], [ $target ] );
1369              
1370             } elsif ($reftype eq "ARRAY") {
1371              
1372 0         0 $retval .= Data::Dumper->Dump( [ $self->{cursor}->[$target] ], [ $target ] );
1373              
1374             } else {
1375              
1376 0 0       0 $retval .= "Current ref is a $reftype, don't know how to dump things from it."
1377             if $self->{warning};
1378             }
1379              
1380 0         0 return $retval;
1381              
1382             } #End sub walker_dump
1383              
1384              
1385              
1386             #---------------------------------------------------------------------------
1387             # Format the CLI prompt (this is called after each command)
1388             #
1389             sub walker_getprompt {
1390              
1391 21     21 0 86 my $self = shift;
1392              
1393             #------------------------------
1394             # Take a copy of the namepath, because we are going to munge it
1395             #
1396 21         22 my @temp_namepath = @{ $self->{namepath} };
  21         79  
1397              
1398 21         27 my (%seen,%seen_twice);
1399 21         22 my $count = 1;
1400              
1401 21         28 for (my $i = 0; $i < scalar @{$self->{refpath}}; $i++) {
  81         271  
1402              
1403             # Check to see if we are seeing this ref for the *second* time.
1404             # If so, define it in the %seen_twice hash.
1405             #
1406 60 100 66     233 if (
1407             exists $seen{ $self->{refpath}[$i] }
1408             and
1409             not exists $seen_twice{ $self->{refpath}[$i] }
1410             ) {
1411              
1412 7         21 $seen_twice{ $self->{refpath}[$i] } = $count++;
1413             }
1414              
1415 60         184 $seen{ $self->{refpath}[$i] } = 1;
1416             }
1417              
1418 21         31 for (my $i = 0; $i < scalar @{$self->{refpath}}; $i++) {
  81         376  
1419              
1420 60 100       202 $temp_namepath[$i] .= "-" . $seen_twice{ $self->{refpath}[$i] } . "-"
1421             if exists $seen_twice{ $self->{refpath}[$i] };
1422             }
1423              
1424 21         338 return sprintf "%s$self->{promptchar} ", join $self->{arrow},@temp_namepath;
1425              
1426             } #End sub walker_getprompt
1427              
1428              
1429             #---------------------------------------------------------------------------
1430             # Format help messages
1431             #
1432             sub walker_help {
1433              
1434 0     0 0 0 my ($self,$arg) = @_;
1435 0         0 my $retval = "";
1436              
1437 0 0 0     0 if (defined $arg and $arg =~ /show/) {
1438              
1439 0         0 ($retval =<<" EOM") =~ s/^\s+//gm;
1440             The following items can be configured
1441             (current value is in parenthesis):
1442            
1443             rootname how the root node is displayed ("$$self{rootname}")
1444             refname how embedded refs are listed ("$$self{refname}")
1445             scalarname how simple scalars are listed ("$$self{scalarname}")
1446             undefname how unefined scalars are listed ("$$self{undefname}")
1447             promptchar customize the session prompt ("$$self{promptchar}")
1448             arrowshaft first part of ref arrow ("$$self{arrowshaft}")
1449             arrowhead last part of ref arrow ("$$self{arrowhead}")
1450            
1451             maxdepth maximum dump-depth (Data::Dumper) ($$self{maxdepth})
1452             indent amount of indent (Data::Dumper) ($$self{indent})
1453             lscol1width column widths for 'ls' displays ($$self{lscol1width})
1454             lscol2width column widths for 'ls' displays ($$self{lscol2width})
1455            
1456             showrecursion note recursion in the prompt ($$self{showrecursion})
1457             showids show ref id numbers in ls lists ($$self{showids})
1458             skipdoublerefs hop over ref-to-refs during walks ($$self{skipdoublerefs})
1459             skipwarning warn when hopping over ref-to-refs ($$self{skipwarning})
1460             truncatescalars truncate scalars in 'ls' displays ($$self{truncatescalars})
1461             (use 0 for no truncation)
1462              
1463             type "show " to display a value
1464             type "set " to assign a new value
1465             EOM
1466              
1467             } else {
1468              
1469 0         0 ($retval =<<" EOM") =~ s/^\s+//gsm;
1470             The following commands are supported:
1471              
1472             cd like UNIX cd
1473             ls like UNIX ls (also respects options -a, -l)
1474             print prints the item as a scalar
1475             dump invokes Data::Dumper
1476             set set configuration variables
1477             show show configuration variables
1478             ! or eval eval arbitrary perl (careful!)
1479             help this help message
1480             help set lists the availabe config variables
1481             EOM
1482             }
1483              
1484 0         0 return $retval;
1485              
1486             } #End sub walker_help
1487              
1488              
1489             #---------------------------------------------------------------------------
1490             # Show the walker's config variables
1491             #
1492             sub walker_show {
1493              
1494 0     0 0 0 my ($self,$arg) = @_;
1495 0         0 my $retval = "";
1496              
1497 0 0 0     0 if (defined $arg and $arg ne "") {
1498              
1499 0         0 my $key = lc $arg;
1500 0         0 $key =~ s/^\s+//;
1501 0         0 $key =~ s/\s+$//;
1502              
1503 0 0       0 unless (exists $self->{$key}) {
1504              
1505 0         0 return "No such config variable as '$key'\n";
1506             }
1507              
1508             # Print out the variable key and value.
1509             # Quote anything that's not a decimal value.
1510             #
1511 0 0       0 if ($self->{$key} =~ /^(?:0|-?[1-9]\d{0,8})$/) {
1512 0         0 $retval = "$key = $self->{$key}\n";
1513             } else {
1514 0         0 $retval = "$key = '$self->{$key}'\n";
1515             }
1516              
1517             } else {
1518              
1519 0         0 foreach (sort { $a cmp $b } keys %Config) {
  0         0  
1520              
1521             # Print out the variable key and value.
1522             # Quote anything that's not a decimal value.
1523             #
1524 0 0       0 if ($self->{$_} =~ /^(?:0|-?[1-9]\d{0,8})$/) {
1525 0         0 $retval .= sprintf "%-15s = %s\n", lc($_), $self->{$_};
1526             } else {
1527 0         0 $retval .= sprintf "%-15s = '%s'\n", lc($_), $self->{$_};
1528             }
1529             }
1530             }
1531              
1532 0         0 return $retval;
1533             }
1534              
1535             #---------------------------------------------------------------------------
1536             # Parse commands, either from the CLI or from an AUTOLOADed function.
1537             # Dispatch to the proper internal methods.
1538             #
1539             sub parse_command {
1540              
1541 80     80 0 95 my $self = shift;
1542 80         166 my $cmd = join ' ',@_;
1543              
1544 80 50       174 $cmd = '' unless defined $cmd;
1545 80         92 my $retval = "";
1546              
1547             #------------------------------------------------------------
1548             # Emulate the pwd command
1549             #
1550 80 50       910 if ($cmd =~ /^(pwd)$/) {
    50          
    50          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    50          
    50          
    0          
1551              
1552 0         0 $retval .= $self->walker_pwd . "\n";
1553              
1554             #------------------------------------------------------------
1555             # Print the help blurb
1556             #
1557             } elsif ($cmd =~ /^\s*(help|h)\s*$/) {
1558              
1559 0         0 $retval .= $self->walker_help;
1560              
1561             } elsif ($cmd =~ /^\s*help\s+(set|show)\s*$/) {
1562              
1563 0         0 $retval .= $self->walker_help("show");
1564              
1565             #------------------------------------------------------------
1566             # Emulate cd
1567             #
1568             } elsif ($cmd =~ /^\s*(cd|chdir)\s+(.+)$/) {
1569              
1570             # Change directories, but don't print anything.
1571             # (walker_chdir returns a reference)
1572             #
1573 26         61 $self->walker_chdir($2);
1574              
1575             #------------------------------------------------------------
1576             # Emulate ls -l
1577             #
1578             } elsif ($cmd =~ /^\s*(lal|lla|all|ll\s+-?a|ls\s+-?al|ls\s+-?la|dir|ls\s+-?a\s+-?l|ls\s+-?l\s+-?a|la\s+-?l)\s*$/) {
1579              
1580 14         37 $retval .= $self->walker_ls("la");
1581            
1582             } elsif ($cmd =~ /^\s*(ll|ls\s+-?l|ls\s+-?l)\s*$/) {
1583              
1584 6         17 $retval .= $self->walker_ls("l");
1585            
1586             } elsif ($cmd =~ /^\s*(ls\s+-?a|la)\s*$/) {
1587              
1588 5         14 $retval .= $self->walker_ls("a");
1589              
1590             } elsif ($cmd =~ /^\s*(l|ls)\s*$/) {
1591              
1592 2         7 $retval .= $self->walker_ls("");
1593              
1594             #------------------------------------------------------------
1595             # Emulate cat
1596             #
1597             } elsif ($cmd =~ /^\s*(cat|type|print|p)\s+(.+?)\s*$/) {
1598              
1599 21         49 $retval .= $self->walker_cat($2) . "\n";
1600              
1601             #------------------------------------------------------------
1602             # Invoke dump
1603             #
1604             } elsif ($cmd =~ /^\s*(dump|d)\s+(.+?)\s*(\d*)$/) {
1605              
1606 0         0 $retval .= $self->walker_dump($2);
1607              
1608             } elsif ($cmd =~ /^\s*(dump|d)\s*$/) {
1609              
1610 0         0 $retval .= $self->walker_dump('.');
1611              
1612             #------------------------------------------------------------
1613             # Adjust config settings ("set indent 2")
1614             #
1615             } elsif ($cmd =~ /^\s*set\s+(\S+?)\s+(.+)$/i) {
1616              
1617 6         21 my ($key,$value) = (lc($1),$2);
1618 6         20 $value =~ s/^[=\s]*//;
1619 6         22 $value =~ s/[\s]*$//;
1620              
1621 6         16 my $msg = $self->walker_set($key,$value);
1622 6 50       15 $retval .= "$msg.\n" if $msg;
1623              
1624              
1625             #------------------------------------------------------------
1626             # Show config settings ("show indent" etc.)
1627             #
1628             } elsif ($cmd =~ /^\s*show(.*)$/i) {
1629              
1630 0 0       0 my $arg = defined($1) ? $1 : "";
1631              
1632 0         0 $retval .= $self->walker_show($arg);
1633              
1634             } else {
1635              
1636 0         0 $retval .= "Ignoring command '$cmd', could not parse. (Type 'help' for help.)\n";
1637             }
1638              
1639 80         167 return $retval;
1640              
1641             } #End sub parse_command
1642              
1643              
1644             #---------------------------------------------------------------------------
1645             # "Walk" a data structure. This function implements the CLI.
1646             #
1647             sub cli {
1648              
1649             # This code handles both OO-invocation as a method and
1650             # non-OO invocation
1651             #
1652 0     0 0   my $class = __PACKAGE__;
1653 0           my ($self,$ref);
1654              
1655 0 0 0       if (defined $_[0] and $_[0] eq $class and defined $_[1] and ref $_[1]) {
    0 0        
    0 0        
    0 0        
      0        
      0        
      0        
      0        
1656              
1657             # cli() was invoked as a class method,
1658             # so we create an object on the fly. This object will
1659             # be destroyed as soon as $self goes out of scope,
1660             # which is at the end of this function.
1661             #
1662 0           $self = new($class);
1663 0           $ref = $_[1];
1664 0 0         $self->walk($ref) or return;
1665              
1666             } elsif (defined $_[0] and ref $_[0] eq $class and defined $_[1] and ref $_[1]) {
1667              
1668             # cli() was invoked as a method on an object,
1669             # so we use this object.
1670             #
1671 0           ($self,$ref) = @_;
1672 0 0         $self->walk($ref) or return;
1673              
1674             } elsif (ref $_[0] eq $class and defined $_[0]->{cursor}) {
1675              
1676             # cli() was entered on a Data::Walker object which already exists,
1677             # so we use that.
1678             #
1679 0           ($self,$ref) = @_;
1680              
1681             } elsif (defined($WALKER) and ref($WALKER) eq __PACKAGE__) {
1682              
1683             # cli() was not invoked with any parameters, but there is an
1684             # implicit Data::Walker object, so we use that.
1685             #
1686 0           $self = $WALKER;
1687              
1688             } else {
1689              
1690 0           print "cli: No reference!";
1691 0           return;
1692             }
1693              
1694 0           printf "%s$self->{promptchar} ",join $self->{arrow},@{$self->{namepath}};
  0            
1695              
1696             #------------------------------------------------------------
1697             # Command loop. We loop through here once for each command
1698             # that the user enters at the prompt.
1699             #
1700 0           COMMAND: while(<>) {
1701              
1702 0           chomp;
1703 0 0         next COMMAND unless /\S/; # Ignore empty commands
1704              
1705 0 0         return if m/^\s*(q|qu|quit|ex|exi|exti|exit)\s*$/i; # 50 ways to leave your CLI
1706              
1707              
1708             #------------------------------------------------------------
1709             # eval: Take whatever the user typed in and eval it
1710             #
1711 0 0         if (s/^\s*(\!|eval)\s+//) {
1712              
1713             # prints "eval--> "...
1714             #
1715 0           print "eval$self->{arrowshaft}$self->{arrow} $_\n";
1716              
1717             # Let the user refer
1718 0           my ($par,$cur);
1719 0 0         $par = $self->{refpath}->[-2] if scalar @{$self->{refpath}} > 1;
  0            
1720 0 0         $par = $self->{refpath}->[-1] if scalar @{$self->{refpath}} == 1;
  0            
1721 0           $cur = $self->{cursor};
1722              
1723 0           s/\$$self->{curname}\b/\$cur/g;
1724 0           s/\$$self->{parname}\b/\$par/g;
1725              
1726 0           my $res = eval;
1727 0 0         $res = "undef" unless defined $res;
1728              
1729             # prints "retv--> "...
1730             #
1731 0           print "retv$self->{arrowshaft}$self->{arrow} $res\n";
1732              
1733             } else {
1734              
1735 0           print $self->parse_command($_);
1736             }
1737              
1738             } continue { #continuing COMMAND: while(<>) {
1739              
1740 0           print $self->walker_getprompt;
1741              
1742             } #End COMMAND: while(<>) {
1743              
1744             } #End sub cli
1745              
1746              
1747             1;
1748              
1749