ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/staticperl/perl/lib/Dumpvalue.pm
Revision: 1.1
Committed: Thu Jun 30 14:27:08 2005 UTC (21 years, 4 months ago) by root
Branch: MAIN
CVS Tags: PERL-5-8-7, HEAD
Branch point for: PERL
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 use 5.006_001; # for (defined ref) and $#$v and our
2     package Dumpvalue;
3     use strict;
4     our $VERSION = '1.11';
5     our(%address, $stab, @stab, %stab, %subs);
6    
7     # documentation nits, handle complex data structures better by chromatic
8     # translate control chars to ^X - Randal Schwartz
9     # Modifications to print types by Peter Gordon v1.0
10    
11     # Ilya Zakharevich -- patches after 5.001 (and some before ;-)
12    
13     # Won't dump symbol tables and contents of debugged files by default
14    
15     # (IZ) changes for objectification:
16     # c) quote() renamed to method set_quote();
17     # d) unctrlSet() renamed to method set_unctrl();
18     # f) Compiles with `use strict', but in two places no strict refs is needed:
19     # maybe more problems are waiting...
20    
21     my %defaults = (
22     globPrint => 0,
23     printUndef => 1,
24     tick => "auto",
25     unctrl => 'quote',
26     subdump => 1,
27     dumpReused => 0,
28     bareStringify => 1,
29     hashDepth => '',
30     arrayDepth => '',
31     dumpDBFiles => '',
32     dumpPackages => '',
33     quoteHighBit => '',
34     usageOnly => '',
35     compactDump => '',
36     veryCompact => '',
37     stopDbSignal => '',
38     );
39    
40     sub new {
41     my $class = shift;
42     my %opt = (%defaults, @_);
43     bless \%opt, $class;
44     }
45    
46     sub set {
47     my $self = shift;
48     my %opt = @_;
49     @$self{keys %opt} = values %opt;
50     }
51    
52     sub get {
53     my $self = shift;
54     wantarray ? @$self{@_} : $$self{pop @_};
55     }
56    
57     sub dumpValue {
58     my $self = shift;
59     die "usage: \$dumper->dumpValue(value)" unless @_ == 1;
60     local %address;
61     local $^W=0;
62     (print "undef\n"), return unless defined $_[0];
63     (print $self->stringify($_[0]), "\n"), return unless ref $_[0];
64     $self->unwrap($_[0],0);
65     }
66    
67     sub dumpValues {
68     my $self = shift;
69     local %address;
70     local $^W=0;
71     (print "undef\n"), return unless defined $_[0];
72     $self->unwrap(\@_,0);
73     }
74    
75     # This one is good for variable names:
76    
77     sub unctrl {
78     local($_) = @_;
79    
80     return \$_ if ref \$_ eq "GLOB";
81     s/([\001-\037\177])/'^'.pack('c',ord($1)^64)/eg;
82     $_;
83     }
84    
85     sub stringify {
86     my $self = shift;
87     local $_ = shift;
88     my $noticks = shift;
89     my $tick = $self->{tick};
90    
91     return 'undef' unless defined $_ or not $self->{printUndef};
92     return $_ . "" if ref \$_ eq 'GLOB';
93     { no strict 'refs';
94     $_ = &{'overload::StrVal'}($_)
95     if $self->{bareStringify} and ref $_
96     and %overload:: and defined &{'overload::StrVal'};
97     }
98    
99     if ($tick eq 'auto') {
100     if (/[\000-\011\013-\037\177]/) {
101     $tick = '"';
102     } else {
103     $tick = "'";
104     }
105     }
106     if ($tick eq "'") {
107     s/([\'\\])/\\$1/g;
108     } elsif ($self->{unctrl} eq 'unctrl') {
109     s/([\"\\])/\\$1/g ;
110     s/([\000-\037\177])/'^'.pack('c',ord($1)^64)/eg;
111     s/([\200-\377])/'\\0x'.sprintf('%2X',ord($1))/eg
112     if $self->{quoteHighBit};
113     } elsif ($self->{unctrl} eq 'quote') {
114     s/([\"\\\$\@])/\\$1/g if $tick eq '"';
115     s/\033/\\e/g;
116     s/([\000-\037\177])/'\\c'.chr(ord($1)^64)/eg;
117     }
118     s/([\200-\377])/'\\'.sprintf('%3o',ord($1))/eg if $self->{quoteHighBit};
119     ($noticks || /^\d+(\.\d*)?\Z/)
120     ? $_
121     : $tick . $_ . $tick;
122     }
123    
124     sub DumpElem {
125     my ($self, $v) = (shift, shift);
126     my $short = $self->stringify($v, ref $v);
127     my $shortmore = '';
128     if ($self->{veryCompact} && ref $v
129     && (ref $v eq 'ARRAY' and !grep(ref $_, @$v) )) {
130     my $depth = $#$v;
131     ($shortmore, $depth) = (' ...', $self->{arrayDepth} - 1)
132     if $self->{arrayDepth} and $depth >= $self->{arrayDepth};
133     my @a = map $self->stringify($_), @$v[0..$depth];
134     print "0..$#{$v} @a$shortmore\n";
135     } elsif ($self->{veryCompact} && ref $v
136     && (ref $v eq 'HASH') and !grep(ref $_, values %$v)) {
137     my @a = sort keys %$v;
138     my $depth = $#a;
139     ($shortmore, $depth) = (' ...', $self->{hashDepth} - 1)
140     if $self->{hashDepth} and $depth >= $self->{hashDepth};
141     my @b = map {$self->stringify($_) . " => " . $self->stringify($$v{$_})}
142     @a[0..$depth];
143     local $" = ', ';
144     print "@b$shortmore\n";
145     } else {
146     print "$short\n";
147     $self->unwrap($v,shift);
148     }
149     }
150    
151     sub unwrap {
152     my $self = shift;
153     return if $DB::signal and $self->{stopDbSignal};
154     my ($v) = shift ;
155     my ($s) = shift ; # extra no of spaces
156     my $sp;
157     my (%v,@v,$address,$short,$fileno);
158    
159     $sp = " " x $s ;
160     $s += 3 ;
161    
162     # Check for reused addresses
163     if (ref $v) {
164     my $val = $v;
165     { no strict 'refs';
166     $val = &{'overload::StrVal'}($v)
167     if %overload:: and defined &{'overload::StrVal'};
168     }
169     ($address) = $val =~ /(0x[0-9a-f]+)\)$/ ;
170     if (!$self->{dumpReused} && defined $address) {
171     $address{$address}++ ;
172     if ( $address{$address} > 1 ) {
173     print "${sp}-> REUSED_ADDRESS\n" ;
174     return ;
175     }
176     }
177     } elsif (ref \$v eq 'GLOB') {
178     $address = "$v" . ""; # To avoid a bug with globs
179     $address{$address}++ ;
180     if ( $address{$address} > 1 ) {
181     print "${sp}*DUMPED_GLOB*\n" ;
182     return ;
183     }
184     }
185    
186     if (ref $v eq 'Regexp') {
187     my $re = "$v";
188     $re =~ s,/,\\/,g;
189     print "$sp-> qr/$re/\n";
190     return;
191     }
192    
193     if ( UNIVERSAL::isa($v, 'HASH') ) {
194     my @sortKeys = sort keys(%$v) ;
195     my $more;
196     my $tHashDepth = $#sortKeys ;
197     $tHashDepth = $#sortKeys < $self->{hashDepth}-1 ? $#sortKeys : $self->{hashDepth}-1
198     unless $self->{hashDepth} eq '' ;
199     $more = "....\n" if $tHashDepth < $#sortKeys ;
200     my $shortmore = "";
201     $shortmore = ", ..." if $tHashDepth < $#sortKeys ;
202     $#sortKeys = $tHashDepth ;
203     if ($self->{compactDump} && !grep(ref $_, values %{$v})) {
204     $short = $sp;
205     my @keys;
206     for (@sortKeys) {
207     push @keys, $self->stringify($_) . " => " . $self->stringify($v->{$_});
208     }
209     $short .= join ', ', @keys;
210     $short .= $shortmore;
211     (print "$short\n"), return if length $short <= $self->{compactDump};
212     }
213     for my $key (@sortKeys) {
214     return if $DB::signal and $self->{stopDbSignal};
215     my $value = $ {$v}{$key} ;
216     print $sp, $self->stringify($key), " => ";
217     $self->DumpElem($value, $s);
218     }
219     print "$sp empty hash\n" unless @sortKeys;
220     print "$sp$more" if defined $more ;
221     } elsif ( UNIVERSAL::isa($v, 'ARRAY') ) {
222     my $tArrayDepth = $#{$v} ;
223     my $more ;
224     $tArrayDepth = $#$v < $self->{arrayDepth}-1 ? $#$v : $self->{arrayDepth}-1
225     unless $self->{arrayDepth} eq '' ;
226     $more = "....\n" if $tArrayDepth < $#{$v} ;
227     my $shortmore = "";
228     $shortmore = " ..." if $tArrayDepth < $#{$v} ;
229     if ($self->{compactDump} && !grep(ref $_, @{$v})) {
230     if ($#$v >= 0) {
231     $short = $sp . "0..$#{$v} " .
232     join(" ",
233     map {exists $v->[$_] ? $self->stringify($v->[$_]) : "empty"} ($[..$tArrayDepth)
234     ) . "$shortmore";
235     } else {
236     $short = $sp . "empty array";
237     }
238     (print "$short\n"), return if length $short <= $self->{compactDump};
239     }
240     for my $num ($[ .. $tArrayDepth) {
241     return if $DB::signal and $self->{stopDbSignal};
242     print "$sp$num ";
243     if (exists $v->[$num]) {
244     $self->DumpElem($v->[$num], $s);
245     } else {
246     print "empty slot\n";
247     }
248     }
249     print "$sp empty array\n" unless @$v;
250     print "$sp$more" if defined $more ;
251     } elsif ( UNIVERSAL::isa($v, 'SCALAR') or ref $v eq 'REF' ) {
252     print "$sp-> ";
253     $self->DumpElem($$v, $s);
254     } elsif ( UNIVERSAL::isa($v, 'CODE') ) {
255     print "$sp-> ";
256     $self->dumpsub(0, $v);
257     } elsif ( UNIVERSAL::isa($v, 'GLOB') ) {
258     print "$sp-> ",$self->stringify($$v,1),"\n";
259     if ($self->{globPrint}) {
260     $s += 3;
261     $self->dumpglob('', $s, "{$$v}", $$v, 1);
262     } elsif (defined ($fileno = fileno($v))) {
263     print( (' ' x ($s+3)) . "FileHandle({$$v}) => fileno($fileno)\n" );
264     }
265     } elsif (ref \$v eq 'GLOB') {
266     if ($self->{globPrint}) {
267     $self->dumpglob('', $s, "{$v}", $v, 1);
268     } elsif (defined ($fileno = fileno(\$v))) {
269     print( (' ' x $s) . "FileHandle({$v}) => fileno($fileno)\n" );
270     }
271     }
272     }
273    
274     sub matchvar {
275     $_[0] eq $_[1] or
276     ($_[1] =~ /^([!~])(.)([\x00-\xff]*)/) and
277     ($1 eq '!') ^ (eval {($_[2] . "::" . $_[0]) =~ /$2$3/});
278     }
279    
280     sub compactDump {
281     my $self = shift;
282     $self->{compactDump} = shift if @_;
283     $self->{compactDump} = 6*80-1
284     if $self->{compactDump} and $self->{compactDump} < 2;
285     $self->{compactDump};
286     }
287    
288     sub veryCompact {
289     my $self = shift;
290     $self->{veryCompact} = shift if @_;
291     $self->compactDump(1) if !$self->{compactDump} and $self->{veryCompact};
292     $self->{veryCompact};
293     }
294    
295     sub set_unctrl {
296     my $self = shift;
297     if (@_) {
298     my $in = shift;
299     if ($in eq 'unctrl' or $in eq 'quote') {
300     $self->{unctrl} = $in;
301     } else {
302     print "Unknown value for `unctrl'.\n";
303     }
304     }
305     $self->{unctrl};
306     }
307    
308     sub set_quote {
309     my $self = shift;
310     if (@_ and $_[0] eq '"') {
311     $self->{tick} = '"';
312     $self->{unctrl} = 'quote';
313     } elsif (@_ and $_[0] eq 'auto') {
314     $self->{tick} = 'auto';
315     $self->{unctrl} = 'quote';
316     } elsif (@_) { # Need to set
317     $self->{tick} = "'";
318     $self->{unctrl} = 'unctrl';
319     }
320     $self->{tick};
321     }
322    
323     sub dumpglob {
324     my $self = shift;
325     return if $DB::signal and $self->{stopDbSignal};
326     my ($package, $off, $key, $val, $all) = @_;
327     local(*stab) = $val;
328     my $fileno;
329     if (($key !~ /^_</ or $self->{dumpDBFiles}) and defined $stab) {
330     print( (' ' x $off) . "\$", &unctrl($key), " = " );
331     $self->DumpElem($stab, 3+$off);
332     }
333     if (($key !~ /^_</ or $self->{dumpDBFiles}) and @stab) {
334     print( (' ' x $off) . "\@$key = (\n" );
335     $self->unwrap(\@stab,3+$off) ;
336     print( (' ' x $off) . ")\n" );
337     }
338     if ($key ne "main::" && $key ne "DB::" && %stab
339     && ($self->{dumpPackages} or $key !~ /::$/)
340     && ($key !~ /^_</ or $self->{dumpDBFiles})
341     && !($package eq "Dumpvalue" and $key eq "stab")) {
342     print( (' ' x $off) . "\%$key = (\n" );
343     $self->unwrap(\%stab,3+$off) ;
344     print( (' ' x $off) . ")\n" );
345     }
346     if (defined ($fileno = fileno(*stab))) {
347     print( (' ' x $off) . "FileHandle($key) => fileno($fileno)\n" );
348     }
349     if ($all) {
350     if (defined &stab) {
351     $self->dumpsub($off, $key);
352     }
353     }
354     }
355    
356     sub CvGV_name {
357     my $self = shift;
358     my $in = shift;
359     return if $self->{skipCvGV}; # Backdoor to avoid problems if XS broken...
360     $in = \&$in; # Hard reference...
361     eval {require Devel::Peek; 1} or return;
362     my $gv = Devel::Peek::CvGV($in) or return;
363     *$gv{PACKAGE} . '::' . *$gv{NAME};
364     }
365    
366     sub dumpsub {
367     my $self = shift;
368     my ($off,$sub) = @_;
369     my $ini = $sub;
370     my $s;
371     $sub = $1 if $sub =~ /^\{\*(.*)\}$/;
372     my $subref = defined $1 ? \&$sub : \&$ini;
373     my $place = $DB::sub{$sub} || (($s = $subs{"$subref"}) && $DB::sub{$s})
374     || (($s = $self->CvGV_name($subref)) && $DB::sub{$s})
375     || ($self->{subdump} && ($s = $self->findsubs("$subref"))
376     && $DB::sub{$s});
377     $s = $sub unless defined $s;
378     $place = '???' unless defined $place;
379     print( (' ' x $off) . "&$s in $place\n" );
380     }
381    
382     sub findsubs {
383     my $self = shift;
384     return undef unless %DB::sub;
385     my ($addr, $name, $loc);
386     while (($name, $loc) = each %DB::sub) {
387     $addr = \&$name;
388     $subs{"$addr"} = $name;
389     }
390     $self->{subdump} = 0;
391     $subs{ shift() };
392     }
393    
394     sub dumpvars {
395     my $self = shift;
396     my ($package,@vars) = @_;
397     local(%address,$^W);
398     my ($key,$val);
399     $package .= "::" unless $package =~ /::$/;
400     *stab = *main::;
401    
402     while ($package =~ /(\w+?::)/g) {
403     *stab = $ {stab}{$1};
404     }
405     $self->{TotalStrings} = 0;
406     $self->{Strings} = 0;
407     $self->{CompleteTotal} = 0;
408     while (($key,$val) = each(%stab)) {
409     return if $DB::signal and $self->{stopDbSignal};
410     next if @vars && !grep( matchvar($key, $_), @vars );
411     if ($self->{usageOnly}) {
412     $self->globUsage(\$val, $key)
413     if ($package ne 'Dumpvalue' or $key ne 'stab')
414     and ref(\$val) eq 'GLOB';
415     } else {
416     $self->dumpglob($package, 0,$key, $val);
417     }
418     }
419     if ($self->{usageOnly}) {
420     print <<EOP;
421     String space: $self->{TotalStrings} bytes in $self->{Strings} strings.
422     EOP
423     $self->{CompleteTotal} += $self->{TotalStrings};
424     print <<EOP;
425     Grand total = $self->{CompleteTotal} bytes (1 level deep) + overhead.
426     EOP
427     }
428     }
429    
430     sub scalarUsage {
431     my $self = shift;
432     my $size;
433     if (UNIVERSAL::isa($_[0], 'ARRAY')) {
434     $size = $self->arrayUsage($_[0]);
435     } elsif (UNIVERSAL::isa($_[0], 'HASH')) {
436     $size = $self->hashUsage($_[0]);
437     } elsif (!ref($_[0])) {
438     $size = length($_[0]);
439     }
440     $self->{TotalStrings} += $size;
441     $self->{Strings}++;
442     $size;
443     }
444    
445     sub arrayUsage { # array ref, name
446     my $self = shift;
447     my $size = 0;
448     map {$size += $self->scalarUsage($_)} @{$_[0]};
449     my $len = @{$_[0]};
450     print "\@$_[1] = $len item", ($len > 1 ? "s" : ""), " (data: $size bytes)\n"
451     if defined $_[1];
452     $self->{CompleteTotal} += $size;
453     $size;
454     }
455    
456     sub hashUsage { # hash ref, name
457     my $self = shift;
458     my @keys = keys %{$_[0]};
459     my @values = values %{$_[0]};
460     my $keys = $self->arrayUsage(\@keys);
461     my $values = $self->arrayUsage(\@values);
462     my $len = @keys;
463     my $total = $keys + $values;
464     print "\%$_[1] = $len item", ($len > 1 ? "s" : ""),
465     " (keys: $keys; values: $values; total: $total bytes)\n"
466     if defined $_[1];
467     $total;
468     }
469    
470     sub globUsage { # glob ref, name
471     my $self = shift;
472     local *stab = *{$_[0]};
473     my $total = 0;
474     $total += $self->scalarUsage($stab) if defined $stab;
475     $total += $self->arrayUsage(\@stab, $_[1]) if @stab;
476     $total += $self->hashUsage(\%stab, $_[1])
477     if %stab and $_[1] ne "main::" and $_[1] ne "DB::";
478     #and !($package eq "Dumpvalue" and $key eq "stab"));
479     $total;
480     }
481    
482     1;
483    
484     =head1 NAME
485    
486     Dumpvalue - provides screen dump of Perl data.
487    
488     =head1 SYNOPSIS
489    
490     use Dumpvalue;
491     my $dumper = new Dumpvalue;
492     $dumper->set(globPrint => 1);
493     $dumper->dumpValue(\*::);
494     $dumper->dumpvars('main');
495     my $dump = $dumper->stringify($some_value);
496    
497     =head1 DESCRIPTION
498    
499     =head2 Creation
500    
501     A new dumper is created by a call
502    
503     $d = new Dumpvalue(option1 => value1, option2 => value2)
504    
505     Recognized options:
506    
507     =over 4
508    
509     =item C<arrayDepth>, C<hashDepth>
510    
511     Print only first N elements of arrays and hashes. If false, prints all the
512     elements.
513    
514     =item C<compactDump>, C<veryCompact>
515    
516     Change style of array and hash dump. If true, short array
517     may be printed on one line.
518    
519     =item C<globPrint>
520    
521     Whether to print contents of globs.
522    
523     =item C<dumpDBFiles>
524    
525     Dump arrays holding contents of debugged files.
526    
527     =item C<dumpPackages>
528    
529     Dump symbol tables of packages.
530    
531     =item C<dumpReused>
532    
533     Dump contents of "reused" addresses.
534    
535     =item C<tick>, C<quoteHighBit>, C<printUndef>
536    
537     Change style of string dump. Default value of C<tick> is C<auto>, one
538     can enable either double-quotish dump, or single-quotish by setting it
539     to C<"> or C<'>. By default, characters with high bit set are printed
540     I<as is>. If C<quoteHighBit> is set, they will be quoted.
541    
542     =item C<usageOnly>
543    
544     rudimentally per-package memory usage dump. If set,
545     C<dumpvars> calculates total size of strings in variables in the package.
546    
547     =item unctrl
548    
549     Changes the style of printout of strings. Possible values are
550     C<unctrl> and C<quote>.
551    
552     =item subdump
553    
554     Whether to try to find the subroutine name given the reference.
555    
556     =item bareStringify
557    
558     Whether to write the non-overloaded form of the stringify-overloaded objects.
559    
560     =item quoteHighBit
561    
562     Whether to print chars with high bit set in binary or "as is".
563    
564     =item stopDbSignal
565    
566     Whether to abort printing if debugger signal flag is raised.
567    
568     =back
569    
570     Later in the life of the object the methods may be queries with get()
571     method and set() method (which accept multiple arguments).
572    
573     =head2 Methods
574    
575     =over 4
576    
577     =item dumpValue
578    
579     $dumper->dumpValue($value);
580     $dumper->dumpValue([$value1, $value2]);
581    
582     Prints a dump to the currently selected filehandle.
583    
584     =item dumpValues
585    
586     $dumper->dumpValues($value1, $value2);
587    
588     Same as C< $dumper->dumpValue([$value1, $value2]); >.
589    
590     =item stringify
591    
592     my $dump = $dumper->stringify($value [,$noticks] );
593    
594     Returns the dump of a single scalar without printing. If the second
595     argument is true, the return value does not contain enclosing ticks.
596     Does not handle data structures.
597    
598     =item dumpvars
599    
600     $dumper->dumpvars('my_package');
601     $dumper->dumpvars('my_package', 'foo', '~bar$', '!......');
602    
603     The optional arguments are considered as literal strings unless they
604     start with C<~> or C<!>, in which case they are interpreted as regular
605     expressions (possibly negated).
606    
607     The second example prints entries with names C<foo>, and also entries
608     with names which ends on C<bar>, or are shorter than 5 chars.
609    
610     =item set_quote
611    
612     $d->set_quote('"');
613    
614     Sets C<tick> and C<unctrl> options to suitable values for printout with the
615     given quote char. Possible values are C<auto>, C<'> and C<">.
616    
617     =item set_unctrl
618    
619     $d->set_unctrl('"');
620    
621     Sets C<unctrl> option with checking for an invalid argument.
622     Possible values are C<unctrl> and C<quote>.
623    
624     =item compactDump
625    
626     $d->compactDump(1);
627    
628     Sets C<compactDump> option. If the value is 1, sets to a reasonable
629     big number.
630    
631     =item veryCompact
632    
633     $d->veryCompact(1);
634    
635     Sets C<compactDump> and C<veryCompact> options simultaneously.
636    
637     =item set
638    
639     $d->set(option1 => value1, option2 => value2);
640    
641     =item get
642    
643     @values = $d->get('option1', 'option2');
644    
645     =back
646    
647     =cut
648