require
5.014;
$winsize
= 80
unless
defined
$winsize
;
sub
ASCII {
return
ord
(
'A'
) == 65; }
$printUndef
= 1
unless
defined
$printUndef
;
$tick
=
"auto"
unless
defined
$tick
;
$unctrl
=
'quote'
unless
defined
$unctrl
;
$subdump
= 1;
$dumpReused
= 0
unless
defined
$dumpReused
;
$bareStringify
= 1
unless
defined
$bareStringify
;
my
$APC
=
chr
utf8::unicode_to_native(0x9F);
my
$backslash_c_question
= (ASCII) ?
'\177'
:
$APC
;
sub
main::dumpValue {
local
%address
;
local
$^W=0;
(
print
"undef\n"
),
return
unless
defined
$_
[0];
(
print
&stringify
(
$_
[0]),
"\n"
),
return
unless
ref
$_
[0];
push
@_
, -1
if
@_
== 1;
dumpvar::unwrap(
$_
[0], 0,
$_
[1]);
}
sub
unctrl {
for
(
my
(
$dummy
) =
shift
) {
local
(
$v
) ;
return
\
$_
if
ref
\
$_
eq
"GLOB"
;
s/([\000-\037])/
'^'
.
chr
(utf8::unicode_to_native(
ord
($1)^64))/eg;
s/
$backslash_c_question
/^?/xg;
return
$_
;
}
}
sub
uniescape {
join
(
""
,
map
{
$_
> 255 ?
sprintf
(
"\\x{%04X}"
,
$_
) :
chr
(
$_
) }
unpack
(
"W*"
,
$_
[0]));
}
sub
stringify {
my
$string
;
if
(
eval
{
$string
= _stringify(
@_
); 1 }) {
return
$string
;
}
return
"<< value could not be dumped: $@ >>"
;
}
sub
_stringify {
(
my
$__
,
local
$noticks
) =
@_
;
for
(
$__
) {
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
(/[^[:^cntrl:]\n]/u) {
$tick
=
'"'
;
}
else
{
$tick
=
"'"
;
}
}
if
(
$tick
eq
"'"
) {
s/([\'\\])/\\$1/g;
}
elsif
(
$unctrl
eq
'unctrl'
) {
s/([\"\\])/\\$1/g ;
$_
=
&unctrl
(
$_
);
s/([[:^ascii:]])/
'\\0x'
.
sprintf
(
'%2X'
,
ord
($1))/eg
if
$quoteHighBit
;
}
elsif
(
$unctrl
eq
'quote'
) {
s/([\
"\\\$\@])/\\$1/g if $tick eq '"
';
s/\e/\\e/g;
s/([\000-\037
$backslash_c_question
])/
'\\c'
._escaped_ord($1)/eg;
}
$_
= uniescape(
$_
);
s/([[:^ascii:]])/
'\\'
.
sprintf
(
'%3o'
,
ord
($1))/eg
if
$quoteHighBit
;
return
(
$noticks
|| /^\d+(\.\d*)?\Z/)
?
$_
:
$tick
.
$_
.
$tick
;
}
}
sub
_escaped_ord {
my
$chr
=
shift
;
if
(
$chr
eq
$backslash_c_question
) {
$chr
=
'?'
;
}
else
{
$chr
=
chr
(utf8::unicode_to_native(
ord
(
$chr
)^64));
$chr
=~ s{\\}{\\\\}g;
}
return
$chr
;
}
sub
ShortArray {
my
$tArrayDepth
= $
$tArrayDepth
= $
unless
$arrayDepth
eq
''
;
my
$shortmore
=
""
;
$shortmore
=
" ..."
if
$tArrayDepth
< $
if
(!
grep
(
ref
$_
, @{
$_
[0]})) {
$short
=
"0..$#{$_[0]} '"
.
join
(
"' '"
, @{
$_
[0]}[0..
$tArrayDepth
]) .
"'$shortmore"
;
return
$short
if
length
$short
<=
$compactDump
;
}
undef
;
}
sub
DumpElem {
my
$short
=
&stringify
(
$_
[0],
ref
$_
[0]);
if
(
$veryCompact
&&
ref
$_
[0]
&& (
ref
$_
[0] eq
'ARRAY'
and !
grep
(
ref
$_
, @{
$_
[0]}) )) {
my
$end
=
"0..$#{$v} '"
.
join
(
"' '"
, @{
$_
[0]}[0..
$tArrayDepth
]) .
"'$shortmore"
;
}
elsif
(
$veryCompact
&&
ref
$_
[0]
&& (
ref
$_
[0] eq
'HASH'
) and !
grep
(
ref
$_
,
values
%{
$_
[0]})) {
my
$end
= 1;
$short
=
$sp
.
"0..$#{$v} '"
.
join
(
"' '"
, @{
$v
}[0..
$tArrayDepth
]) .
"'$shortmore"
;
}
else
{
print
"$short\n"
;
unwrap(
$_
[0],
$_
[1],
$_
[2])
if
ref
$_
[0];
}
}
sub
unwrap {
return
if
$DB::signal
;
local
(
$v
) =
shift
;
local
(
$s
) =
shift
;
local
(
$m
) =
shift
;
return
if
$m
== 0;
local
(
%v
,
@v
,
$sp
,
$value
,
$key
,
@sortKeys
,
$more
,
$shortmore
,
$short
) ;
local
(
$tHashDepth
,
$tArrayDepth
) ;
$sp
=
" "
x
$s
;
$s
+= 3 ;
eval
{
if
(
ref
$v
) {
my
$val
=
$v
;
$val
= &{
'overload::StrVal'
}(
$v
)
if
%overload::
and
defined
&{
'overload::StrVal'
};
$val
=~ s/^.*=//;
(
$item_type
,
$address
) =
$val
=~ /([^\(]+)
\(
(0x[0-9a-f]+)
\)
$/x;
if
(!
$dumpReused
&&
defined
$address
) {
$address
{
$address
}++ ;
if
(
$address
{
$address
} > 1 ) {
print
"${sp}-> REUSED_ADDRESS\n"
;
return
;
}
}
}
elsif
(
ref
\
$v
eq
'GLOB'
) {
$address
=
"$v"
.
""
;
$address
{
$address
}++ ;
if
(
$address
{
$address
} > 1 ) {
print
"${sp}*DUMPED_GLOB*\n"
;
return
;
}
}
if
(
ref
$v
eq
'Regexp'
) {
my
$re
=
"$v"
;
$re
=~ s,/,\\/,g;
print
"$sp-> qr/$re/\n"
;
return
;
}
if
(
$item_type
eq
'HASH'
) {
my
@sortKeys
=
sort
keys
(
%$v
) ;
undef
$more
;
$tHashDepth
=
$#sortKeys
;
$tHashDepth
=
$#sortKeys
<
$hashDepth
-1 ?
$#sortKeys
:
$hashDepth
-1
unless
$hashDepth
eq
''
;
$more
=
"....\n"
if
$tHashDepth
<
$#sortKeys
;
$shortmore
=
""
;
$shortmore
=
", ..."
if
$tHashDepth
<
$#sortKeys
;
$#sortKeys
=
$tHashDepth
;
if
(
$compactDump
&& !
grep
(
ref
$_
,
values
%{
$v
})) {
$short
=
$sp
;
my
@keys
;
for
(
@sortKeys
) {
push
@keys
,
&stringify
(
$_
) .
" => "
.
&stringify
(
$v
->{
$_
});
}
$short
.=
join
', '
,
@keys
;
$short
.=
$shortmore
;
(
print
"$short\n"
),
return
if
length
$short
<=
$compactDump
;
}
for
$key
(
@sortKeys
) {
return
if
$DB::signal
;
$value
= $ {
$v
}{
$key
} ;
print
"$sp"
,
&stringify
(
$key
),
" => "
;
DumpElem
$value
,
$s
,
$m
-1;
}
print
"$sp empty hash\n"
unless
@sortKeys
;
print
"$sp$more"
if
defined
$more
;
}
elsif
(
$item_type
eq
'ARRAY'
) {
$tArrayDepth
= $
undef
$more
;
$tArrayDepth
= $
if
defined
$arrayDepth
&&
$arrayDepth
ne
''
;
$more
=
"....\n"
if
$tArrayDepth
< $
$shortmore
=
""
;
$shortmore
=
" ..."
if
$tArrayDepth
< $
if
(
$compactDump
&& !
grep
(
ref
$_
, @{
$v
})) {
if
(
$#$v
>= 0) {
$short
=
$sp
.
"0..$#{$v} "
.
join
(
" "
,
map
{
exists
$v
->[
$_
] ? stringify
$v
->[
$_
] :
"empty"
} (0..
$tArrayDepth
)
) .
"$shortmore"
;
}
else
{
$short
=
$sp
.
"empty array"
;
}
(
print
"$short\n"
),
return
if
length
$short
<=
$compactDump
;
}
for
$num
(0 ..
$tArrayDepth
) {
return
if
$DB::signal
;
print
"$sp$num "
;
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
(
$item_type
eq
'SCALAR'
) {
unless
(
defined
$$v
) {
print
"$sp-> undef\n"
;
return
;
}
print
"$sp-> "
;
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'
) {
print
"$sp-> "
;
dumpsub (0,
$v
);
}
elsif
(
$item_type
eq
'GLOB'
) {
print
"$sp-> "
,
&stringify
(
$$v
,1),
"\n"
;
if
(
$globPrint
) {
$s
+= 3;
dumpglob(
$s
,
"{$$v}"
,
$$v
, 1,
$m
-1);
}
elsif
(
defined
(
$fileno
=
eval
{
fileno
(
$v
)})) {
print
( (
' '
x (
$s
+3)) .
"FileHandle({$$v}) => fileno($fileno)\n"
);
}
}
elsif
(
ref
\
$v
eq
'GLOB'
) {
if
(
$globPrint
) {
dumpglob(
$s
,
"{$v}"
,
$v
, 1,
$m
-1)
if
$globPrint
;
}
elsif
(
defined
(
$fileno
=
eval
{
fileno
(\
$v
)})) {
print
( (
' '
x
$s
) .
"FileHandle({$v}) => fileno($fileno)\n"
);
}
}
};
if
($@) {
print
( (
' '
x
$s
) .
"<< value could not be dumped: $@ >>\n"
);
}
return
;
}
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] =~ /^([!~])(.)([\x00-\xff]*)/) and
($1 eq
'!'
) ^ (
eval
{(
$_
[2] .
"::"
.
$_
[0]) =~ /$2$3/});
}
sub
compactDump {
$compactDump
=
shift
if
@_
;
$compactDump
= 6*80-1
if
$compactDump
and
$compactDump
< 2;
$compactDump
;
}
sub
veryCompact {
$veryCompact
=
shift
if
@_
;
compactDump(1)
if
!
$compactDump
and
$veryCompact
;
$veryCompact
;
}
sub
unctrlSet {
if
(
@_
) {
my
$in
=
shift
;
if
(
$in
eq
'unctrl'
or
$in
eq
'quote'
) {
$unctrl
=
$in
;
}
else
{
print
"Unknown value for 'unctrl'.\n"
;
}
}
$unctrl
;
}
sub
quote {
if
(
@_
and
$_
[0] eq
'"'
) {
$tick
=
'"'
;
$unctrl
=
'quote'
;
}
elsif
(
@_
and
$_
[0] eq
'auto'
) {
$tick
=
'auto'
;
$unctrl
=
'quote'
;
}
elsif
(
@_
) {
$tick
=
"'"
;
$unctrl
=
'unctrl'
;
}
$tick
;
}
sub
dumpglob {
return
if
$DB::signal
;
my
(
$off
,
$key
,
$val
,
$all
,
$m
) =
@_
;
local
(
*entry
) =
$val
;
my
$fileno
;
if
((
$key
!~ /^_</ or
$dumpDBFiles
) and
defined
$entry
) {
print
( (
' '
x
$off
) .
"\$"
,
&unctrl
(
$key
),
" = "
);
DumpElem
$entry
, 3+
$off
,
$m
;
}
if
((
$key
!~ /^_</ or
$dumpDBFiles
) and
@entry
) {
print
( (
' '
x
$off
) .
"\@$key = (\n"
);
unwrap(\
@entry
,3+
$off
,
$m
) ;
print
( (
' '
x
$off
) .
")\n"
);
}
if
(
$key
ne
"main::"
&&
$key
ne
"DB::"
&&
%entry
&& (
$dumpPackages
or
$key
!~ /::$/)
&& (
$key
!~ /^_</ or
$dumpDBFiles
)
&& !(
$package
eq
"dumpvar"
and
$key
eq
"stab"
)) {
print
( (
' '
x
$off
) .
"\%$key = (\n"
);
unwrap(\
%entry
,3+
$off
,
$m
) ;
print
( (
' '
x
$off
) .
")\n"
);
}
if
(
defined
(
$fileno
=
eval
{
fileno
(
*entry
)})) {
print
( (
' '
x
$off
) .
"FileHandle($key) => fileno($fileno)\n"
);
}
if
(
$all
) {
if
(
defined
&entry
) {
dumpsub(
$off
,
$key
);
}
}
}
sub
dumplex {
return
if
$DB::signal
;
my
(
$key
,
$val
,
$m
,
@vars
) =
@_
;
return
if
@vars
&& !
grep
( matchlex(
$key
,
$_
),
@vars
);
local
%address
;
my
$off
= 0;
my
$fileno
;
if
(UNIVERSAL::isa(
$val
,
'ARRAY'
)) {
print
( (
' '
x
$off
) .
"$key = (\n"
);
unwrap(
$val
,3+
$off
,
$m
) ;
print
( (
' '
x
$off
) .
")\n"
);
}
elsif
(UNIVERSAL::isa(
$val
,
'HASH'
)) {
print
( (
' '
x
$off
) .
"$key = (\n"
);
unwrap(
$val
,3+
$off
,
$m
) ;
print
( (
' '
x
$off
) .
")\n"
);
}
elsif
(UNIVERSAL::isa(
$val
,
'IO'
)) {
print
( (
' '
x
$off
) .
"FileHandle($key) => fileno($fileno)\n"
);
}
else
{
print
( (
' '
x
$off
) .
&unctrl
(
$key
),
" = "
);
DumpElem
$$val
, 3+
$off
,
$m
;
}
}
sub
CvGV_name_or_bust {
my
$in
=
shift
;
return
if
$skipCvGV
;
$in
= \
&$in
;
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
,
$m
,
@vars
) =
@_
;
local
(
%address
,
$key
,
$val
,$^W);
$package
.=
"::"
unless
$package
=~ /::$/;
*stab
= *{
"main::"
};
while
(
$package
=~ /(\w+?::)/g){
*stab
= $ {stab}{$1};
}
local
$TotalStrings
= 0;
local
$Strings
= 0;
local
$CompleteTotal
= 0;
while
((
$key
,
$val
) =
each
(
%stab
)) {
return
if
$DB::signal
;
next
if
@vars
&& !
grep
( matchvar(
$key
,
$_
),
@vars
);
if
(
$usageOnly
) {
globUsage(\
$val
,
$key
)
if
(
$package
ne
'dumpvar'
or
$key
ne
'stab'
)
and
ref
(\
$val
) eq
'GLOB'
;
}
else
{
dumpglob(0,
$key
,
$val
, 0,
$m
);
}
}
if
(
$usageOnly
) {
print
"String space: $TotalStrings bytes in $Strings strings.\n"
;
$CompleteTotal
+=
$TotalStrings
;
print
"Grand total = $CompleteTotal bytes (1 level deep) + overhead.\n"
;
}
}
sub
scalarUsage {
my
$size
=
length
(
$_
[0]);
$TotalStrings
+=
$size
;
$Strings
++;
$size
;
}
sub
arrayUsage {
my
$size
= 0;
map
{
$size
+= scalarUsage(
$_
)} @{
$_
[0]};
my
$len
= @{
$_
[0]};
print
"\@$_[1] = $len item"
, (
$len
> 1 ?
"s"
:
""
),
" (data: $size bytes)\n"
if
defined
$_
[1];
$CompleteTotal
+=
$size
;
$size
;
}
sub
hashUsage {
my
@keys
=
keys
%{
$_
[0]};
my
@values
=
values
%{
$_
[0]};
my
$keys
= arrayUsage \
@keys
;
my
$values
= arrayUsage \
@values
;
my
$len
=
@keys
;
my
$total
=
$keys
+
$values
;
print
"\%$_[1] = $len item"
, (
$len
> 1 ?
"s"
:
""
),
" (keys: $keys; values: $values; total: $total bytes)\n"
if
defined
$_
[1];
$total
;
}
sub
globUsage {
local
*name
= *{
$_
[0]};
$total
= 0;
$total
+= scalarUsage
$name
if
defined
$name
;
$total
+= arrayUsage \
@name
,
$_
[1]
if
@name
;
$total
+= hashUsage \
%name
,
$_
[1]
if
%name
and
$_
[1] ne
"main::"
and
$_
[1] ne
"DB::"
;
$total
;
}
sub
packageUsage {
my
(
$package
,
@vars
) =
@_
;
$package
.=
"::"
unless
$package
=~ /::$/;
local
*stab
= *{
"main::"
};
while
(
$package
=~ /(\w+?::)/g){
*stab
= $ {stab}{$1};
}
local
$TotalStrings
= 0;
local
$CompleteTotal
= 0;
my
(
$key
,
$val
);
while
((
$key
,
$val
) =
each
(
%stab
)) {
next
if
@vars
&& !
grep
(
$key
eq
$_
,
@vars
);
globUsage \
$val
,
$key
unless
$package
eq
'dumpvar'
and
$key
eq
'stab'
;
}
print
"String space: $TotalStrings.\n"
;
$CompleteTotal
+=
$TotalStrings
;
print
"\nGrand total = $CompleteTotal bytes\n"
;
}
1;