X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2Fdumpvar.pl;h=43e107ff5f6b55f53a2acfac2559ad032724befa;hb=9a66ea416324d5cd1459c59e545b38c9c889fdaa;hp=c78319b5396f985ae1ed6fcb3b7fe478df6fb035;hpb=d338d6fe1dfdfdbc07c2d6d7a2a4ae7db5887d93;p=p5sagit%2Fp5-mst-13.2.git diff --git a/lib/dumpvar.pl b/lib/dumpvar.pl index c78319b..43e107f 100644 --- a/lib/dumpvar.pl +++ b/lib/dumpvar.pl @@ -19,14 +19,19 @@ $winsize = 80 unless defined $winsize; # $globPrint = 1; $printUndef = 1 unless defined $printUndef; -$tick = "'" unless defined $tick; +$tick = "auto" unless defined $tick; $unctrl = 'quote' unless defined $unctrl; +$subdump = 1; +$dumpReused = 0 unless defined $dumpReused; +$bareStringify = 1 unless defined $bareStringify; sub main::dumpValue { local %address; + local $^W=0; (print "undef\n"), return unless defined $_[0]; (print &stringify($_[0]), "\n"), return unless ref $_[0]; - dumpvar::unwrap($_[0],0); + push @_, -1 if @_ == 1; + dumpvar::unwrap($_[0], 0, $_[1]); } # This one is good for variable names: @@ -40,30 +45,58 @@ sub unctrl { $_; } +sub uniescape { + join("", + map { $_ > 255 ? sprintf("\\x{%04X}", $_) : chr($_) } + unpack("U*", $_[0])); +} + sub stringify { local($_,$noticks) = @_; local($v) ; + my $tick = $tick; return 'undef' unless defined $_ or not $printUndef; return $_ . "" if ref \$_ eq 'GLOB'; + $_ = &{'overload::StrVal'}($_) + if $bareStringify and ref $_ + and %overload:: and defined &{'overload::StrVal'}; + + if ($tick eq 'auto') { + if (/[\000-\011\013-\037\177]/) { + $tick = '"'; + }else { + $tick = "'"; + } + } if ($tick eq "'") { s/([\'\\])/\\$1/g; } elsif ($unctrl eq 'unctrl') { s/([\"\\])/\\$1/g ; s/([\000-\037\177])/'^'.pack('c',ord($1)^64)/eg; + # uniescape? s/([\200-\377])/'\\0x'.sprintf('%2X',ord($1))/eg if $quoteHighBit; } elsif ($unctrl eq 'quote') { s/([\"\\\$\@])/\\$1/g if $tick eq '"'; s/\033/\\e/g; - s/([\000-\037\177])/'\\c'.chr(ord($1)^64)/eg; + s/([\000-\037\177])/'\\c'._escaped_ord($1)/eg; } + $_ = uniescape($_); s/([\200-\377])/'\\'.sprintf('%3o',ord($1))/eg if $quoteHighBit; ($noticks || /^\d+(\.\d*)?\Z/) ? $_ : $tick . $_ . $tick; } +# Ensure a resulting \ is escaped to be \\ +sub _escaped_ord { + my $chr = shift; + $chr = chr(ord($chr)^64); + $chr =~ s{\\}{\\\\}g; + return $chr; +} + sub ShortArray { my $tArrayDepth = $#{$_[0]} ; $tArrayDepth = $#{$_[0]} < $arrayDepth-1 ? $#{$_[0]} : $arrayDepth-1 @@ -91,7 +124,7 @@ sub DumpElem { join("' '", @{$v}[0..$tArrayDepth]) . "'$shortmore"; } else { print "$short\n"; - unwrap($_[0],$_[1]); + unwrap($_[0],$_[1],$_[2]) if ref $_[0]; } } @@ -99,7 +132,9 @@ sub unwrap { return if $DB::signal; local($v) = shift ; local($s) = shift ; # extra no of spaces - local(%v,@v,$sp,$value,$key,$type,@sortKeys,$more,$shortmore,$short) ; + local($m) = shift ; # maximum recursion depth + return if $m == 0; + local(%v,@v,$sp,$value,$key,@sortKeys,$more,$shortmore,$short) ; local($tHashDepth,$tArrayDepth) ; $sp = " " x $s ; @@ -107,9 +142,23 @@ sub unwrap { # Check for reused addresses if (ref $v) { - ($address) = $v =~ /(0x[0-9a-f]+)/ ; - if (defined $address) { - ($type) = $v =~ /=(.*?)\(/ ; + my $val = $v; + $val = &{'overload::StrVal'}($v) + if %overload:: and defined &{'overload::StrVal'}; + # Match type and address. + # Unblessed references will look like TYPE(0x...) + # Blessed references will look like Class=TYPE(0x...) + ($start_part, $val) = split /=/,$val; + $val = $start_part unless defined $val; + ($item_type, $address) = + $val =~ /([^\(]+) # Keep stuff that's + # not an open paren + \( # Skip open paren + (0x[0-9a-f]+) # Save the address + \) # Skip close paren + $/x; # Should be at end now + + if (!$dumpReused && defined $address) { $address{$address}++ ; if ( $address{$address} > 1 ) { print "${sp}-> REUSED_ADDRESS\n" ; @@ -117,6 +166,7 @@ sub unwrap { } } } elsif (ref \$v eq 'GLOB') { + # This is a raw glob. Special handling for that. $address = "$v" . ""; # To avoid a bug with globs $address{$address}++ ; if ( $address{$address} > 1 ) { @@ -125,8 +175,17 @@ sub unwrap { } } - if ( ref $v eq 'HASH' or $type eq 'HASH') { - @sortKeys = sort keys(%$v) ; + if (ref $v eq 'Regexp') { + # Reformat the regexp to look the standard way. + my $re = "$v"; + $re =~ s,/,\\/,g; + print "$sp-> qr/$re/\n"; + return; + } + + if ( $item_type eq 'HASH' ) { + # Hash ref or hash-based object. + my @sortKeys = sort keys(%$v) ; undef $more ; $tHashDepth = $#sortKeys ; $tHashDepth = $#sortKeys < $hashDepth-1 ? $#sortKeys : $hashDepth-1 @@ -154,22 +213,29 @@ sub unwrap { return if $DB::signal; $value = $ {$v}{$key} ; print "$sp", &stringify($key), " => "; - DumpElem $value, $s; + DumpElem $value, $s, $m-1; } print "$sp empty hash\n" unless @sortKeys; print "$sp$more" if defined $more ; - } elsif ( ref $v eq 'ARRAY' or $type eq 'ARRAY') { + } elsif ( $item_type eq 'ARRAY' ) { + # Array ref or array-based object. Also: undef. + # See how big the array is. $tArrayDepth = $#{$v} ; undef $more ; + # Bigger than the max? $tArrayDepth = $#{$v} < $arrayDepth-1 ? $#{$v} : $arrayDepth-1 - unless $arrayDepth eq '' ; + if defined $arrayDepth && $arrayDepth ne ''; + # Yep. Don't show it all. $more = "....\n" if $tArrayDepth < $#{$v} ; $shortmore = ""; $shortmore = " ..." if $tArrayDepth < $#{$v} ; + if ($compactDump && !grep(ref $_, @{$v})) { if ($#$v >= 0) { - $short = $sp . "0..$#{$v} '" . - join("' '", @{$v}[0..$tArrayDepth]) . "'$shortmore"; + $short = $sp . "0..$#{$v} " . + join(" ", + map {exists $v->[$_] ? stringify $v->[$_] : "empty"} ($[..$tArrayDepth) + ) . "$shortmore"; } else { $short = $sp . "empty array"; } @@ -182,34 +248,64 @@ sub unwrap { for $num ($[ .. $tArrayDepth) { return if $DB::signal; print "$sp$num "; - DumpElem $v->[$num], $s; + if (exists $v->[$num]) { + if (defined $v->[$num]) { + DumpElem $v->[$num], $s, $m-1; + } + else { + print "undef\n"; + } + } else { + print "empty slot\n"; + } } print "$sp empty array\n" unless @$v; print "$sp$more" if defined $more ; - } elsif ( ref $v eq 'SCALAR' or ref $v eq 'REF' or $type eq 'SCALAR' ) { + } elsif ( $item_type eq 'SCALAR' ) { + unless (defined $$v) { + print "$sp-> undef\n"; + return; + } print "$sp-> "; - DumpElem $$v, $s; - } elsif (ref $v eq 'GLOB') { + DumpElem $$v, $s, $m-1; + } elsif ( $item_type eq 'REF' ) { + print "$sp-> $$v\n"; + return unless defined $$v; + unwrap($$v, $s+3, $m-1); + } elsif ( $item_type eq 'CODE' ) { + # Code object or reference. + print "$sp-> "; + dumpsub (0, $v); + } elsif ( $item_type eq 'GLOB' ) { + # Glob object or reference. print "$sp-> ",&stringify($$v,1),"\n"; if ($globPrint) { $s += 3; - dumpglob($s, "{$$v}", $$v, 1); + dumpglob($s, "{$$v}", $$v, 1, $m-1); } elsif (defined ($fileno = fileno($v))) { print( (' ' x ($s+3)) . "FileHandle({$$v}) => fileno($fileno)\n" ); } } elsif (ref \$v eq 'GLOB') { + # Raw glob (again?) if ($globPrint) { - dumpglob($s, "{$v}", $v, 1) if $globPrint; + dumpglob($s, "{$v}", $v, 1, $m-1) if $globPrint; } elsif (defined ($fileno = fileno(\$v))) { print( (' ' x $s) . "FileHandle({$v}) => fileno($fileno)\n" ); } } } +sub matchlex { + (my $var = $_[0]) =~ s/.//; + $var eq $_[1] or + ($_[1] =~ /^([!~])(.)([\x00-\xff]*)/) and + ($1 eq '!') ^ (eval { $var =~ /$2$3/ }); +} + sub matchvar { $_[0] eq $_[1] or - ($_[1] =~ /^([!~])(.)/) and - ($1 eq '!') ^ (eval {($_[2] . "::" . $_[0]) =~ /$2$'/}); + ($_[1] =~ /^([!~])(.)([\x00-\xff]*)/) and + ($1 eq '!') ^ (eval {($_[2] . "::" . $_[0]) =~ /$2$3/}); } sub compactDump { @@ -240,6 +336,9 @@ sub quote { if (@_ and $_[0] eq '"') { $tick = '"'; $unctrl = 'quote'; + } elsif (@_ and $_[0] eq 'auto') { + $tick = 'auto'; + $unctrl = 'quote'; } elsif (@_) { # Need to set $tick = "'"; $unctrl = 'unctrl'; @@ -249,23 +348,24 @@ sub quote { sub dumpglob { return if $DB::signal; - my ($off,$key, $val, $all) = @_; + my ($off,$key, $val, $all, $m) = @_; local(*entry) = $val; my $fileno; - if (defined $entry) { + if (($key !~ /^_ fileno($fileno)\n" ); + } + # No lexical subroutines yet... + # elsif (UNIVERSAL::isa($val,'CODE')) { + # dumpsub($off, $$val); + # } + else { + print( (' ' x $off) . &unctrl($key), " = " ); + DumpElem $$val, 3+$off, $m; + } +} + +sub CvGV_name_or_bust { + my $in = shift; + return if $skipCvGV; # Backdoor to avoid problems if XS broken... + $in = \&$in; # Hard reference... + eval {require Devel::Peek; 1} or return; + my $gv = Devel::Peek::CvGV($in) or return; + *$gv{PACKAGE} . '::' . *$gv{NAME}; +} + +sub dumpsub { + my ($off,$sub) = @_; + my $ini = $sub; + my $s; + $sub = $1 if $sub =~ /^\{\*(.*)\}$/; + my $subref = defined $1 ? \&$sub : \&$ini; + my $place = $DB::sub{$sub} || (($s = $subs{"$subref"}) && $DB::sub{$s}) + || (($s = CvGV_name_or_bust($subref)) && $DB::sub{$s}) + || ($subdump && ($s = findsubs("$subref")) && $DB::sub{$s}); + $place = '???' unless defined $place; + $s = $sub unless defined $s; + print( (' ' x $off) . "&$s in $place\n" ); +} + +sub findsubs { + return undef unless %DB::sub; + my ($addr, $name, $loc); + while (($name, $loc) = each %DB::sub) { + $addr = \&$name; + $subs{"$addr"} = $name; + } + $subdump = 0; + $subs{ shift() }; +} + sub main::dumpvar { - my ($package,@vars) = @_; - local(%address,$key,$val); + my ($package,$m,@vars) = @_; + local(%address,$key,$val,$^W); $package .= "::" unless $package =~ /::$/; *stab = *{"main::"}; while ($package =~ /(\w+?::)/g){ @@ -297,9 +457,11 @@ sub main::dumpvar { return if $DB::signal; next if @vars && !grep( matchvar($key, $_), @vars ); if ($usageOnly) { - globUsage(\$val, $key) unless $package eq 'dumpvar' and $key eq 'stab'; + globUsage(\$val, $key) + if ($package ne 'dumpvar' or $key ne 'stab') + and ref(\$val) eq 'GLOB'; } else { - dumpglob(0,$key, $val); + dumpglob(0,$key, $val, 0, $m); } } if ($usageOnly) { @@ -344,8 +506,8 @@ sub globUsage { # glob ref, name local *name = *{$_[0]}; $total = 0; $total += scalarUsage $name if defined $name; - $total += arrayUsage \@name, $_[1] if defined @name; - $total += hashUsage \%name, $_[1] if defined %name and $_[1] ne "main::" + $total += arrayUsage \@name, $_[1] if @name; + $total += hashUsage \%name, $_[1] if %name and $_[1] ne "main::" and $_[1] ne "DB::"; #and !($package eq "dumpvar" and $key eq "stab")); $total; } @@ -371,46 +533,3 @@ sub packageUsage { 1; -package dumpvar; - -# translate control chars to ^X - Randal Schwartz -sub unctrl { - local($_) = @_; - return \$_ if ref \$_ eq "GLOB"; - s/([\001-\037\177])/'^'.pack('c',ord($1)^64)/eg; - $_; -} -sub main'dumpvar { - ($package,@vars) = @_; - $package .= "::" unless $package =~ /::$/; - *stab = *{"main::"}; - while ($package =~ /(\w+?::)/g){ - *stab = ${stab}{$1}; - } - while (($key,$val) = each(%stab)) { - { - next if @vars && !grep($key eq $_,@vars); - local(*entry) = $val; - if (defined $entry) { - print "\$",&unctrl($key)," = '",&unctrl($entry),"'\n"; - } - if (defined @entry) { - print "\@$key = (\n"; - foreach $num ($[ .. $#entry) { - print " $num\t'",&unctrl($entry[$num]),"'\n"; - } - print ")\n"; - } - if ($key ne "main::" && $key ne "DB::" && defined %entry - && !($package eq "dumpvar" and $key eq "stab")) { - print "\%$key = (\n"; - foreach $key (sort keys(%entry)) { - print " $key\t'",&unctrl($entry{$key}),"'\n"; - } - print ")\n"; - } - } - } -} - -1;