<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">use 5.005_64;			# for (defined ref) and $#$v and our
package Dumpvalue;
use strict;
our(%address, $stab, @stab, %stab, %subs);

# translate control chars to ^X - Randal Schwartz
# Modifications to print types by Peter Gordon v1.0

# Ilya Zakharevich -- patches after 5.001 (and some before ;-)

# Won't dump symbol tables and contents of debugged files by default

# (IZ) changes for objectification:
#   c) quote() renamed to method set_quote();
#   d) unctrlSet() renamed to method set_unctrl();
#   f) Compiles with `use strict', but in two places no strict refs is needed:
#      maybe more problems are waiting...

my %defaults = (
		globPrint	      =&gt; 0,
		printUndef	      =&gt; 1,
		tick		      =&gt; "auto",
		unctrl		      =&gt; 'quote',
		subdump		      =&gt; 1,
		dumpReused	      =&gt; 0,
		bareStringify	      =&gt; 1,
		hashDepth	      =&gt; '',
		arrayDepth	      =&gt; '',
		dumpDBFiles	      =&gt; '',
		dumpPackages	      =&gt; '',
		quoteHighBit	      =&gt; '',
		usageOnly	      =&gt; '',
		compactDump	      =&gt; '',
		veryCompact	      =&gt; '',
		stopDbSignal	      =&gt; '',
	       );

sub new {
  my $class = shift;
  my %opt = (%defaults, @_);
  bless \%opt, $class;
}

sub set {
  my $self = shift;
  my %opt = @_;
  @$self{keys %opt} = values %opt;
}

sub get {
  my $self = shift;
  wantarray ? @$self{@_} : $$self{pop @_};
}

sub dumpValue {
  my $self = shift;
  die "usage: \$dumper-&gt;dumpValue(value)" unless @_ == 1;
  local %address;
  local $^W=0;
  (print "undef\n"), return unless defined $_[0];
  (print $self-&gt;stringify($_[0]), "\n"), return unless ref $_[0];
  $self-&gt;unwrap($_[0],0);
}

sub dumpValues {
  my $self = shift;
  local %address;
  local $^W=0;
  (print "undef\n"), return unless defined $_[0];
  $self-&gt;unwrap(\@_,0);
}

# This one is good for variable names:

sub unctrl {
  local($_) = @_;

  return \$_ if ref \$_ eq "GLOB";
  s/([\001-\037\177])/'^'.pack('c',ord($1)^64)/eg;
  $_;
}

sub stringify {
  my $self = shift;
  local $_ = shift;
  my $noticks = shift;
  my $tick = $self-&gt;{tick};

  return 'undef' unless defined $_ or not $self-&gt;{printUndef};
  return $_ . "" if ref \$_ eq 'GLOB';
  { no strict 'refs';
    $_ = &amp;{'overload::StrVal'}($_)
      if $self-&gt;{bareStringify} and ref $_
	and %overload:: and defined &amp;{'overload::StrVal'};
  }

  if ($tick eq 'auto') {
    if (/[\000-\011\013-\037\177]/) {
      $tick = '"';
    } else {
      $tick = "'";
    }
  }
  if ($tick eq "'") {
    s/([\'\\])/\\$1/g;
  } elsif ($self-&gt;{unctrl} eq 'unctrl') {
    s/([\"\\])/\\$1/g ;
    s/([\000-\037\177])/'^'.pack('c',ord($1)^64)/eg;
    s/([\200-\377])/'\\0x'.sprintf('%2X',ord($1))/eg
      if $self-&gt;{quoteHighBit};
  } elsif ($self-&gt;{unctrl} eq 'quote') {
    s/([\"\\\$\@])/\\$1/g if $tick eq '"';
    s/\033/\\e/g;
    s/([\000-\037\177])/'\\c'.chr(ord($1)^64)/eg;
  }
  s/([\200-\377])/'\\'.sprintf('%3o',ord($1))/eg if $self-&gt;{quoteHighBit};
  ($noticks || /^\d+(\.\d*)?\Z/)
    ? $_
      : $tick . $_ . $tick;
}

sub DumpElem {
  my ($self, $v) = (shift, shift);
  my $short = $self-&gt;stringify($v, ref $v);
  my $shortmore = '';
  if ($self-&gt;{veryCompact} &amp;&amp; ref $v
      &amp;&amp; (ref $v eq 'ARRAY' and !grep(ref $_, @$v) )) {
    my $depth = $#$v;
    ($shortmore, $depth) = (' ...', $self-&gt;{arrayDepth} - 1)
      if $self-&gt;{arrayDepth} and $depth &gt;= $self-&gt;{arrayDepth};
    my @a = map $self-&gt;stringify($_), @$v[0..$depth];
    print "0..$#{$v}  @a$shortmore\n";
  } elsif ($self-&gt;{veryCompact} &amp;&amp; ref $v
	   &amp;&amp; (ref $v eq 'HASH') and !grep(ref $_, values %$v)) {
    my @a = sort keys %$v;
    my $depth = $#a;
    ($shortmore, $depth) = (' ...', $self-&gt;{hashDepth} - 1)
      if $self-&gt;{hashDepth} and $depth &gt;= $self-&gt;{hashDepth};
    my @b = map {$self-&gt;stringify($_) . " =&gt; " . $self-&gt;stringify($$v{$_})}
      @a[0..$depth];
    local $" = ', ';
    print "@b$shortmore\n";
  } else {
    print "$short\n";
    $self-&gt;unwrap($v,shift);
  }
}

sub unwrap {
  my $self = shift;
  return if $DB::signal and $self-&gt;{stopDbSignal};
  my ($v) = shift ;
  my ($s) = shift ;		# extra no of spaces
  my $sp;
  my (%v,@v,$address,$short,$fileno);

  $sp = " " x $s ;
  $s += 3 ;

  # Check for reused addresses
  if (ref $v) {
    my $val = $v;
    { no strict 'refs';
      $val = &amp;{'overload::StrVal'}($v)
	if %overload:: and defined &amp;{'overload::StrVal'};
    }
    ($address) = $val =~ /(0x[0-9a-f]+)\)$/ ;
    if (!$self-&gt;{dumpReused} &amp;&amp; defined $address) {
      $address{$address}++ ;
      if ( $address{$address} &gt; 1 ) {
	print "${sp}-&gt; REUSED_ADDRESS\n" ;
	return ;
      }
    }
  } elsif (ref \$v eq 'GLOB') {
    $address = "$v" . "";	# To avoid a bug with globs
    $address{$address}++ ;
    if ( $address{$address} &gt; 1 ) {
      print "${sp}*DUMPED_GLOB*\n" ;
      return ;
    }
  }

  if (ref $v eq 'Regexp') {
    my $re = "$v";
    $re =~ s,/,\\/,g;
    print "$sp-&gt; qr/$re/\n";
    return;
  }

  if ( UNIVERSAL::isa($v, 'HASH') ) {
    my @sortKeys = sort keys(%$v) ;
    my $more;
    my $tHashDepth = $#sortKeys ;
    $tHashDepth = $#sortKeys &lt; $self-&gt;{hashDepth}-1 ? $#sortKeys : $self-&gt;{hashDepth}-1
      unless $self-&gt;{hashDepth} eq '' ;
    $more = "....\n" if $tHashDepth &lt; $#sortKeys ;
    my $shortmore = "";
    $shortmore = ", ..." if $tHashDepth &lt; $#sortKeys ;
    $#sortKeys = $tHashDepth ;
    if ($self-&gt;{compactDump} &amp;&amp; !grep(ref $_, values %{$v})) {
      $short = $sp;
      my @keys;
      for (@sortKeys) {
	push @keys, $self-&gt;stringify($_) . " =&gt; " . $self-&gt;stringify($v-&gt;{$_});
      }
      $short .= join ', ', @keys;
      $short .= $shortmore;
      (print "$short\n"), return if length $short &lt;= $self-&gt;{compactDump};
    }
    for my $key (@sortKeys) {
      return if $DB::signal and $self-&gt;{stopDbSignal};
      my $value = $ {$v}{$key} ;
      print $sp, $self-&gt;stringify($key), " =&gt; ";
      $self-&gt;DumpElem($value, $s);
    }
    print "$sp  empty hash\n" unless @sortKeys;
    print "$sp$more" if defined $more ;
  } elsif ( UNIVERSAL::isa($v, 'ARRAY') ) {
    my $tArrayDepth = $#{$v} ;
    my $more ;
    $tArrayDepth = $#$v &lt; $self-&gt;{arrayDepth}-1 ? $#$v : $self-&gt;{arrayDepth}-1
      unless  $self-&gt;{arrayDepth} eq '' ;
    $more = "....\n" if $tArrayDepth &lt; $#{$v} ;
    my $shortmore = "";
    $shortmore = " ..." if $tArrayDepth &lt; $#{$v} ;
    if ($self-&gt;{compactDump} &amp;&amp; !grep(ref $_, @{$v})) {
      if ($#$v &gt;= 0) {
	$short = $sp . "0..$#{$v}  " .
	  join(" ", 
	       map {exists $v-&gt;[$_] ? $self-&gt;stringify($v-&gt;[$_]) : "empty"} ($[..$tArrayDepth)
	      ) . "$shortmore";
      } else {
	$short = $sp . "empty array";
      }
      (print "$short\n"), return if length $short &lt;= $self-&gt;{compactDump};
    }
    for my $num ($[ .. $tArrayDepth) {
      return if $DB::signal and $self-&gt;{stopDbSignal};
      print "$sp$num  ";
      if (exists $v-&gt;[$num]) {
        $self-&gt;DumpElem($v-&gt;[$num], $s);
      } else {
	print "empty slot\n";
      }
    }
    print "$sp  empty array\n" unless @$v;
    print "$sp$more" if defined $more ;
  } elsif (  UNIVERSAL::isa($v, 'SCALAR') or ref $v eq 'REF' ) {
    print "$sp-&gt; ";
    $self-&gt;DumpElem($$v, $s);
  } elsif ( UNIVERSAL::isa($v, 'CODE') ) {
    print "$sp-&gt; ";
    $self-&gt;dumpsub(0, $v);
  } elsif ( UNIVERSAL::isa($v, 'GLOB') ) {
    print "$sp-&gt; ",$self-&gt;stringify($$v,1),"\n";
    if ($self-&gt;{globPrint}) {
      $s += 3;
      $self-&gt;dumpglob('', $s, "{$$v}", $$v, 1);
    } elsif (defined ($fileno = fileno($v))) {
      print( (' ' x ($s+3)) .  "FileHandle({$$v}) =&gt; fileno($fileno)\n" );
    }
  } elsif (ref \$v eq 'GLOB') {
    if ($self-&gt;{globPrint}) {
      $self-&gt;dumpglob('', $s, "{$v}", $v, 1);
    } elsif (defined ($fileno = fileno(\$v))) {
      print( (' ' x $s) .  "FileHandle({$v}) =&gt; fileno($fileno)\n" );
    }
  }
}

sub matchvar {
  $_[0] eq $_[1] or
    ($_[1] =~ /^([!~])(.)([\x00-\xff]*)/) and
      ($1 eq '!') ^ (eval {($_[2] . "::" . $_[0]) =~ /$2$3/});
}

sub compactDump {
  my $self = shift;
  $self-&gt;{compactDump} = shift if @_;
  $self-&gt;{compactDump} = 6*80-1 
    if $self-&gt;{compactDump} and $self-&gt;{compactDump} &lt; 2;
  $self-&gt;{compactDump};
}

sub veryCompact {
  my $self = shift;
  $self-&gt;{veryCompact} = shift if @_;
  $self-&gt;compactDump(1) if !$self-&gt;{compactDump} and $self-&gt;{veryCompact};
  $self-&gt;{veryCompact};
}

sub set_unctrl {
  my $self = shift;
  if (@_) {
    my $in = shift;
    if ($in eq 'unctrl' or $in eq 'quote') {
      $self-&gt;{unctrl} = $in;
    } else {
      print "Unknown value for `unctrl'.\n";
    }
  }
  $self-&gt;{unctrl};
}

sub set_quote {
  my $self = shift;
  if (@_ and $_[0] eq '"') {
    $self-&gt;{tick} = '"';
    $self-&gt;{unctrl} = 'quote';
  } elsif (@_ and $_[0] eq 'auto') {
    $self-&gt;{tick} = 'auto';
    $self-&gt;{unctrl} = 'quote';
  } elsif (@_) {		# Need to set
    $self-&gt;{tick} = "'";
    $self-&gt;{unctrl} = 'unctrl';
  }
  $self-&gt;{tick};
}

sub dumpglob {
  my $self = shift;
  return if $DB::signal and $self-&gt;{stopDbSignal};
  my ($package, $off, $key, $val, $all) = @_;
  local(*stab) = $val;
  my $fileno;
  if (($key !~ /^_&lt;/ or $self-&gt;{dumpDBFiles}) and defined $stab) {
    print( (' ' x $off) . "\$", &amp;unctrl($key), " = " );
    $self-&gt;DumpElem($stab, 3+$off);
  }
  if (($key !~ /^_&lt;/ or $self-&gt;{dumpDBFiles}) and @stab) {
    print( (' ' x $off) . "\@$key = (\n" );
    $self-&gt;unwrap(\@stab,3+$off) ;
    print( (' ' x $off) .  ")\n" );
  }
  if ($key ne "main::" &amp;&amp; $key ne "DB::" &amp;&amp; %stab
      &amp;&amp; ($self-&gt;{dumpPackages} or $key !~ /::$/)
      &amp;&amp; ($key !~ /^_&lt;/ or $self-&gt;{dumpDBFiles})
      &amp;&amp; !($package eq "Dumpvalue" and $key eq "stab")) {
    print( (' ' x $off) . "\%$key = (\n" );
    $self-&gt;unwrap(\%stab,3+$off) ;
    print( (' ' x $off) .  ")\n" );
  }
  if (defined ($fileno = fileno(*stab))) {
    print( (' ' x $off) .  "FileHandle($key) =&gt; fileno($fileno)\n" );
  }
  if ($all) {
    if (defined &amp;stab) {
      $self-&gt;dumpsub($off, $key);
    }
  }
}

sub CvGV_name {
  my $self = shift;
  my $in = shift;
  return if $self-&gt;{skipCvGV};	# Backdoor to avoid problems if XS broken...
  $in = \&amp;$in;			# Hard reference...
  eval {require Devel::Peek; 1} or return;
  my $gv = Devel::Peek::CvGV($in) or return;
  *$gv{PACKAGE} . '::' . *$gv{NAME};
}

sub dumpsub {
  my $self = shift;
  my ($off,$sub) = @_;
  my $ini = $sub;
  my $s;
  $sub = $1 if $sub =~ /^\{\*(.*)\}$/;
  my $subref = defined $1 ? \&amp;$sub : \&amp;$ini;
  my $place = $DB::sub{$sub} || (($s = $subs{"$subref"}) &amp;&amp; $DB::sub{$s})
    || (($s = $self-&gt;CvGV_name($subref)) &amp;&amp; $DB::sub{$s})
    || ($self-&gt;{subdump} &amp;&amp; ($s = $self-&gt;findsubs("$subref"))
	&amp;&amp; $DB::sub{$s});
  $s = $sub unless defined $s;
  $place = '???' unless defined $place;
  print( (' ' x $off) .  "&amp;$s in $place\n" );
}

sub findsubs {
  my $self = shift;
  return undef unless %DB::sub;
  my ($addr, $name, $loc);
  while (($name, $loc) = each %DB::sub) {
    $addr = \&amp;$name;
    $subs{"$addr"} = $name;
  }
  $self-&gt;{subdump} = 0;
  $subs{ shift() };
}

sub dumpvars {
  my $self = shift;
  my ($package,@vars) = @_;
  local(%address,$^W);
  my ($key,$val);
  $package .= "::" unless $package =~ /::$/;
  *stab = *main::;

  while ($package =~ /(\w+?::)/g) {
    *stab = $ {stab}{$1};
  }
  $self-&gt;{TotalStrings} = 0;
  $self-&gt;{Strings} = 0;
  $self-&gt;{CompleteTotal} = 0;
  while (($key,$val) = each(%stab)) {
    return if $DB::signal and $self-&gt;{stopDbSignal};
    next if @vars &amp;&amp; !grep( matchvar($key, $_), @vars );
    if ($self-&gt;{usageOnly}) {
      $self-&gt;globUsage(\$val, $key)
	if ($package ne 'Dumpvalue' or $key ne 'stab')
	   and ref(\$val) eq 'GLOB';
    } else {
      $self-&gt;dumpglob($package, 0,$key, $val);
    }
  }
  if ($self-&gt;{usageOnly}) {
    print &lt;&lt;EOP;
String space: $self-&gt;{TotalStrings} bytes in $self-&gt;{Strings} strings.
EOP
    $self-&gt;{CompleteTotal} += $self-&gt;{TotalStrings};
    print &lt;&lt;EOP;
Grand total = $self-&gt;{CompleteTotal} bytes (1 level deep) + overhead.
EOP
  }
}

sub scalarUsage {
  my $self = shift;
  my $size = length($_[0]);
  $self-&gt;{TotalStrings} += $size;
  $self-&gt;{Strings}++;
  $size;
}

sub arrayUsage {		# array ref, name
  my $self = shift;
  my $size = 0;
  map {$size += $self-&gt;scalarUsage($_)} @{$_[0]};
  my $len = @{$_[0]};
  print "\@$_[1] = $len item", ($len &gt; 1 ? "s" : ""), " (data: $size bytes)\n"
      if defined $_[1];
  $self-&gt;{CompleteTotal} +=  $size;
  $size;
}

sub hashUsage {			# hash ref, name
  my $self = shift;
  my @keys = keys %{$_[0]};
  my @values = values %{$_[0]};
  my $keys = $self-&gt;arrayUsage(\@keys);
  my $values = $self-&gt;arrayUsage(\@values);
  my $len = @keys;
  my $total = $keys + $values;
  print "\%$_[1] = $len item", ($len &gt; 1 ? "s" : ""),
    " (keys: $keys; values: $values; total: $total bytes)\n"
      if defined $_[1];
  $total;
}

sub globUsage {			# glob ref, name
  my $self = shift;
  local *stab = *{$_[0]};
  my $total = 0;
  $total += $self-&gt;scalarUsage($stab) if defined $stab;
  $total += $self-&gt;arrayUsage(\@stab, $_[1]) if @stab;
  $total += $self-&gt;hashUsage(\%stab, $_[1]) 
    if %stab and $_[1] ne "main::" and $_[1] ne "DB::";	
  #and !($package eq "Dumpvalue" and $key eq "stab"));
  $total;
}

1;

=head1 NAME

Dumpvalue - provides screen dump of Perl data.

=head1 SYNOPSIS

  use Dumpvalue;
  my $dumper = new Dumpvalue;
  $dumper-&gt;set(globPrint =&gt; 1);
  $dumper-&gt;dumpValue(\*::);
  $dumper-&gt;dumpvars('main');

=head1 DESCRIPTION

=head2 Creation

A new dumper is created by a call

  $d = new Dumpvalue(option1 =&gt; value1, option2 =&gt; value2)

Recognized options:

=over

=item C&lt;arrayDepth&gt;, C&lt;hashDepth&gt;

Print only first N elements of arrays and hashes.  If false, prints all the
elements.

=item C&lt;compactDump&gt;, C&lt;veryCompact&gt;

Change style of array and hash dump.  If true, short array
may be printed on one line.

=item C&lt;globPrint&gt;

Whether to print contents of globs.

=item C&lt;DumpDBFiles&gt;

Dump arrays holding contents of debugged files.

=item C&lt;DumpPackages&gt;

Dump symbol tables of packages.

=item C&lt;DumpReused&gt;

Dump contents of "reused" addresses.

=item C&lt;tick&gt;, C&lt;HighBit&gt;, C&lt;printUndef&gt;

Change style of string dump.  Default value of C&lt;tick&gt; is C&lt;auto&gt;, one
can enable either double-quotish dump, or single-quotish by setting it
to C&lt;"&gt; or C&lt;'&gt;.  By default, characters with high bit set are printed
I&lt;as is&gt;.

=item C&lt;UsageOnly&gt;

I&lt;very&gt; rudimentally per-package memory usage dump.  If set,
C&lt;dumpvars&gt; calculates total size of strings in variables in the package.

=item unctrl

Changes the style of printout of strings.  Possible values are
C&lt;unctrl&gt; and C&lt;quote&gt;.

=item subdump

Whether to try to find the subroutine name given the reference.

=item bareStringify

Whether to write the non-overloaded form of the stringify-overloaded objects.

=item quoteHighBit

Whether to print chars with high bit set in binary or "as is".

=item stopDbSignal

Whether to abort printing if debugger signal flag is raised.

=back

Later in the life of the object the methods may be queries with get()
method and set() method (which accept multiple arguments).

=head2 Methods

=over

=item dumpValue

  $dumper-&gt;dumpValue($value);
  $dumper-&gt;dumpValue([$value1, $value2]);

=item dumpValues

  $dumper-&gt;dumpValues($value1, $value2);

=item dumpvars

  $dumper-&gt;dumpvars('my_package');
  $dumper-&gt;dumpvars('my_package', 'foo', '~bar$', '!......');

The optional arguments are considered as literal strings unless they
start with C&lt;~&gt; or C&lt;!&gt;, in which case they are interpreted as regular
expressions (possibly negated).

The second example prints entries with names C&lt;foo&gt;, and also entries
with names which ends on C&lt;bar&gt;, or are shorter than 5 chars.

=item set_quote

  $d-&gt;set_quote('"');

Sets C&lt;tick&gt; and C&lt;unctrl&gt; options to suitable values for printout with the
given quote char.  Possible values are C&lt;auto&gt;, C&lt;'&gt; and C&lt;"&gt;.

=item set_unctrl

  $d-&gt;set_unctrl('"');

Sets C&lt;unctrl&gt; option with checking for an invalid argument.
Possible values are C&lt;unctrl&gt; and C&lt;quote&gt;.

=item compactDump

  $d-&gt;compactDump(1);

Sets C&lt;compactDump&gt; option.  If the value is 1, sets to a reasonable
big number.

=item veryCompact

  $d-&gt;veryCompact(1);

Sets C&lt;compactDump&gt; and C&lt;veryCompact&gt; options simultaneously.

=item set

  $d-&gt;set(option1 =&gt; value1, option2 =&gt; value2);

=item get

  @values = $d-&gt;get('option1', 'option2');

=back

=cut

</pre></body></html>