Some escapes were mentioned twice, although they're not qr//-specific
[p5sagit/p5-mst-13.2.git] / lib / dumpvar.pl
CommitLineData
d338d6fe 1require 5.002; # For (defined ref)
2package dumpvar;
3
4# Needed for PrettyPrinter only:
5
6# require 5.001; # Well, it coredumps anyway undef DB in 5.000 (not now)
7
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$winsize = 80 unless defined $winsize;
16
17
18# Defaults
19
20# $globPrint = 1;
21$printUndef = 1 unless defined $printUndef;
54d04a52 22$tick = "auto" unless defined $tick;
d338d6fe 23$unctrl = 'quote' unless defined $unctrl;
54d04a52 24$subdump = 1;
22fae026 25$dumpReused = 0 unless defined $dumpReused;
ee239bfe 26$bareStringify = 1 unless defined $bareStringify;
d338d6fe 27
28sub main::dumpValue {
29 local %address;
b2391ea8 30 local $^W=0;
d338d6fe 31 (print "undef\n"), return unless defined $_[0];
32 (print &stringify($_[0]), "\n"), return unless ref $_[0];
f338af47 33 push @_, -1 if @_ == 1;
34 dumpvar::unwrap($_[0], 0, $_[1]);
d338d6fe 35}
36
37# This one is good for variable names:
38
39sub unctrl {
40 local($_) = @_;
41 local($v) ;
42
43 return \$_ if ref \$_ eq "GLOB";
2f3efc97 44 if (ord('A') == 193) { # EBCDIC.
45 # EBCDIC has no concept of "\cA" or "A" being related
46 # to each other by a linear/boolean mapping.
47 } else {
48 s/([\001-\037\177])/'^'.pack('c',ord($1)^64)/eg;
49 }
d338d6fe 50 $_;
51}
52
f8543d02 53sub uniescape {
54 join("",
55 map { $_ > 255 ? sprintf("\\x{%04X}", $_) : chr($_) }
56 unpack("U*", $_[0]));
57}
58
d338d6fe 59sub stringify {
60 local($_,$noticks) = @_;
61 local($v) ;
54d04a52 62 my $tick = $tick;
d338d6fe 63
64 return 'undef' unless defined $_ or not $printUndef;
65 return $_ . "" if ref \$_ eq 'GLOB';
ee239bfe 66 $_ = &{'overload::StrVal'}($_)
67 if $bareStringify and ref $_
475342a6 68 and %overload:: and defined &{'overload::StrVal'};
ee239bfe 69
54d04a52 70 if ($tick eq 'auto') {
2f3efc97 71 if (ord('A') == 193) {
72 if (/[\000-\011]/ or /[\013-\024\31-\037\177]/) {
73 $tick = '"';
74 } else {
75 $tick = "'";
76 }
77 } else {
78 if (/[\000-\011\013-\037\177]/) {
79 $tick = '"';
80 } else {
81 $tick = "'";
82 }
83 }
54d04a52 84 }
d338d6fe 85 if ($tick eq "'") {
86 s/([\'\\])/\\$1/g;
87 } elsif ($unctrl eq 'unctrl') {
88 s/([\"\\])/\\$1/g ;
89 s/([\000-\037\177])/'^'.pack('c',ord($1)^64)/eg;
f8543d02 90 # uniescape?
d338d6fe 91 s/([\200-\377])/'\\0x'.sprintf('%2X',ord($1))/eg
92 if $quoteHighBit;
93 } elsif ($unctrl eq 'quote') {
94 s/([\"\\\$\@])/\\$1/g if $tick eq '"';
95 s/\033/\\e/g;
2f3efc97 96 if (ord('A') == 193) { # EBCDIC.
97 s/([\000-\037\177])/'\\c'.chr(193)/eg; # Unfinished.
98 } else {
99 s/([\000-\037\177])/'\\c'._escaped_ord($1)/eg;
100 }
d338d6fe 101 }
f8543d02 102 $_ = uniescape($_);
d338d6fe 103 s/([\200-\377])/'\\'.sprintf('%3o',ord($1))/eg if $quoteHighBit;
104 ($noticks || /^\d+(\.\d*)?\Z/)
105 ? $_
106 : $tick . $_ . $tick;
107}
108
bd6f3d36 109# Ensure a resulting \ is escaped to be \\
110sub _escaped_ord {
111 my $chr = shift;
112 $chr = chr(ord($chr)^64);
113 $chr =~ s{\\}{\\\\}g;
114 return $chr;
115}
116
d338d6fe 117sub ShortArray {
118 my $tArrayDepth = $#{$_[0]} ;
119 $tArrayDepth = $#{$_[0]} < $arrayDepth-1 ? $#{$_[0]} : $arrayDepth-1
120 unless $arrayDepth eq '' ;
121 my $shortmore = "";
122 $shortmore = " ..." if $tArrayDepth < $#{$_[0]} ;
123 if (!grep(ref $_, @{$_[0]})) {
124 $short = "0..$#{$_[0]} '" .
125 join("' '", @{$_[0]}[0..$tArrayDepth]) . "'$shortmore";
126 return $short if length $short <= $compactDump;
127 }
128 undef;
129}
130
131sub DumpElem {
132 my $short = &stringify($_[0], ref $_[0]);
133 if ($veryCompact && ref $_[0]
134 && (ref $_[0] eq 'ARRAY' and !grep(ref $_, @{$_[0]}) )) {
135 my $end = "0..$#{$v} '" .
136 join("' '", @{$_[0]}[0..$tArrayDepth]) . "'$shortmore";
137 } elsif ($veryCompact && ref $_[0]
138 && (ref $_[0] eq 'HASH') and !grep(ref $_, values %{$_[0]})) {
139 my $end = 1;
140 $short = $sp . "0..$#{$v} '" .
141 join("' '", @{$v}[0..$tArrayDepth]) . "'$shortmore";
142 } else {
143 print "$short\n";
9bcb75ad 144 unwrap($_[0],$_[1],$_[2]) if ref $_[0];
d338d6fe 145 }
146}
147
148sub unwrap {
149 return if $DB::signal;
150 local($v) = shift ;
151 local($s) = shift ; # extra no of spaces
d03c2a1b 152 local($m) = shift ; # maximum recursion depth
153 return if $m == 0;
ee239bfe 154 local(%v,@v,$sp,$value,$key,@sortKeys,$more,$shortmore,$short) ;
d338d6fe 155 local($tHashDepth,$tArrayDepth) ;
156
157 $sp = " " x $s ;
158 $s += 3 ;
159
160 # Check for reused addresses
161 if (ref $v) {
ee239bfe 162 my $val = $v;
163 $val = &{'overload::StrVal'}($v)
475342a6 164 if %overload:: and defined &{'overload::StrVal'};
9bcb75ad 165 # Match type and address.
166 # Unblessed references will look like TYPE(0x...)
167 # Blessed references will look like Class=TYPE(0x...)
168 ($start_part, $val) = split /=/,$val;
169 $val = $start_part unless defined $val;
170 ($item_type, $address) =
171 $val =~ /([^\(]+) # Keep stuff that's
172 # not an open paren
173 \( # Skip open paren
174 (0x[0-9a-f]+) # Save the address
175 \) # Skip close paren
176 $/x; # Should be at end now
177
22fae026 178 if (!$dumpReused && defined $address) {
d338d6fe 179 $address{$address}++ ;
180 if ( $address{$address} > 1 ) {
181 print "${sp}-> REUSED_ADDRESS\n" ;
182 return ;
183 }
184 }
185 } elsif (ref \$v eq 'GLOB') {
9bcb75ad 186 # This is a raw glob. Special handling for that.
d338d6fe 187 $address = "$v" . ""; # To avoid a bug with globs
188 $address{$address}++ ;
189 if ( $address{$address} > 1 ) {
190 print "${sp}*DUMPED_GLOB*\n" ;
191 return ;
192 }
193 }
194
7894fbab 195 if (ref $v eq 'Regexp') {
9bcb75ad 196 # Reformat the regexp to look the standard way.
7894fbab 197 my $re = "$v";
198 $re =~ s,/,\\/,g;
199 print "$sp-> qr/$re/\n";
200 return;
201 }
202
9bcb75ad 203 if ( $item_type eq 'HASH' ) {
204 # Hash ref or hash-based object.
205 my @sortKeys = sort keys(%$v) ;
d338d6fe 206 undef $more ;
207 $tHashDepth = $#sortKeys ;
208 $tHashDepth = $#sortKeys < $hashDepth-1 ? $#sortKeys : $hashDepth-1
209 unless $hashDepth eq '' ;
210 $more = "....\n" if $tHashDepth < $#sortKeys ;
211 $shortmore = "";
212 $shortmore = ", ..." if $tHashDepth < $#sortKeys ;
213 $#sortKeys = $tHashDepth ;
214 if ($compactDump && !grep(ref $_, values %{$v})) {
215 #$short = $sp .
216 # (join ', ',
217# Next row core dumps during require from DB on 5.000, even with map {"_"}
218 # map {&stringify($_) . " => " . &stringify($v->{$_})}
219 # @sortKeys) . "'$shortmore";
220 $short = $sp;
221 my @keys;
222 for (@sortKeys) {
223 push @keys, &stringify($_) . " => " . &stringify($v->{$_});
224 }
225 $short .= join ', ', @keys;
226 $short .= $shortmore;
227 (print "$short\n"), return if length $short <= $compactDump;
228 }
229 for $key (@sortKeys) {
230 return if $DB::signal;
231 $value = $ {$v}{$key} ;
232 print "$sp", &stringify($key), " => ";
d03c2a1b 233 DumpElem $value, $s, $m-1;
d338d6fe 234 }
235 print "$sp empty hash\n" unless @sortKeys;
236 print "$sp$more" if defined $more ;
9bcb75ad 237 } elsif ( $item_type eq 'ARRAY' ) {
238 # Array ref or array-based object. Also: undef.
239 # See how big the array is.
d338d6fe 240 $tArrayDepth = $#{$v} ;
241 undef $more ;
9bcb75ad 242 # Bigger than the max?
d338d6fe 243 $tArrayDepth = $#{$v} < $arrayDepth-1 ? $#{$v} : $arrayDepth-1
600d99fa 244 if defined $arrayDepth && $arrayDepth ne '';
9bcb75ad 245 # Yep. Don't show it all.
d338d6fe 246 $more = "....\n" if $tArrayDepth < $#{$v} ;
247 $shortmore = "";
248 $shortmore = " ..." if $tArrayDepth < $#{$v} ;
9bcb75ad 249
d338d6fe 250 if ($compactDump && !grep(ref $_, @{$v})) {
251 if ($#$v >= 0) {
54d04a52 252 $short = $sp . "0..$#{$v} " .
253 join(" ",
d9182636 254 map {exists $v->[$_] ? stringify $v->[$_] : "empty"} ($[..$tArrayDepth)
255 ) . "$shortmore";
d338d6fe 256 } else {
257 $short = $sp . "empty array";
258 }
259 (print "$short\n"), return if length $short <= $compactDump;
260 }
261 #if ($compactDump && $short = ShortArray($v)) {
262 # print "$short\n";
263 # return;
264 #}
265 for $num ($[ .. $tArrayDepth) {
266 return if $DB::signal;
267 print "$sp$num ";
d9182636 268 if (exists $v->[$num]) {
9bcb75ad 269 if (defined $v->[$num]) {
270 DumpElem $v->[$num], $s, $m-1;
271 }
272 else {
273 print "undef\n";
274 }
d9182636 275 } else {
276 print "empty slot\n";
277 }
d338d6fe 278 }
279 print "$sp empty array\n" unless @$v;
280 print "$sp$more" if defined $more ;
9bcb75ad 281 } elsif ( $item_type eq 'SCALAR' ) {
282 unless (defined $$v) {
283 print "$sp-> undef\n";
284 return;
285 }
d338d6fe 286 print "$sp-> ";
d03c2a1b 287 DumpElem $$v, $s, $m-1;
9bcb75ad 288 } elsif ( $item_type eq 'REF' ) {
289 print "$sp-> $$v\n";
290 return unless defined $$v;
291 unwrap($$v, $s+3, $m-1);
292 } elsif ( $item_type eq 'CODE' ) {
293 # Code object or reference.
54d04a52 294 print "$sp-> ";
295 dumpsub (0, $v);
9bcb75ad 296 } elsif ( $item_type eq 'GLOB' ) {
297 # Glob object or reference.
d338d6fe 298 print "$sp-> ",&stringify($$v,1),"\n";
299 if ($globPrint) {
300 $s += 3;
3a4b996c 301 dumpglob($s, "{$$v}", $$v, 1, $m-1);
bef1d284 302 } elsif (defined ($fileno = eval {fileno($v)})) {
d338d6fe 303 print( (' ' x ($s+3)) . "FileHandle({$$v}) => fileno($fileno)\n" );
304 }
305 } elsif (ref \$v eq 'GLOB') {
9bcb75ad 306 # Raw glob (again?)
d338d6fe 307 if ($globPrint) {
3a4b996c 308 dumpglob($s, "{$v}", $v, 1, $m-1) if $globPrint;
bef1d284 309 } elsif (defined ($fileno = eval {fileno(\$v)})) {
d338d6fe 310 print( (' ' x $s) . "FileHandle({$v}) => fileno($fileno)\n" );
311 }
312 }
313}
314
a7b657ee 315sub matchlex {
316 (my $var = $_[0]) =~ s/.//;
317 $var eq $_[1] or
318 ($_[1] =~ /^([!~])(.)([\x00-\xff]*)/) and
319 ($1 eq '!') ^ (eval { $var =~ /$2$3/ });
320}
321
d338d6fe 322sub matchvar {
323 $_[0] eq $_[1] or
b2391ea8 324 ($_[1] =~ /^([!~])(.)([\x00-\xff]*)/) and
325 ($1 eq '!') ^ (eval {($_[2] . "::" . $_[0]) =~ /$2$3/});
d338d6fe 326}
327
328sub compactDump {
329 $compactDump = shift if @_;
330 $compactDump = 6*80-1 if $compactDump and $compactDump < 2;
331 $compactDump;
332}
333
334sub veryCompact {
335 $veryCompact = shift if @_;
336 compactDump(1) if !$compactDump and $veryCompact;
337 $veryCompact;
338}
339
340sub unctrlSet {
341 if (@_) {
342 my $in = shift;
343 if ($in eq 'unctrl' or $in eq 'quote') {
344 $unctrl = $in;
345 } else {
346 print "Unknown value for `unctrl'.\n";
347 }
348 }
349 $unctrl;
350}
351
352sub quote {
353 if (@_ and $_[0] eq '"') {
354 $tick = '"';
355 $unctrl = 'quote';
54d04a52 356 } elsif (@_ and $_[0] eq 'auto') {
357 $tick = 'auto';
358 $unctrl = 'quote';
d338d6fe 359 } elsif (@_) { # Need to set
360 $tick = "'";
361 $unctrl = 'unctrl';
362 }
363 $tick;
364}
365
366sub dumpglob {
367 return if $DB::signal;
3a4b996c 368 my ($off,$key, $val, $all, $m) = @_;
d338d6fe 369 local(*entry) = $val;
370 my $fileno;
54d04a52 371 if (($key !~ /^_</ or $dumpDBFiles) and defined $entry) {
d338d6fe 372 print( (' ' x $off) . "\$", &unctrl($key), " = " );
3a4b996c 373 DumpElem $entry, 3+$off, $m;
d338d6fe 374 }
475342a6 375 if (($key !~ /^_</ or $dumpDBFiles) and @entry) {
d338d6fe 376 print( (' ' x $off) . "\@$key = (\n" );
3a4b996c 377 unwrap(\@entry,3+$off,$m) ;
d338d6fe 378 print( (' ' x $off) . ")\n" );
379 }
475342a6 380 if ($key ne "main::" && $key ne "DB::" && %entry
d338d6fe 381 && ($dumpPackages or $key !~ /::$/)
54d04a52 382 && ($key !~ /^_</ or $dumpDBFiles)
d338d6fe 383 && !($package eq "dumpvar" and $key eq "stab")) {
384 print( (' ' x $off) . "\%$key = (\n" );
3a4b996c 385 unwrap(\%entry,3+$off,$m) ;
d338d6fe 386 print( (' ' x $off) . ")\n" );
387 }
bef1d284 388 if (defined ($fileno = eval{fileno(*entry)})) {
d338d6fe 389 print( (' ' x $off) . "FileHandle($key) => fileno($fileno)\n" );
390 }
391 if ($all) {
392 if (defined &entry) {
54d04a52 393 dumpsub($off, $key);
d338d6fe 394 }
395 }
396}
397
a7b657ee 398sub dumplex {
399 return if $DB::signal;
400 my ($key, $val, $m, @vars) = @_;
401 return if @vars && !grep( matchlex($key, $_), @vars );
402 local %address;
403 my $off = 0; # It reads better this way
404 my $fileno;
405 if (UNIVERSAL::isa($val,'ARRAY')) {
406 print( (' ' x $off) . "$key = (\n" );
407 unwrap($val,3+$off,$m) ;
408 print( (' ' x $off) . ")\n" );
409 }
410 elsif (UNIVERSAL::isa($val,'HASH')) {
411 print( (' ' x $off) . "$key = (\n" );
412 unwrap($val,3+$off,$m) ;
413 print( (' ' x $off) . ")\n" );
414 }
415 elsif (UNIVERSAL::isa($val,'IO')) {
416 print( (' ' x $off) . "FileHandle($key) => fileno($fileno)\n" );
417 }
418 # No lexical subroutines yet...
419 # elsif (UNIVERSAL::isa($val,'CODE')) {
420 # dumpsub($off, $$val);
421 # }
422 else {
423 print( (' ' x $off) . &unctrl($key), " = " );
424 DumpElem $$val, 3+$off, $m;
425 }
426}
427
83ee9e09 428sub CvGV_name_or_bust {
429 my $in = shift;
430 return if $skipCvGV; # Backdoor to avoid problems if XS broken...
431 $in = \&$in; # Hard reference...
432 eval {require Devel::Peek; 1} or return;
433 my $gv = Devel::Peek::CvGV($in) or return;
434 *$gv{PACKAGE} . '::' . *$gv{NAME};
435}
436
54d04a52 437sub dumpsub {
438 my ($off,$sub) = @_;
83ee9e09 439 my $ini = $sub;
440 my $s;
54d04a52 441 $sub = $1 if $sub =~ /^\{\*(.*)\}$/;
83ee9e09 442 my $subref = defined $1 ? \&$sub : \&$ini;
443 my $place = $DB::sub{$sub} || (($s = $subs{"$subref"}) && $DB::sub{$s})
444 || (($s = CvGV_name_or_bust($subref)) && $DB::sub{$s})
445 || ($subdump && ($s = findsubs("$subref")) && $DB::sub{$s});
54d04a52 446 $place = '???' unless defined $place;
83ee9e09 447 $s = $sub unless defined $s;
448 print( (' ' x $off) . "&$s in $place\n" );
54d04a52 449}
450
451sub findsubs {
475342a6 452 return undef unless %DB::sub;
54d04a52 453 my ($addr, $name, $loc);
454 while (($name, $loc) = each %DB::sub) {
455 $addr = \&$name;
456 $subs{"$addr"} = $name;
457 }
458 $subdump = 0;
459 $subs{ shift() };
460}
461
d338d6fe 462sub main::dumpvar {
3a4b996c 463 my ($package,$m,@vars) = @_;
b2391ea8 464 local(%address,$key,$val,$^W);
d338d6fe 465 $package .= "::" unless $package =~ /::$/;
466 *stab = *{"main::"};
467 while ($package =~ /(\w+?::)/g){
468 *stab = $ {stab}{$1};
469 }
470 local $TotalStrings = 0;
471 local $Strings = 0;
472 local $CompleteTotal = 0;
473 while (($key,$val) = each(%stab)) {
474 return if $DB::signal;
475 next if @vars && !grep( matchvar($key, $_), @vars );
476 if ($usageOnly) {
4c82ae22 477 globUsage(\$val, $key)
478 if ($package ne 'dumpvar' or $key ne 'stab')
479 and ref(\$val) eq 'GLOB';
d338d6fe 480 } else {
3a4b996c 481 dumpglob(0,$key, $val, 0, $m);
d338d6fe 482 }
483 }
484 if ($usageOnly) {
485 print "String space: $TotalStrings bytes in $Strings strings.\n";
486 $CompleteTotal += $TotalStrings;
487 print "Grand total = $CompleteTotal bytes (1 level deep) + overhead.\n";
488 }
489}
490
491sub scalarUsage {
492 my $size = length($_[0]);
493 $TotalStrings += $size;
494 $Strings++;
495 $size;
496}
497
498sub arrayUsage { # array ref, name
499 my $size = 0;
500 map {$size += scalarUsage($_)} @{$_[0]};
501 my $len = @{$_[0]};
502 print "\@$_[1] = $len item", ($len > 1 ? "s" : ""),
503 " (data: $size bytes)\n"
504 if defined $_[1];
505 $CompleteTotal += $size;
506 $size;
507}
508
509sub hashUsage { # hash ref, name
510 my @keys = keys %{$_[0]};
511 my @values = values %{$_[0]};
512 my $keys = arrayUsage \@keys;
513 my $values = arrayUsage \@values;
514 my $len = @keys;
515 my $total = $keys + $values;
516 print "\%$_[1] = $len item", ($len > 1 ? "s" : ""),
517 " (keys: $keys; values: $values; total: $total bytes)\n"
518 if defined $_[1];
519 $total;
520}
521
522sub globUsage { # glob ref, name
523 local *name = *{$_[0]};
524 $total = 0;
525 $total += scalarUsage $name if defined $name;
475342a6 526 $total += arrayUsage \@name, $_[1] if @name;
527 $total += hashUsage \%name, $_[1] if %name and $_[1] ne "main::"
d338d6fe 528 and $_[1] ne "DB::"; #and !($package eq "dumpvar" and $key eq "stab"));
529 $total;
530}
531
532sub packageUsage {
533 my ($package,@vars) = @_;
534 $package .= "::" unless $package =~ /::$/;
535 local *stab = *{"main::"};
536 while ($package =~ /(\w+?::)/g){
537 *stab = $ {stab}{$1};
538 }
539 local $TotalStrings = 0;
540 local $CompleteTotal = 0;
541 my ($key,$val);
542 while (($key,$val) = each(%stab)) {
543 next if @vars && !grep($key eq $_,@vars);
544 globUsage \$val, $key unless $package eq 'dumpvar' and $key eq 'stab';
545 }
546 print "String space: $TotalStrings.\n";
547 $CompleteTotal += $TotalStrings;
548 print "\nGrand total = $CompleteTotal bytes\n";
549}
550
5511;
552