xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/Dumpvalue.pm (revision 0:68f95e015346)
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