use
5.010001;
use
vars
qw(@EXPORT @EXPORT_OK $VERSION $DEBUG)
;
*import
= \
&Exporter::import
;
@EXPORT
=
qw(dd ddx)
;
@EXPORT_OK
=
qw(dump pp dumpf quote)
;
our
$AUTHORITY
=
'cpan:PERLANCAR'
;
our
$DATE
=
'2025-02-21'
;
our
$DIST
=
'Data-Dump-Color'
;
our
$VERSION
=
'0.251'
;
$DEBUG
=
$ENV
{DEBUG};
use
vars
qw(%seen %refcnt @fixup @cfixup %require $TRY_BASE64 @FILTERS $INDENT)
;
use
vars
qw($COLOR $COLOR_THEME $INDEX $LENTHRESHOLD)
;
require
Win32::Console::ANSI
if
$^O =~ /Win/;
my
$lan_available
;
eval
{
*looks_like_number
= \
&Scalar::Util::LooksLikeNumber::looks_like_number
;
$lan_available
= 1;
1;
} or
do
{
*looks_like_number
= \
&Scalar::Util::looks_like_number
;
};
$TRY_BASE64
= 50
unless
defined
$TRY_BASE64
;
$INDENT
=
" "
unless
defined
$INDENT
;
$INDEX
= 1
unless
defined
$INDEX
;
$LENTHRESHOLD
= 500
unless
defined
$LENTHRESHOLD
;
$COLOR
= (
defined
$ENV
{NO_COLOR} ? 0 :
undef
) //
$ENV
{COLOR} // (-t STDOUT) // 1;
$COLOR_THEME
=
$ENV
{DATA_DUMP_COLOR_THEME} //
((
$ENV
{TERM} //
""
) =~ /256/ ?
'Default256'
:
'Default16'
);
our
$ct_obj
;
sub
max {
return
undef
unless
@_
;
my
$max
=
shift
;
$_
>
$max
and
$max
=
$_
foreach
@_
;
return
$max
;
}
sub
_get_color_theme_obj {
Module::Load::Util::instantiate_class_with_optional_args(
{
ns_prefixes
=>[
'ColorTheme::Data::Dump::Color'
,
'ColorTheme'
,
''
]},
$COLOR_THEME
);
}
sub
_col {
my
(
$item
,
$str
) =
@_
;
return
$str
unless
$COLOR
;
local
$ct_obj
= _get_color_theme_obj()
unless
defined
$ct_obj
;
my
$ansi
=
''
;
$item
=
$ct_obj
->get_item_color(
$item
);
if
(
defined
$item
) {
$ansi
= ColorThemeUtil::ANSI::item_color_to_ansi(
$item
);
}
if
(
length
$ansi
) {
$ansi
.
$str
.
"\e[0m"
;
}
else
{
$str
;
}
}
sub
dump
{
local
%seen
;
local
%refcnt
;
local
%require
;
local
@fixup
;
local
@cfixup
;
local
$ct_obj
= _get_color_theme_obj()
if
$COLOR
&& !(
defined
$ct_obj
);
my
$name
=
"var"
;
my
@dump
;
my
@cdump
;
for
my
$v
(
@_
) {
my
(
$val
,
$cval
) = _dump(
$v
,
$name
, [],
tied
(
$v
));
push
(
@dump
, [
$name
,
$val
]);
push
(
@cdump
, [
$name
,
$cval
]);
}
continue
{
$name
++;
}
my
$out
=
""
;
my
$cout
=
""
;
if
(
%require
) {
for
(
sort
keys
%require
) {
$out
.=
"require $_;\n"
;
$cout
.= _col(
keyword
=>
"require"
).
" "
._col(
symbol
=>
$_
).
";\n"
;
}
}
if
(
%refcnt
) {
for
my
$i
(0..
$#dump
) {
my
$name
=
$dump
[
$i
][0];
my
$cname
=
$cdump
[
$i
][0];
if
(
$refcnt
{
$name
}) {
$out
.=
"my \$$name = $dump[$i][1];\n"
;
$cout
.= _col(
keyword
=>
"my"
).
" "
._col(
symbol
=>
"\$$cname"
).
" = $cdump[$i][1];\n"
;
undef
$dump
[
$i
][1];
undef
$cdump
[
$i
][1];
}
}
for
my
$i
(0..
$#fixup
) {
$out
.=
"$fixup[$i];\n"
;
$cout
.=
"$cfixup[$i];\n"
;
}
}
my
$paren
= (
@dump
!= 1);
$out
.=
"("
if
$paren
;
$cout
.=
"("
if
$paren
;
my
(
$f
,
$cf
) = format_list(
$paren
,
undef
,
[0],
[
map
{
defined
(
$_
->[1]) ?
$_
->[1] :
"\$"
.
$_
->[0]}
@dump
],
[
map
{
defined
(
$_
->[1]) ?
$_
->[1] :
"\$"
.
$_
->[0]}
@cdump
],
\
@_
,
);
$out
.=
$f
;
$cout
.=
$cf
;
$out
.=
")"
if
$paren
;
$cout
.=
")"
if
$paren
;
if
(
%refcnt
||
%require
) {
$out
.=
";\n"
;
$cout
.=
";\n"
;
$out
=~ s/^/
$INDENT
/gm;
$cout
=~ s/^/
$INDENT
/gm;
$out
=
"do {\n$out}"
;
$cout
= _col(
keyword
=>
"do"
).
" {\n$cout}"
;
}
print
STDERR
"$cout\n"
unless
defined
wantarray
;
$cout
;
}
*pp
= \
&dump
;
sub
dd {
print
dump
(
@_
),
"\n"
;
@_
;
}
sub
ddx {
my
(
undef
,
$file
,
$line
) =
caller
;
$file
=~ s,.*[\\/],,;
my
$out
=
dump
(
@_
) .
"\n"
;
$out
=~ s/^/
$out
= _col(
linum
=>
"$file:$line: "
) .
$out
;
print
$out
;
}
sub
dumpf {
goto
&Data::Dump::Filtered::dump_filtered
;
}
sub
_dump
{
my
$ref
=
ref
$_
[0];
my
$rval
=
$ref
?
$_
[0] : \
$_
[0];
shift
;
my
(
$name
,
$idx
,
$dont_remember
,
$pclass
,
$pidx
) =
@_
;
my
(
$class
,
$type
,
$id
);
my
$strval
= overload::StrVal(
$rval
);
if
((
my
$i
=
rindex
(
$strval
,
"="
)) >= 0) {
$class
=
substr
(
$strval
, 0,
$i
);
$strval
=
substr
(
$strval
,
$i
+1);
}
if
((
my
$i
=
index
(
$strval
,
"(0x"
)) >= 0) {
$type
=
substr
(
$strval
, 0,
$i
);
$id
=
substr
(
$strval
,
$i
+ 2, -1);
}
else
{
die
"Can't parse "
. overload::StrVal(
$rval
);
}
if
($] < 5.008 &&
$type
eq
"SCALAR"
) {
$type
=
"REF"
if
$ref
eq
"REF"
;
}
warn
"\$$name(@$idx) $class $type $id ($ref)"
if
$DEBUG
;
my
$out
;
my
$cout
;
my
$comment
;
my
$hide_keys
;
if
(
@FILTERS
) {
my
$pself
=
""
;
(
$pself
,
undef
) = fullname(
"self"
, [
@$idx
[
$pidx
..(
@$idx
- 1)]])
if
$pclass
;
my
$ctx
= Data::Dump::FilterContext->new(
$rval
,
$class
,
$type
,
$ref
,
$pclass
,
$pidx
,
$idx
);
my
@bless
;
for
my
$filter
(
@FILTERS
) {
if
(
my
$f
=
$filter
->(
$ctx
,
$rval
)) {
if
(
my
$v
=
$f
->{object}) {
local
@FILTERS
;
(
$out
,
$cout
) = _dump(
$v
,
$name
,
$idx
, 1);
$dont_remember
++;
}
if
(
defined
(
my
$c
=
$f
->{
bless
})) {
push
(
@bless
,
$c
);
}
if
(
my
$c
=
$f
->{comment}) {
$comment
=
$c
;
}
if
(
defined
(
my
$c
=
$f
->{
dump
})) {
$out
=
$c
;
$cout
=
$c
;
$dont_remember
++;
}
if
(
my
$h
=
$f
->{hide_keys}) {
if
(
ref
(
$h
) eq
"ARRAY"
) {
$hide_keys
=
sub
{
for
my
$k
(
@$h
) {
return
(1, 1)
if
$k
eq
$_
[0];
}
return
(0, 0);
};
}
}
}
}
push
(
@bless
,
""
)
if
defined
(
$out
) && !
@bless
;
if
(
@bless
) {
$class
=
shift
(
@bless
);
warn
"More than one filter callback tried to bless object"
if
@bless
;
}
}
unless
(
$dont_remember
) {
if
(
my
$s
=
$seen
{
$id
}) {
my
(
$sname
,
$sidx
) =
@$s
;
$refcnt
{
$sname
}++;
my
(
$sref
,
$csref
) = fullname(
$sname
,
$sidx
,
(
$ref
&&
$type
eq
"SCALAR"
));
warn
"SEEN: [\$$name(@$idx)] => [\$$sname(@$sidx)] ($ref,$sref)"
if
$DEBUG
;
return
(
$sref
,
$csref
)
unless
$sname
eq
$name
;
$refcnt
{
$name
}++;
my
(
$fn
,
$cfn
) = fullname(
$name
,
$idx
);
push
(
@fixup
,
"$fn = $sref"
);
push
(
@cfixup
,
"$cfn = $csref"
);
return
(
"do{my \$fix}"
,
_col(
keyword
=>
"do"
).
"{"
._col(
keyword
=>
"my"
).
" "
._col(
symbol
=>
"\$fix"
).
"}"
,
)
if
@$idx
&&
$idx
->[-1] eq
'$'
;
my
$str
= squote(
$sref
);
return
(
$str
,
_col(
string
=>
$str
),
);
}
$seen
{
$id
} = [
$name
,
$idx
];
}
if
(
$class
) {
$pclass
=
$class
;
$pidx
=
@$idx
;
}
if
(
defined
$out
) {
}
elsif
(
$type
eq
"SCALAR"
||
$type
eq
"REF"
||
$type
eq
"REGEXP"
) {
if
(
$ref
) {
if
(
$class
&&
$class
eq
"Regexp"
) {
my
$v
=
"$rval"
;
my
$mod
=
""
;
if
(
$v
=~ /^\(\?\^?([msix-]*):([\x00-\xFF]*)\)\z/) {
$mod
= $1;
$v
= $2;
$mod
=~ s/-.*//;
}
my
$sep
=
'/'
;
my
$sep_count
= (
$v
=~
tr
/\///);
if
(
$sep_count
) {
for
(
'|'
,
','
,
':'
,
'#'
) {
my
$c
=
eval
"\$v =~ tr/\Q$_\E//"
;
if
(
$c
<
$sep_count
) {
$sep
=
$_
;
$sep_count
=
$c
;
last
if
$sep_count
== 0;
}
}
}
$v
=~ s/\Q
$sep
\E/\\
$sep
/g;
$out
=
"qr$sep$v$sep$mod"
;
$cout
= _col(
'Regexp'
,
$out
);
undef
(
$class
);
}
else
{
delete
$seen
{
$id
}
if
$type
eq
"SCALAR"
;
my
(
$val
,
$cval
) = _dump(
$$rval
,
$name
, [
@$idx
, [
"\$"
,
"\$"
]], 0,
$pclass
,
$pidx
);
$out
=
$class
?
"do{\\(my \$o = $val)}"
:
"\\$val"
;
$cout
=
$class
? _col(
keyword
=>
"do"
).
"{\\("
._col(
keyword
=>
"my"
).
" "
._col(
symbol
=>
"\$o"
).
" = $cval)}"
:
"\\$cval"
;
}
}
else
{
if
(!
defined
$$rval
) {
$out
=
'undef'
;
$cout
= _col(
'undef'
,
"undef"
);
}
elsif
(
my
$ntype
= looks_like_number(
$$rval
)) {
if
(
$lan_available
) {
my
$val
=
$ntype
< 20 ?
qq("$$rval")
:
$$rval
;
my
$col
=
$ntype
=~ /^(5|13|8704)$/ ?
"float"
:
"number"
;
$out
=
$val
;
$cout
= _col(
$col
=>
$val
);
}
else
{
my
$val
=
$$rval
;
my
$col
=
"number"
;
$out
=
$val
;
$cout
= _col(
$col
=>
$val
);
}
}
else
{
$out
= str(
$$rval
);
$cout
= _col(
string
=>
$out
);
}
if
(
$class
&& !
@$idx
) {
$refcnt
{
$name
}++;
my
(
$obj
,
$cobj
) = fullname(
$name
,
$idx
);
my
$cl
= quote(
$class
);
push
(
@fixup
,
"bless \\$obj, $cl"
);
push
(
@cfixup
, _col(
keyword
=>
"bless"
).
" \\$cobj, "
._col(
string
=>
$cl
));
}
}
}
elsif
(
$type
eq
"GLOB"
) {
if
(
$ref
) {
delete
$seen
{
$id
};
my
(
$val
,
$cval
) = _dump(
$$rval
,
$name
, [
@$idx
, [
"*"
,
"*"
]], 0,
$pclass
,
$pidx
);
$out
=
"\\$val"
;
$cout
=
"\\$cval"
;
if
(
$out
=~ /^\\\
*Symbol::
/) {
$require
{Symbol}++;
$out
=
"Symbol::gensym()"
;
$cout
= _col(
glob
=>
$out
);
}
}
else
{
my
$val
=
"$$rval"
;
$out
=
"$$rval"
;
$cout
= _col(
glob
=>
$out
);
for
my
$k
(
qw(SCALAR ARRAY HASH)
) {
my
$gval
= *
$$rval
{
$k
};
next
unless
defined
$gval
;
next
if
$k
eq
"SCALAR"
&& !
defined
$$gval
;
my
$f
=
scalar
@fixup
;
push
(
@fixup
,
"RESERVED"
);
my
$cgval
;
(
$gval
,
$cgval
) = _dump(
$gval
,
$name
, [
@$idx
, [
"*{$k}"
,
"*{"
._col(
string
=>
$k
).
"}"
]], 0,
$pclass
,
$pidx
);
$refcnt
{
$name
}++;
my
(
$gname
,
$cgname
) = fullname(
$name
,
$idx
);
$fixup
[
$f
] =
"$gname = $gval"
;
$cfixup
[
$f
] =
"$gname = $cgval"
;
}
}
}
elsif
(
$type
eq
"ARRAY"
) {
my
@vals
;
my
@cvals
;
my
$tied
= tied_str(
tied
(
@$rval
));
my
$i
= 0;
for
my
$v
(
@$rval
) {
my
(
$d
,
$cd
) = _dump(
$v
,
$name
, [
@$idx
, [
"[$i]"
,
"["
._col(
number
=>
$i
).
"]"
]],
$tied
,
$pclass
,
$pidx
);
push
@vals
,
$d
;
push
@cvals
,
$cd
;
$i
++;
}
my
(
$f
,
$cf
) = format_list(1,
$tied
, [
scalar
(
@$idx
)], \
@vals
, \
@cvals
,
$rval
);
$out
=
"[$f]"
;
$cout
=
"[$cf]"
;
}
elsif
(
$type
eq
"HASH"
) {
my
(
@keys
,
@vals
,
@cvals
,
@origk
,
@origv
);
my
$tied
= tied_str(
tied
(
%$rval
));
my
$kstat_max
= 0;
my
$kstat_sum
= 0;
my
$kstat_sum2
= 0;
my
@orig_keys
=
keys
%$rval
;
if
(
$hide_keys
) {
@orig_keys
=
grep
!
$hide_keys
->(
$_
),
@orig_keys
;
}
my
$text_keys
= 0;
for
(
@orig_keys
) {
$text_keys
++,
last
unless
/^[-+]?(?:0|[1-9]\d*)(?:\.\d+)?\z/;
}
if
(
$text_keys
) {
@orig_keys
=
sort
{
lc
(
$a
) cmp
lc
(
$b
) }
@orig_keys
;
}
else
{
@orig_keys
=
sort
{
$a
<=>
$b
}
@orig_keys
;
}
my
$quote
;
for
my
$key
(
@orig_keys
) {
next
if
$key
=~ /^-?[a-zA-Z_]\w*\z/;
next
if
$key
=~ /^-?[1-9]\d{0,8}\z/;
$quote
++;
last
;
}
my
@lenvlastline
;
for
my
$key
(
@orig_keys
) {
my
$val
= \
$rval
->{
$key
};
push
(
@origk
,
$key
);
$key
= quote(
$key
)
if
$quote
;
$kstat_max
=
length
(
$key
)
if
length
(
$key
) >
$kstat_max
;
$kstat_sum
+=
length
(
$key
);
$kstat_sum2
+=
length
(
$key
)
*length
(
$key
);
push
(
@keys
,
$key
);
my
(
$v
,
$cv
) = _dump(
$$val
,
$name
, [
@$idx
, [
"{$key}"
,
"{"
._col(
string
=>
$key
).
"}"
]],
$tied
,
$pclass
,
$pidx
);
push
(
@vals
,
$v
);
push
(
@cvals
,
$cv
);
push
(
@origv
,
$$val
);
my
(
$vlastline
) =
$v
=~ /(.*)\z/;
my
$lenvlastline
=
length
(
$vlastline
);
push
@lenvlastline
,
$lenvlastline
;
}
my
$nl
=
""
;
my
$klen_pad
= 0;
my
$tmp
=
"@keys @vals"
;
if
(
length
(
$tmp
) > 60 ||
$tmp
=~ /\n/ ||
$tied
) {
$nl
=
"\n"
;
if
(
$kstat_max
< 4) {
$klen_pad
=
$kstat_max
;
}
elsif
(
@keys
>= 2) {
my
$n
=
@keys
;
my
$avg
=
$kstat_sum
/
$n
;
my
$stddev
=
sqrt
((
$kstat_sum2
-
$n
*
$avg
*
$avg
) / (
$n
- 1));
if
(
$stddev
/
$kstat_max
< 0.25) {
$klen_pad
=
$kstat_max
;
}
if
(
$DEBUG
) {
push
(
@keys
,
"__S"
);
push
(
@vals
,
sprintf
(
"%.2f (%d/%.1f/%.1f)"
,
$stddev
/
$kstat_max
,
$kstat_max
,
$avg
,
$stddev
));
push
(
@cvals
,
sprintf
(
"%.2f (%d/%.1f/%.1f)"
,
$stddev
/
$kstat_max
,
$kstat_max
,
$avg
,
$stddev
));
}
}
}
my
$maxkvlen
= 0;
for
(0..
$#keys
) {
my
$klen
=
length
(
$keys
[
$_
]);
$klen
=
$klen_pad
if
$klen
<
$klen_pad
;
my
$kvlen
=
$klen
+
$lenvlastline
[
$_
];
$maxkvlen
=
$kvlen
if
$maxkvlen
<
$kvlen
;
}
$maxkvlen
= 80
if
$maxkvlen
> 80;
$out
=
"{$nl"
;
$cout
=
"{$nl"
;
$out
.=
"$INDENT# $tied$nl"
if
$tied
;
$cout
.=
$INDENT
._col(
comment
=>
"# $tied"
).
$nl
if
$tied
;
my
$i
= 0;
my
$idxwidth
=
length
(~~
@keys
);
while
(
@keys
) {
my
$key
=
shift
(
@keys
);
my
$val
=
shift
@vals
;
my
$cval
=
shift
@cvals
;
my
$origk
=
shift
@origk
;
my
$origv
=
shift
@origv
;
my
$lenvlastline
=
shift
@lenvlastline
;
my
$vmultiline
=
length
(
$val
) >
$lenvlastline
;
my
$vpad
=
$INDENT
. (
" "
x (
$klen_pad
?
$klen_pad
+ 4 : 0));
$val
=~ s/\n/\n
$vpad
/gm;
$cval
=~ s/\n/\n
$vpad
/gm;
my
$kpad
=
$nl
?
$INDENT
:
" "
;
my
$pad_len
= (
$klen_pad
-
length
(
$key
));
if
(
$pad_len
< 0) {
$pad_len
= 0; }
$key
.=
" "
x
$pad_len
if
$nl
;
my
$cpad
=
" "
x max(0,
$maxkvlen
- (
$vmultiline
? -6+
length
(
$vpad
) :
length
(
$key
)) -
$lenvlastline
);
my
$visaid
=
""
;
$visaid
.=
sprintf
(
"%s{%${idxwidth}i}"
,
"."
x
@$idx
,
$i
)
if
$INDEX
;
$visaid
.=
" klen="
.
length
(
$origk
)
if
defined
$origk
&&
length
(
$origk
) >=
$LENTHRESHOLD
;
$visaid
.=
" vlen="
.
length
(
$origv
)
if
defined
$origv
&&
length
(
$origv
) >=
$LENTHRESHOLD
;
$out
.=
"$kpad$key => $val,"
. (
$nl
&&
length
(
$visaid
) ?
" $cpad# $visaid"
:
""
) .
$nl
;
$cout
.=
$kpad
._col(
key
=>
$key
).
" => $cval,"
.(
$nl
&&
length
(
$visaid
) ?
" $cpad"
._col(
comment
=>
"# $visaid"
) :
""
) .
$nl
;
$i
++;
}
$out
=~ s/,$/ /
unless
$nl
;
$cout
=~ s/,$/ /
unless
$nl
;
$out
.=
"}"
;
$cout
.=
"}"
;
}
elsif
(
$type
eq
"CODE"
) {
$out
=
'sub { ... }'
;
$cout
= _col(
keyword
=>
'sub'
).
' { ... }'
;
}
elsif
(
$type
eq
"VSTRING"
) {
$out
=
sprintf
+(
$ref
?
'\v%vd'
:
'v%vd'
),
$$rval
;
$cout
= _col(
string
=>
$out
);
}
else
{
warn
"Can't handle $type data"
;
$out
=
"'#$type#'"
;
$cout
= _col(
comment
=>
$out
);
}
if
(
$class
&&
$ref
) {
$cout
= _col(
keyword
=>
"bless"
).
"($cout, "
. _col(
string
=> quote(
$class
)) .
")"
;
$out
=
"bless($out, "
.quote(
$class
).
")"
;
}
if
(
$comment
) {
$comment
=~ s/^/
$comment
.=
"\n"
unless
$comment
=~ /\n\z/;
$comment
=~ s/^
$cout
= _col(
comment
=>
$comment
).
$out
;
$out
=
"$comment$out"
;
}
return
(
$out
,
$cout
);
}
sub
tied_str {
my
$tied
=
shift
;
if
(
$tied
) {
if
(
my
$tied_ref
=
ref
(
$tied
)) {
$tied
=
"tied $tied_ref"
;
}
else
{
$tied
=
"tied"
;
}
}
return
$tied
;
}
sub
fullname
{
my
(
$name
,
$idx
,
$ref
) =
@_
;
substr
(
$name
, 0, 0) =
"\$"
;
my
$cname
=
$name
;
my
@i
=
@$idx
;
if
(
$ref
&&
@i
&&
$i
[0][0] eq
"\$"
) {
shift
(
@i
);
$ref
= 0;
}
while
(
@i
&&
$i
[0][0] eq
"\$"
) {
shift
@i
;
$name
=
"\$$name"
;
$cname
= _col(
symbol
=>
$name
);
}
my
$last_was_index
;
for
my
$i
(
@i
) {
if
(
$i
->[0] eq
"*"
||
$i
->[0] eq
"\$"
) {
$last_was_index
= 0;
$name
=
"$i->[0]\{$name}"
;
$cname
=
"$i->[1]\{$cname}"
;
}
elsif
(
$i
->[0] =~ s/^\*//) {
$name
.=
$i
->[0];
$cname
.=
$i
->[1];
$last_was_index
++;
}
else
{
$name
.=
"->"
;
$cname
.=
"->"
;
$name
.=
$i
->[0];
$cname
.=
$i
->[1];
}
}
$name
=
"\\$name"
if
$ref
;
(
$name
,
$cname
);
}
sub
format_list
{
my
$paren
=
shift
;
my
$comment
=
shift
;
my
$extra
=
shift
;
my
$indent_lim
=
$paren
? 0 : 1;
my
@vals
= @{
shift
(
@_
) };
my
@cvals
= @{
shift
(
@_
) };
my
@orig
= @{
shift
(
@_
) };
if
(
@vals
> 3) {
my
$i
= 0;
while
(
$i
<
@vals
) {
my
$j
=
$i
+ 1;
my
$v
=
$vals
[
$i
];
while
(
$j
<
@vals
) {
if
(
$v
eq
"0"
||
$v
=~ /^-?[1-9]\d{0,9}\z/) {
$v
++;
}
elsif
(
$v
=~ /^
"([A-Za-z]{1,3}\d*)"
\z/) {
$v
= $1;
$v
++;
$v
=
qq("$v")
;
}
else
{
last
;
}
last
if
$vals
[
$j
] ne
$v
;
$j
++;
}
if
(
$j
-
$i
> 3) {
splice
(
@vals
,
$i
,
$j
-
$i
,
"$vals[$i] .. $vals[$j-1]"
);
splice
(
@cvals
,
$i
,
$j
-
$i
,
"$cvals[$i] .. $cvals[$j-1]"
);
splice
(
@orig
,
$i
,
$j
-
$i
, [
@orig
[
$i
..
$j
-1]]);
}
$i
++;
}
}
my
$tmp
=
"@vals"
;
if
(
$comment
|| (
@vals
>
$indent_lim
&& (
length
(
$tmp
) > 60 ||
$tmp
=~ /\n/))) {
my
$maxvlen
= 0;
for
(
@vals
) {
my
(
$vfirstline
) = /\A(.*)/;
my
$lenvfirstline
=
length
(
$vfirstline
);
$maxvlen
=
$lenvfirstline
if
$maxvlen
<
$lenvfirstline
;
}
$maxvlen
= 80
if
$maxvlen
> 80;
$maxvlen
+=
length
(
$INDENT
);
my
@res
= (
"\n"
,
$comment
?
"$INDENT# $comment\n"
:
""
);
my
@cres
= (
"\n"
,
$comment
?
$INDENT
._col(
"# $comment"
).
"\n"
:
""
);
my
@elem
=
@vals
;
my
@celem
=
@cvals
;
for
(
@elem
) { s/^/
$INDENT
/gm; }
for
(
@celem
) { s/^/
$INDENT
/gm; }
my
$idxwidth
=
length
(~~
@elem
);
for
my
$i
(0..
$#elem
) {
my
(
$vlastline
) =
$elem
[
$i
] =~ /(.*)\z/;
my
$cpad
=
" "
x max(0,
$maxvlen
-
length
(
$vlastline
));
my
$visaid
=
""
;
$visaid
.=
sprintf
(
"%s[%${idxwidth}i]"
,
"."
x
$extra
->[0],
$i
)
if
$INDEX
;
$visaid
.=
" len="
.
length
(
$orig
[
$i
])
if
defined
$orig
[
$i
] &&
length
(
$orig
[
$i
]) >=
$LENTHRESHOLD
;
push
@res
,
$elem
[
$i
],
","
, (
length
(
$visaid
) ?
" $cpad# $visaid"
:
""
),
"\n"
;
push
@cres
,
$celem
[
$i
],
","
, (
length
(
$visaid
) ?
" $cpad"
._col(
comment
=>
"# $visaid"
) :
""
),
"\n"
;
}
return
(
join
(
""
,
@res
),
join
(
""
,
@cres
));
}
else
{
return
(
join
(
", "
,
@vals
),
join
(
", "
,
@cvals
));
}
}
sub
str {
if
(
length
(
$_
[0]) > 20) {
for
(
$_
[0]) {
if
(/^(.)\1\1\1/s) {
unless
(/[^\Q$1\E]/) {
my
$base
= quote($1);
my
$repeat
=
length
;
return
"($base x $repeat)"
}
}
if
(
length
(
$_
) < 16 * 1024 && /^(.{2,5}?)\1*\z/s) {
my
$base
= quote($1);
my
$repeat
=
length
(
$_
)/
length
($1);
return
"($base x $repeat)"
;
}
}
}
local
$_
=
"e
;
if
(
length
(
$_
) > 40 && !/\\x\{/ &&
length
(
$_
) > (
length
(
$_
[0]) * 2)) {
if
(
$TRY_BASE64
&&
length
(
$_
[0]) >
$TRY_BASE64
&&
(
defined
&utf8::is_utf8
&& !utf8::is_utf8(
$_
[0])) &&
{
$require
{
"MIME::Base64"
}++;
return
"MIME::Base64::decode(\""
.
MIME::Base64::encode(
$_
[0],
""
) .
"\")"
;
}
return
"pack(\"H*\",\""
.
unpack
(
"H*"
,
$_
[0]) .
"\")"
;
}
return
$_
;
}
my
%esc
= (
"\a"
=>
"\\a"
,
"\b"
=>
"\\b"
,
"\t"
=>
"\\t"
,
"\n"
=>
"\\n"
,
"\f"
=>
"\\f"
,
"\r"
=>
"\\r"
,
"\e"
=>
"\\e"
,
);
sub
quote {
local
(
$_
) =
$_
[0];
s/([\\\"\@\$])/\\$1/g;
return
qq("$_")
unless
/[^\040-\176]/;
s/([\a\b\t\n\f\r\e])/
$esc
{$1}/g;
s/([\0-\037])(?!\d)/
sprintf
(
'\\%o'
,
ord
($1))/eg;
s/([\0-\037\177-\377])/
sprintf
(
'\\x%02X'
,
ord
($1))/eg;
s/([^\040-\176])/
sprintf
(
'\\x{%X}'
,
ord
($1))/eg;
return
qq("$_")
;
}
sub
squote {
local
(
$_
) =
$_
[0];
s/([\\'])/\\$1/g;
return
qq('$_')
;
}
1;