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