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