perl5db.pl revision 7c478bd95313f5f23a4c958a745db2134aa03244
package DB;
# Debugger for Perl 5.00x; perl5db.pl patch level:
$VERSION = 1.07;
$header = "perl5db.pl version $VERSION";
#
# This file is automatically included if you do perl -d.
# It's probably not useful to include this yourself.
#
# Perl supplies the values for %sub. It effectively inserts
# a &DB'DB(); in front of every place that can have a
# breakpoint. Instead of a subroutine call it calls &DB::sub with
# $DB::sub being the called subroutine. It also inserts a BEGIN
# {require 'perl5db.pl'} before the first line.
#
# After each `require'd file is compiled, but before it is executed, a
# call to DB::postponed($main::{'_<'.$filename}) is emulated. Here the
# $filename is the expanded name of the `require'd file (as found as
# value of %INC).
#
# Additional services from Perl interpreter:
#
# if caller() is called from the package DB, it provides some
# additional data.
#
# The array @{$main::{'_<'.$filename}} is the line-by-line contents of
# $filename.
#
# The hash %{'_<'.$filename} contains breakpoints and action (it is
# keyed by line number), and individual entries are settable (as
# interpreter, though the values used by perl5db.pl have the form
# "$break_condition\0$action". Values are magical in numeric context.
#
# The scalar ${'_<'.$filename} contains $filename.
#
# Note that no subroutine call is possible until &DB::sub is defined
# (for subroutines defined outside of the package DB). In fact the same is
# true if $deep is not defined.
#
# $Log: perldb.pl,v $
#
# At start reads $rcfile that may set important options. This file
# may define a subroutine &afterinit that will be executed after the
# debugger is initialized.
#
# After $rcfile is read reads environment variable PERLDB_OPTS and parses
# it as a rest of `O ...' line in debugger prompt.
#
# The options that can be specified only at startup:
# [To set in $rcfile, call &parse_options("optionName=new_value").]
#
# TTY - the TTY to use for debugging i/o.
#
# noTTY - if set, goes in NonStop mode. On interrupt if TTY is not set
# Term::Rendezvous. Current variant is to have the name of TTY in this
# file.
#
# ReadLine - If false, dummy ReadLine is used, so you can debug
# ReadLine applications.
#
# NonStop - if true, no i/o is performed until interrupt.
#
# LineInfo - file or pipe to print line number info to. If it is a
# pipe, a short "emacs like" message is used.
#
# RemotePort - host:port to connect to on remote host for remote debugging.
#
# Example $rcfile: (delete leading hashes!)
#
# &parse_options("NonStop=1 LineInfo=db.out");
# sub afterinit { $trace = 1; }
#
# The script will run without human intervention, putting trace
# information into db.out. (If you interrupt it, you would better
# reset LineInfo to something "interactive"!)
#
##################################################################
# Enhanced by ilya@math.ohio-state.edu (Ilya Zakharevich)
# Latest version available: ftp://ftp.math.ohio-state.edu/pub/users/ilya/perl
# modified Perl debugger, to be run from Emacs in perldb-mode
# Ray Lischner (uunet!mntgfx!lisch) as of 5 Nov 1990
# Johan Vromans -- upgrade to 4.0 pl 10
# Ilya Zakharevich -- patches after 5.001 (and some before ;-)
# Changelog:
# A lot of things changed after 0.94. First of all, core now informs
# debugger about entry into XSUBs, overloaded operators, tied operations,
# BEGIN and END. Handy with `O f=2'.
# This can make debugger a little bit too verbose, please be patient
# and report your problems promptly.
# Now the option frame has 3 values: 0,1,2.
# Note that if DESTROY returns a reference to the object (or object),
# the deletion of data may be postponed until the next function call,
# due to the need to examine the return value.
# Changes: 0.95: `v' command shows versions.
# Changes: 0.96: `v' command shows version of readline.
# primitive completion works (dynamic variables, subs for `b' and `l',
# options). Can `p %var'
# Better help (`h <' now works). New commands <<, >>, {, {{.
# {dump|print}_trace() coded (to be able to do it from <<cmd).
# `c sub' documented.
# At last enough magic combined to stop after the end of debuggee.
# !! should work now (thanks to Emacs bracket matching an extra
# `]' in a regexp is caught).
# `L', `D' and `A' span files now (as documented).
# Breakpoints in `require'd code are possible (used in `R').
# Some additional words on internal work of debugger.
# `b load filename' implemented.
# `b postpone subr' implemented.
# now only `q' exits debugger (overwriteable on $inhibit_exit).
# When restarting debugger breakpoints/actions persist.
# Buglet: When restarting debugger only one breakpoint/action per
# autoloaded function persists.
# Changes: 0.97: NonStop will not stop in at_exit().
# Option AutoTrace implemented.
# Trace printed differently if frames are printed too.
# new `inhibitExit' option.
# printing of a very long statement interruptible.
# Changes: 0.98: New command `m' for printing possible methods
# 'l -' is a synonim for `-'.
# Cosmetic bugs in printing stack trace.
# `frame' & 8 to print "expanded args" in stack trace.
# new `maxTraceLen' option.
# frame & 4 and frame & 8 granted.
# new command `m'
# nonstoppable lines do not have `:' near the line number.
# `b compile subname' implemented.
# Will not use $` any more.
# `-' behaves sane now.
# Changes: 0.99: Completion for `f', `m'.
# `m' will remove duplicate names instead of duplicate functions.
# `b load' strips trailing whitespace.
# completion ignores leading `|'; takes into account current package
# when completing a subroutine name (same for `l').
# Changes: 1.07: Many fixed by tchrist 13-March-2000
# BUG FIXES:
# + Added bare mimimal security checks on perldb rc files, plus
# comments on what else is needed.
# + Fixed the ornaments that made "|h" completely unusable.
# They are not used in print_help if they will hurt. Strip pod
# if we're paging to less.
# + Fixed mis-formatting of help messages caused by ornaments
# to restore Larry's original formatting.
# + Fixed many other formatting errors. The code is still suboptimal,
# and needs a lot of work at restructuing. It's also misindented
# in many places.
# + Fixed bug where trying to look at an option like your pager
# shows "1".
# + Fixed some $? processing. Note: if you use csh or tcsh, you will
# lose. You should consider shell escapes not using their shell,
# or else not caring about detailed status. This should really be
# unified into one place, too.
# + Fixed bug where invisible trailing whitespace on commands hoses you,
# tricking Perl into thinking you wern't calling a debugger command!
# + Fixed bug where leading whitespace on commands hoses you. (One
# suggests a leading semicolon or any other irrelevant non-whitespace
# to indicate literal Perl code.)
# + Fixed bugs that ate warnings due to wrong selected handle.
# + Fixed a precedence bug on signal stuff.
# + Fixed some unseemly wording.
# + Fixed bug in help command trying to call perl method code.
# + Fixed to call dumpvar from exception handler. SIGPIPE killed us.
# ENHANCEMENTS:
# + Added some comments. This code is still nasty spaghetti.
# very easy to do if you just typed a bare >, <, or {. (A command
# without an argument should *never* be a destructive action; this
# API is fundamentally screwed up; likewise option setting, which
# is equally buggered.)
# + Added command stack dump on argument of "?" for >, <, or {.
# + Added a semi-built-in doc viewer command that calls man with the
# proper %Config::Config path (and thus gets caching, man -k, etc),
# or else perldoc on obstreperous platforms.
# + Added to and rearranged the help information.
# + Detected apparent misuse of { ... } to declare a block; this used
# to work but now is a command, and mysteriously gave no complaint.
####################################################################
# Needed for the statement after exec():
local($^W) = 0; # Switch run-time warnings off during init.
warn ( # Do not ;-)
@ARGS,
$panic,
) if 0;
# Command-line + PERLLIB:
# $prevwarn = $prevdie = $prevbus = $prevsegv = ''; # Does not help?!
# (local $^W cannot help - other packages!).
%optionVars = (
inhibit_exit => \$inhibit_exit,
maxTraceLen => \$maxtrace,
ImmediateStop => \$ImmediateStop,
RemotePort => \$remoteport,
);
%optionAction = (
recallCommand => \&recallCommand,
signalLevel => \&signalLevel,
RemotePort => \&RemotePort,
);
%optionRequire = (
compactDump => 'dumpvar.pl',
veryCompact => 'dumpvar.pl',
quote => 'dumpvar.pl',
);
# These guys may be defined in $ENV{PERL5DB} :
&pager(
: ($^O eq 'os2'
? 'cmd /c more'
: 'more'))) unless defined $pager;
setman();
$rcfile=".perldb";
} else {
$rcfile="perldb.ini";
}
# This isn't really safe, because there's a race
# between checking and opening. The solution is to
# open and fstat the handle, but then you have to read and
# eval the contents. But then the silly thing gets
# your lexical scope, which is unfortunately at best.
sub safe_do {
my $file = shift;
# Just exactly what part of the word "CORE::" don't you understand?
unless (is_safe_file($file)) {
return;
}
do $file;
CORE::warn("perldb: couldn't parse $file: $@") if $@;
}
# Verifies that owner is either real user or superuser and that no
# one but owner may write to it. This function is of limited use
# when called on a path instead of upon a handle, because there are
# no guarantees that filename (by dirent) whose file (by ino) is
# eventually accessed is the same as the one tested.
# Assumes that the file's existence is not in doubt.
sub is_safe_file {
my $path = shift;
stat($path) || return; # mysteriously vaporized
return 1;
}
if (-f $rcfile) {
safe_do("./$rcfile");
}
safe_do("$ENV{HOME}/$rcfile");
}
safe_do("$ENV{LOGDIR}/$rcfile");
}
if (defined $ENV{PERLDB_OPTS}) {
}
# Here begin the unreadable code. It needs fixing.
if (exists $ENV{PERLDB_RESTART}) {
delete $ENV{PERLDB_RESTART};
# $restart = 1;
for (0 .. $#had_breakpoints) {
}
$val =~ s/[\\\']/\\$1/g;
parse_options("$opt'$val'");
}
}
if ($notty) {
$runnonstop = 1;
} else {
# Is Perl being run from a slave editor or graphical debugger?
#require Term::ReadLine;
if ($^O eq 'cygwin') {
undef $console;
} elsif ($^O eq 'dos' or -e "con" or $^O eq 'MSWin32') {
$console = "con";
} elsif ($^O eq 'MacOS') {
} else {
$console = "Dev:Console";
}
} else {
$console = "sys\$command";
}
$console = undef;
}
# Around a bug:
$console = undef;
}
if ($^O eq 'epoc') {
$console = undef;
}
if (defined $remoteport) {
PeerAddr => $remoteport,
Proto => 'tcp',
);
if (!$OUT) { die "Unable to connect to remote host: $remoteport\n"; }
}
else {
if (defined $console) {
} else {
open(IN,"<&STDIN");
}
# so open("|more") can read from STDOUT and so we don't dingle stdin
}
select($OUT);
$| = 1; # for DB::OUT
select(STDOUT);
$| = 1; # for real STDOUT
unless ($runnonstop) {
print $OUT "\nLoading DB routines from $header\n";
print $OUT ("Editor support ",
".\n");
print $OUT "\nEnter h or `h h' for help, or `$doccmd perldebug' for more help.\n\n";
}
}
for (@args) {
s/\'/\\\'/g;
s/(.*)/'$1'/ unless /^-?[\d.]+$/;
}
if (defined &afterinit) { # May be defined in $rcfile
&afterinit();
}
$I_m_init = 1;
############################################################ Subroutines
sub DB {
# _After_ the perl program is compiled, $single is set to 1:
if ($single and not $second_time++) {
if ($runnonstop) { # Disable until signal
for ($i=0; $i <= $stack_depth; ) {
$stack[$i++] &= ~1;
}
$single = 0;
# return; # Would not print trace!
} elsif ($ImmediateStop) {
$ImmediateStop = 0;
$signal = 1;
}
}
&save;
$filename_ini = $filename;
$usercontext = '($@, $!, $^E, $,, $/, $\, $^W) = @saved;' .
"package $package;"; # this won't let them modify, alas
$max = $#dbline;
if ($stop eq '1') {
$signal |= 1;
} elsif ($stop) {
}
}
my $was_signal = $signal;
if ($trace & 2) {
for (my $n = 0; $n <= $#to_watch; $n++) {
local $onetimeDump; # Do not output results
$signal = 1;
}
}
}
}
$was_signal = $signal;
$signal = 0;
if ($slave_editor) {
$position = "\032\032$filename:$line:0\n";
} elsif ($package eq 'DB::fake') {
print_help(<<EOP);
$package = 'main';
$usercontext = '($@, $!, $^E, $,, $/, $\, $^W) = @saved;' .
"package $package;"; # this won't let them modify, alas
} else {
$sub =~ s/\'/::/;
$prefix .= "$sub($filename:";
if (length($prefix) > 30) {
$position = "$prefix$line):\n$line:\t$dbline[$line]$after";
$prefix = "";
$infix = ":\t";
} else {
$infix = "):\t";
$position = "$prefix$line$infix$dbline[$line]$after";
}
if ($frame) {
} else {
}
last if $dbline[$i] =~ /^\s*[\;\}\#\n]/;
last if $signal;
$incr_pos = "$prefix$i$infix$dbline[$i]$after";
if ($frame) {
} else {
}
}
}
}
if ($single || $was_signal) {
&eval;
}
if $single & 4;
CMD:
($#hist+1) . ('>' x $level) .
" ")))
{
$single = 0;
$signal = 0;
$cmd =~ s/\\$/\n/ && do {
redo CMD;
};
PIPE: {
$cmd =~ s/^\s+//s; # trim annoying leading whitespace
$cmd =~ s/\s+$//s; # trim annoying trailing whitespace
($i) = split(/\s+/,$cmd);
if ($alias{$i}) {
# squelch the sigmangler
eval "\$cmd =~ $alias{$i}";
if ($@) {
print $OUT "Couldn't evaluate `$i' alias: $@";
next CMD;
}
}
$cmd =~ /^h$/ && do {
print_help($help);
next CMD; };
$cmd =~ /^h\s+h$/ && do {
next CMD; };
# support long commands; otherwise bogus errors
# happen when you ask for h on <CR> for example
$cmd =~ /^h\s+(\S.*)$/ && do {
# XXX: finds CR but not <CR>
print_help($1);
}
} else {
print_help("B<$asked> is not a debugger command.\n");
}
next CMD; };
$cmd =~ /^t$/ && do {
$trace ^= 1;
print $OUT "Trace = " .
next CMD; };
$cmd =~ /^S(\s+(!)?(.+))?$/ && do {
}
}
next CMD; };
$cmd =~ /^v$/ && do {
list_versions(); next CMD};
$cmd =~ /^V$/ && do {
$cmd = "V $package"; };
$cmd =~ /^V\b\s*(\S+)\s*(.*)/ && do {
$packname = $1;
local $frame = 0;
local $doret = -2;
# must detect sigpipe failures
if ($@) {
}
} else {
print $OUT "dumpvar.pl not available.\n";
}
select ($savout);
next CMD; };
$cmd =~ s/^x\b/ / && do { # So that will be evaled
$onetimeDump = 'dump'; };
$cmd =~ s/^m\s+([\w:]+)\s*$/ / && do {
$cmd =~ s/^m\b/ / && do { # So this will be evaled
$onetimeDump = 'methods'; };
$cmd =~ /^f\b\s*(.*)/ && do {
$file = $1;
$file =~ s/\s+$//;
if (!$file) {
print $OUT "The old f command is now the r command.\n";
print $OUT "The new f command switches filenames.\n";
next CMD;
}
if (($try) = grep(m#^_<.*$file#, keys %main::)) {{
print $OUT "Choosing $try matching `$file':\n";
}}
}
print $OUT "No file matching `$file' is loaded.\n";
next CMD;
$max = $#dbline;
$start = 1;
$cmd = "l";
} else {
print $OUT "Already in $file.\n";
next CMD;
}
};
$cmd =~ s/^l\s+-\s*$/-/;
$evalarg = $2;
my ($s) = &eval;
$s = CvGV_name($s);
print($OUT "Interpreted as: $1 $s\n");
$cmd = "$1 $s";
};
$cmd =~ /^l\b\s*([\':A-Za-z_][\':\w]*(\[.*\])?)/s && do {
$subname = $1;
$subname =~ s/\'/::/;
unless $subname =~ /::/;
print $OUT "Switching to file '$file'.\n"
unless $slave_editor;
$max = $#dbline;
}
if ($subrange) {
$subrange =~ s/-.*/+/;
}
$cmd = "l $subrange";
} else {
print $OUT "Subroutine $subname not found.\n";
next CMD;
} };
$cmd =~ /^\.$/ && do {
$filename = $filename_ini;
$max = $#dbline;
next CMD };
$cmd =~ /^w\b\s*(\d*)$/ && do {
#print $OUT 'l ' . $start . '-' . ($start + $incr);
$cmd =~ /^-$/ && do {
$cmd =~ /^l$/ && do {
$cmd =~ /^l\b\s*(\d*)\+(\d*)$/ && do {
$incr = $2;
$cmd =~ /^l\b\s*((-?[\d\$\.]+)([-,]([\d\$\.]+))?)?/ && do {
$i = $2;
$i = $line if $i eq '.';
$i = 1 if $i < 1;
if ($slave_editor) {
print $OUT "\032\032$filename:$i:0\n";
$i = $end;
} else {
for (; $i <= $end; $i++) {
and $filename eq $filename_ini)
? '==>'
$i++, last if $signal;
}
}
$start = $i; # remember in case they want more
next CMD; };
$cmd =~ /^D$/ && do {
print $OUT "Deleting all breakpoints...\n";
my $file;
for $file (keys %had_breakpoints) {
my $max = $#dbline;
my $was;
for ($i = 1; $i <= $max ; $i++) {
if (defined $dbline{$i}) {
$dbline{$i} =~ s/^[^\0]+//;
if ($dbline{$i} =~ s/^\0?$//) {
delete $dbline{$i};
}
}
}
delete $had_breakpoints{$file};
}
}
undef %postponed;
undef %postponed_file;
undef %break_on_load;
next CMD; };
$cmd =~ /^L$/ && do {
my $file;
for $file (keys %had_breakpoints) {
my $max = $#dbline;
my $was;
for ($i = 1; $i <= $max; $i++) {
if (defined $dbline{$i}) {
if $stop;
if $action;
last if $signal;
}
}
}
if (%postponed) {
print $OUT "Postponed breakpoints in subroutines:\n";
my $subname;
print $OUT " $subname\t$postponed{$subname}\n";
last if $signal;
}
}
my @have = map { # Combined keys
keys %{$postponed_file{$_}}
} keys %postponed_file;
if (@have) {
print $OUT "Postponed breakpoints in files:\n";
for $file (keys %postponed_file) {
print $OUT " $file:\n";
print $OUT " $line:\n";
if $stop;
if $action;
last if $signal;
}
last if $signal;
}
}
if (%break_on_load) {
print $OUT "Breakpoints on load:\n";
my $file;
for $file (keys %break_on_load) {
print $OUT " $file\n";
last if $signal;
}
}
if ($trace & 2) {
print $OUT "Watch-expressions:\n";
my $expr;
print $OUT " $expr\n";
last if $signal;
}
}
next CMD; };
{
}
print $OUT "Will stop on load of `@{[join '\', `', sort keys %break_on_load]}'.\n";
next CMD; };
$subname =~ s/\'/::/g;
unless $subname =~ /::/;
? "break +0 if $cond" : "compile";
next CMD; };
$cmd =~ /^b\b\s*([':A-Za-z_][':\w]*(?:\[.*\])?)\s*(.*)/ && do {
$subname = $1;
$subname =~ s/\'/::/g;
unless $subname =~ /::/;
# Filename below can contain ':'
$i += 0;
if ($i) {
$max = $#dbline;
} else {
print $OUT "Subroutine $subname not found.\n";
}
next CMD; };
$cmd =~ /^b\b\s*(\d*)\s*(.*)/ && do {
$i = $1 || $line;
if ($dbline[$i] == 0) {
print $OUT "Line $i not breakable.\n";
} else {
}
next CMD; };
$cmd =~ /^d\b\s*(\d*)/ && do {
$i = $1 || $line;
if ($dbline[$i] == 0) {
print $OUT "Line $i not breakable.\n";
} else {
$dbline{$i} =~ s/^[^\0]*//;
}
next CMD; };
$cmd =~ /^A$/ && do {
print $OUT "Deleting all actions...\n";
my $file;
for $file (keys %had_breakpoints) {
my $max = $#dbline;
my $was;
for ($i = 1; $i <= $max ; $i++) {
if (defined $dbline{$i}) {
}
}
delete $had_breakpoints{$file};
}
}
next CMD; };
$cmd =~ /^O\s*$/ && do {
for (@options) {
&dump_option($_);
}
next CMD; };
$cmd =~ /^O\s*(\S.*)/ && do {
parse_options($1);
next CMD; };
$cmd =~ /^\<\<\s*(.*)/ && do { # \<\< for CPerl sake: not HERE
next CMD; };
$cmd =~ /^>>\s*(.*)/ && do {
next CMD; };
$cmd =~ /^<\s*(.*)/ && do {
unless ($1) {
print $OUT "All < actions cleared.\n";
$pre = [];
next CMD;
}
if ($1 eq '?') {
unless (@$pre) {
print $OUT "No pre-prompt Perl actions.\n";
next CMD;
}
print $OUT "Perl commands run before each prompt:\n";
print $OUT "\t< -- $action\n";
}
next CMD;
}
next CMD; };
$cmd =~ /^>\s*(.*)/ && do {
unless ($1) {
print $OUT "All > actions cleared.\n";
$post = [];
next CMD;
}
if ($1 eq '?') {
unless (@$post) {
print $OUT "No post-prompt Perl actions.\n";
next CMD;
}
print $OUT "Perl commands run after each prompt:\n";
print $OUT "\t> -- $action\n";
}
next CMD;
}
next CMD; };
$cmd =~ /^\{\{\s*(.*)/ && do {
print $OUT "{{ is now a debugger command\n",
"use `;{{' if you mean Perl code\n";
$cmd = "h {{";
redo CMD;
}
push @$pretype, $1;
next CMD; };
$cmd =~ /^\{\s*(.*)/ && do {
unless ($1) {
print $OUT "All { actions cleared.\n";
$pretype = [];
next CMD;
}
if ($1 eq '?') {
unless (@$pretype) {
print $OUT "No pre-prompt debugger actions.\n";
next CMD;
}
print $OUT "Debugger commands run before each prompt:\n";
print $OUT "\t{ -- $action\n";
}
next CMD;
}
print $OUT "{ is now a debugger command\n",
"use `;{' if you mean Perl code\n";
$cmd = "h {";
redo CMD;
}
$pretype = [$1];
next CMD; };
$cmd =~ /^a\b\s*(\d*)\s*(.*)/ && do {
if (length $j) {
if ($dbline[$i] == 0) {
print $OUT "Line $i may not have an action.\n";
} else {
}
} else {
}
next CMD; };
$cmd =~ /^n$/ && do {
$single = 2;
last CMD; };
$cmd =~ /^s$/ && do {
$single = 1;
last CMD; };
$cmd =~ /^c\b\s*([\w:]*)\s*$/ && do {
$subname = $i = $1;
# Probably not needed, since we finish an interactive
# sub-session anyway...
# local $filename = $filename;
# local *dbline = *dbline; # XXX Would this work?!
if ($i =~ /\D/) { # subroutine name
unless $subname =~ /::/;
$i += 0;
if ($i) {
$max = $#dbline;
} else {
print $OUT "Subroutine $subname not found.\n";
next CMD;
}
}
if ($i) {
if ($dbline[$i] == 0) {
print $OUT "Line $i not breakable.\n";
next CMD;
}
}
for ($i=0; $i <= $stack_depth; ) {
$stack[$i++] &= ~1;
}
last CMD; };
$cmd =~ /^r$/ && do {
last CMD; };
$cmd =~ /^R$/ && do {
print $OUT "Warning: some settings and command-line options may be lost!\n";
# Put all the old includes at the start to get
# the same debugger.
for (@ini_INC) {
push @flags, '-I', $_;
}
# Arrange for setting the old INC:
if ($0 eq '-e') {
for (1..$#{'::_<-e'}) { # The first line is PERL5DB
chomp ($cl = ${'::_<-e'}[$_]);
}
} else {
@script = $0;
}
set_list("PERLDB_HIST",
my @had_breakpoints = keys %had_breakpoints;
my @hard;
for (0 .. $#had_breakpoints) {
my $file = $had_breakpoints[$_];
if $file =~ /^\(eval \d+\)$/;
my @add;
if $postponed_file{$file};
}
for (@hard) { # Yes, really-really...
# Find the subroutines in this eval
}
unless (%subs) {
print $OUT
"No subroutines in $_, ignoring breakpoints.\n";
next;
}
# One breakpoint per sub only:
and (not defined $offset # Not caught
}
}
if (defined $offset) {
"break $offset if $dbline{$line}";
} else {
print $OUT "Breakpoint in $_:$line ignored: after all the subroutines.\n";
}
}
}
#print "$^X, '-d', @flags, @script, ($slave_editor ? '-emacs' : ()), @ARGS";
print $OUT "exec failed: $!\n";
last CMD; };
$cmd =~ /^T$/ && do {
next CMD; };
$cmd =~ /^W\s*$/ && do {
$trace &= ~2;
next CMD; };
$cmd =~ /^W\b\s*(.*)/s && do {
push @to_watch, $1;
$evalarg = $1;
$trace |= 2;
next CMD; };
$cmd =~ /^\/(.*)$/ && do {
$inpat = $1;
$inpat =~ s:([^\\])/$:$1:;
if ($inpat ne "") {
# squelch the sigmangler
eval '$inpat =~ m'."\a$inpat\a";
if ($@ ne "") {
print $OUT "$@";
next CMD;
}
}
$incr = -1;
eval '
for (;;) {
++$start;
if ($slave_editor) {
print $OUT "\032\032$filename:$start:0\n";
} else {
}
last;
}
} ';
next CMD; };
$cmd =~ /^\?(.*)$/ && do {
$inpat = $1;
$inpat =~ s:([^\\])\?$:$1:;
if ($inpat ne "") {
# squelch the sigmangler
eval '$inpat =~ m'."\a$inpat\a";
if ($@ ne "") {
print $OUT $@;
next CMD;
}
}
$incr = -1;
eval '
for (;;) {
--$start;
if ($slave_editor) {
print $OUT "\032\032$filename:$start:0\n";
} else {
}
last;
}
} ';
next CMD; };
$i = $1 ? ($#hist-($2||1)) : ($2||$#hist);
redo CMD; };
&system($1);
next CMD; };
$pat = "^$1";
for ($i = $#hist; $i; --$i) {
}
if (!$i) {
print $OUT "No such command!\n\n";
next CMD;
}
redo CMD; };
next CMD; };
# XXX: using csh or tcsh destroys sigint retvals!
#&system($1); # use this instead
next CMD; };
$cmd =~ /^H\b\s*(-(\d+))?/ && do {
for ($i=$#hist; $i>$end; $i--) {
unless $hist[$i] =~ /^.?$/;
};
next CMD; };
runman($1);
next CMD; };
$cmd =~ s/^=\s*// && do {
my @keys;
if (length $cmd == 0) {
}
elsif (my($k,$v) = ($cmd =~ /^(\S+)\s+(\S.*)/)) {
# can't use $_ or kill //g state
for my $x ($k, $v) { $x =~ s/\a/\\a/g }
$alias{$k} = "s\a$k\a$v\a";
# squelch the sigmangler
unless (eval "sub { s\a$k\a$v\a }; 1") {
print $OUT "Can't alias $k to $v: $@\n";
delete $alias{$k};
next CMD;
}
@keys = ($k);
}
else {
}
for my $k (@keys) {
if ((my $v = $alias{$k}) =~ ss\a$k\a(.*)\a$1) {
print $OUT "$k\t= $1\n";
}
elsif (defined $alias{$k}) {
print $OUT "$k\t$alias{$k}\n";
}
else {
print "No alias for $k\n";
}
}
next CMD; };
$cmd =~ /^\|\|?\s*[^|]/ && do {
if ($pager =~ /^\|/) {
} else {
}
fix_less();
&warn("Can't pipe output to `$pager'");
if ($pager =~ /^\|/) {
|| &warn("Can't restore DB::OUT");
open(STDOUT,">&SAVEOUT")
|| &warn("Can't restore STDOUT");
close(SAVEOUT);
} else {
|| &warn("Can't restore DB::OUT");
}
next CMD;
}
$|= 1;
$cmd =~ s/^\|+\s*//;
redo PIPE;
};
# XXX Local variants do not work!
} # PIPE:
if ($onetimeDump) {
$onetimeDump = undef;
} elsif ($term_pid == $$) {
print $OUT "\n";
}
} continue { # CMD:
if ($piped) {
if ($pager =~ /^\|/) {
$? = 0;
# we cannot warn here: the handle is missing --tchrist
# most of the $? crud was coping with broken cshisms
if ($?) {
print SAVEOUT "Pager `$pager' failed: ";
if ($? == -1) {
print SAVEOUT "shell returned -1\n";
} elsif ($? >> 8) {
print SAVEOUT
( $? & 127 ) ? " (SIG#".($?&127).")" : "",
( $? & 128 ) ? " -- core dumped" : "", "\n";
} else {
}
}
# Will stop ignoring SIGPIPE if done like nohup(1)
# does SIGINT but Perl doesn't give us a choice.
} else {
}
close(SAVEOUT);
$piped= "";
}
} # CMD:
&eval;
}
} # if ($single || $signal)
($@, $!, $^E, $,, $/, $\, $^W) = @saved;
();
}
# The following code may be executed now:
# BEGIN {warn 4}
sub sub {
$al = " for $$sub";
}
$#stack = $stack_depth;
$single &= 1;
($frame & 4
# Why -1? But it works! :-(
if (wantarray) {
($frame & 4
print $fh "list context return from $sub:\n";
$doret = -2;
}
@ret;
} else {
if (defined wantarray) {
} else {
};
($frame & 4
print $fh (defined wantarray
? "scalar context return from $sub: "
: "void context return from $sub\n");
$doret = -2;
}
$ret;
}
}
sub save {
@saved = ($@, $!, $^E, $,, $/, $\, $^W);
$, = ""; $/ = "\n"; $\ = ""; $^W = 0;
}
# The following takes its argument via $evalarg to preserve current @_
sub eval {
# 'my' would make it visible from user code
# but so does local! --tchrist
local @res;
{
local $od = $^D;
$^D = $od;
}
my $at = $@;
if ($at) {
} elsif ($onetimeDump eq 'dump') {
} elsif ($onetimeDump eq 'methods') {
}
@res;
}
sub postponed_sub {
my $subname = shift;
# Filename below can contain ':'
if ($i) {
$i += $offset;
local $^W = 0; # != 0 is magical below
my $max = $#dbline;
} else {
print $OUT "Subroutine $subname not found.\n";
}
return;
}
#print $OUT "In postponed_sub for `$subname'.\n";
}
sub postponed {
if ($ImmediateStop) {
$ImmediateStop = 0;
$signal = 1;
}
return &postponed_sub
unless ref \$_[0] eq 'GLOB'; # A subroutine is compiled.
# Cannot be done before the file is compiled
local *dbline = shift;
$filename =~ s/^_<//;
if $break_on_load{$filename};
return unless $postponed_file{$filename};
#%dbline = %{$postponed_file{$filename}}; # Cannot be done: unsufficient magic
my $key;
}
delete $postponed_file{$filename};
}
sub dumpit {
local ($savout) = select(shift);
local $frame = 0;
local $doret = -2;
do 'dumpvar.pl';
}
} else {
print $OUT "dumpvar.pl not available.\n";
}
select ($savout);
}
# Tied method do not create a context, so may get wrong message:
sub print_trace {
my $fh = shift;
my $s;
for ($i=0; $i <= $#sub; $i++) {
last if $signal;
local $" = ', ';
? "(@{ $sub[$i]{args} })"
: '' ;
$s = $sub[$i]{sub};
if ($short) {
print $fh "$sub[$i]{context}=$sub$args from $file:$sub[$i]{line}\n";
} else {
print $fh "$sub[$i]{context} = $s$args" .
" called from $file" .
" line $sub[$i]{line}\n";
}
}
}
sub dump_trace {
my $skip = shift;
my $count = shift || 1e9;
$skip++;
$trace = 0;
for ($i = $skip;
$i++) {
@a = ();
my $type;
if (not defined $arg) {
push @a, "undef";
push @a, "tied";
push @a, "ref($type)";
} else {
local $_ = "$arg"; # Safe to stringify now - should not call f().
s/([\'\\])/\\$1/g;
s/(.*)/'$1'/s
unless /^(?: -?[\d.]+ | \*[\w:]* )$/x;
push(@a, $_);
}
}
$args = $h ? [@a] : undef;
$e =~ s/\n\s*\;\s*\Z// if $e;
$e =~ s/([\\\'])/\\$1/g if $e;
if ($r) {
$sub = "require '$e'";
} elsif (defined $r) {
$sub = "eval '$e'";
} elsif ($sub eq '(eval)') {
$sub = "eval {...}";
}
last if $signal;
}
@sub;
}
sub action {
my $action = shift;
while ($action =~ s/\\$//) {
#print $OUT "+ ";
#$action .= "\n";
}
$action;
}
sub unbalanced {
# i hate using globals!
$balanced_brace_re ||= qr{
^ \{
(?:
(?> [^{}] + ) # Non-parens without backtracking
|
(??{ $balanced_brace_re }) # Group with matching parens
) *
\} $
}x;
return $_[0] !~ m/$balanced_brace_re/;
}
sub gets {
&readline("cont: ");
}
sub system {
# We save, change, then restore STDIN and STDOUT to avoid fork() since
# some non-Unix systems can do system() but have problems with fork().
# XXX: using csh or tcsh destroys sigint retvals!
system(@_);
close(SAVEIN);
close(SAVEOUT);
# most of the $? crud was coping with broken cshisms
if ($? >> 8) {
} elsif ($?) {
(($? & 128) ? " -- core dumped" : "") , ")", "\n");
}
return $?;
}
sub setterm {
local $frame = 0;
local $doret = -2;
if ($notty) {
if ($tty) {
$| = 1;
select($sel);
} else {
eval "require Term::Rendezvous;" or die;
}
}
if (!$rl) {
} else {
if defined $rl_attribs->{basic_word_break_characters}
}
}
$term_pid = $$;
}
sub resetterm { # We forked, so we need a different TTY
$term_pid = $$;
if (defined &get_fork_TTY) {
} elsif (not defined $fork_TTY
# Possibly _inside_ XTERM
sleep 10000000' |];
chomp $fork_TTY;
}
if (defined $fork_TTY) {
undef $fork_TTY;
} else {
print_help(<<EOP);
I<#########> Forked, but do not know how to change a B<TTY>. I<#########>
}
}
sub readline {
local $.;
if (@typeahead) {
return $got;
}
local $frame = 0;
local $doret = -2;
$OUT->write(join('', @_));
my $stuff;
$stuff;
}
else {
$term->readline(@_);
}
}
sub dump_option {
$val =~ s/([\\\'])/\\$1/g;
}
sub option_val {
my $val;
if (defined $optionVars{$opt}
and defined ${$optionVars{$opt}}) {
} elsif (defined $optionAction{$opt}
and defined &{$optionAction{$opt}}) {
} elsif (defined $optionAction{$opt}
or defined $optionVars{$opt}
and not defined ${$optionVars{$opt}}) {
} else {
}
$val
}
sub parse_options {
local($_)= @_;
# too dangerous to let intuitive usage overwrite important things
# defaultion should never be the default
my %opt_needs_val = map { ( $_ => 1 ) } qw{
};
while (length) {
my $val_defaulted;
s/^\s+// && next;
s/^(\w+)(\W?)// or print($OUT "Invalid option `$_'\n"), last;
my $val;
if ("?" eq $sep) {
print($OUT "Option query `$opt?' followed by non-space `$_'\n"), last
if /^\S/;
#&dump_option($opt);
} elsif ($sep !~ /\S/) {
$val_defaulted = 1;
} elsif ($sep eq "=") {
if (s/ (["']) ( (?: \\. | (?! \1 ) [^\\] )* ) \1 //x) {
my $quote = $1;
} else {
s/^(\S*)//;
$val = $1;
unless length $val;
}
} else { #{ to "let some poor schmuck bounce on the % key in B<vi>."
print($OUT "Unclosed option value `$opt$sep$_'\n"), last;
}
my $option;
print $OUT "Option `$opt' is non-boolean. Use `O $option=VAL' to set, `O $option?' to query\n";
next;
}
eval qq{
local \$frame = 0;
local \$doret = -2;
require '$optionRequire{$option}';
1;
} || die # XXX: shouldn't happen
if defined $optionRequire{$option} &&
defined $val;
if defined $optionVars{$option} &&
defined $val;
if defined $optionAction{$option} &&
defined &{$optionAction{$option}} &&
defined $val;
# Not $rcfile
}
}
sub set_list {
my $val;
for $i (0 .. $#list) {
$val =~ s/\\/\\\\/g;
}
}
sub get_list {
my $stem = shift;
my @list;
my $n = delete $ENV{"${stem}_n"};
my $val;
for $i (0 .. $n - 1) {
}
@list;
}
sub catch {
$signal = 1;
}
sub warn {
my($msg)= join("",@_);
}
sub TTY {
if ($in =~ /,/) {
} else {
}
} elsif ($term and @_) {
&warn("Too late to set TTY, enabled on next `R'!\n");
}
$tty = shift if @_;
}
sub noTTY {
if ($term) {
&warn("Too late to set noTTY, enabled on next `R'!\n") if @_;
}
$notty = shift if @_;
$notty;
}
sub ReadLine {
if ($term) {
&warn("Too late to set ReadLine, enabled on next `R'!\n") if @_;
}
$rl = shift if @_;
$rl;
}
sub RemotePort {
if ($term) {
&warn("Too late to set RemotePort, enabled on next 'R'!\n") if @_;
}
$remoteport = shift if @_;
}
sub tkRunning {
} else {
print $OUT "tkRunning not supported by current ReadLine package.\n";
0;
}
}
sub NonStop {
if ($term) {
&warn("Too late to set up NonStop mode, enabled on next `R'!\n") if @_;
}
$runnonstop = shift if @_;
}
sub pager {
if (@_) {
$pager = shift;
}
$pager;
}
sub shellBang {
if (@_) {
$sh = quotemeta shift;
}
$psh =~ s/\\b$//;
$psh =~ s/\\(.)/$1/g;
&sethelp;
$psh;
}
sub ornaments {
if (defined $term) {
} else {
$ornaments = shift;
}
}
sub recallCommand {
if (@_) {
$rc = quotemeta shift;
}
$prc =~ s/\\b$//;
$prc =~ s/\\(.)/$1/g;
&sethelp;
$prc;
}
sub LineInfo {
return $lineinfo unless @_;
$lineinfo = shift;
$slave_editor = ($stream =~ /^\|/);
$| = 1;
select($save);
$lineinfo;
}
sub list_versions {
my %version;
my $file;
for (keys %INC) {
$file = $_;
s,\.p[lm]$,,i ;
s,/,::,g ;
if (defined ${ $_ . '::VERSION' }) {
}
}
}
sub sethelp {
# XXX: make sure these are tabs between the command and explantion,
# or print_help will screw up your formatting if you have
# eeevil ornaments enabled. This is an insane mess.
$help = "
`B<R>' after you set them).
Type `|h' for a paged display if this was too hard to read.
"; # Fix balance of vi % matching: } }}
$summary = <<"END_SUM";
B<<>[B<<>]|B<{>[B<{>]|B<>>[B<>>] [I<cmd>] Do pre/post-prompt B<b> [I<ln>|I<event>|I<sub>] [I<cnd>] Set breakpoint
# ')}}; # Fix balance of vi % matching
}
sub print_help {
local $_ = shift;
# Restore proper alignment destroyed by eeevil I<> and B<>
# ornaments: A pox on both their houses!
#
# A help command will have everything up to and including
# the first tab sequence paddeed into a field 16 (or if indented 20)
# wide. If it's wide than that, an extra space will be added.
s{
^ # only matters at start of line
( \040{4} | \t )* # some subcommands are indented
( < ? # so <CR> works
[BI] < [^\t\n] + ) # find an eeevil ornament
( \t+ ) # original separation, discarded
( .* ) # this will now start (no earlier) than
# column 16
} {
# replace with this whole string:
. $command
. $text;
}mgex;
s{ # handle bold ornaments
B < ( [^>] + | > ) >
} {
. $1
}gex;
s{ # handle italic ornaments
I < ( [^>] + | > ) >
} {
. $1
}gex;
print $OUT $_;
}
sub fix_less {
}
# changes environment!
}
sub diesignal {
local $frame = 0;
local $doret = -2;
kill 'ABRT', $$ if $panic++;
}
else {
}
kill 'ABRT', $$;
}
sub dbwarn {
local $frame = 0;
local $doret = -2;
# require may be broken.
warn(@_, "\nCannot print stack trace, load with -MCarp option to see stack"),
}
sub dbdie {
local $frame = 0;
local $doret = -2;
if ($dieLevel > 2) {
&warn(@_); # Yell no matter what
return;
}
if ($dieLevel < 2) {
die @_ if $^S; # in eval propagate
}
# require may be broken.
die(@_, "\nCannot print stack trace, load with -MCarp option to see stack")
# We do not want to debug this chunk (automatic disabling works
# inside DB::DB, but not in Carp).
die $mess;
}
sub warnLevel {
if (@_) {
$warnLevel = shift;
if ($warnLevel) {
} else {
}
}
}
sub dieLevel {
if (@_) {
$dieLevel = shift;
if ($dieLevel) {
#$SIG{__DIE__} = \&DB::diehard if $dieLevel >= 2;
print $OUT "Stack dump during die enabled",
if $I_m_init;
} else {
print $OUT "Default die handler restored.\n";
}
}
$dieLevel;
}
sub signalLevel {
if (@_) {
$signalLevel = shift;
if ($signalLevel) {
} else {
}
}
}
sub CvGV_name {
my $in = shift;
}
sub CvGV_name_or_bust {
my $in = shift;
return if $skipCvGV; # Backdoor to avoid problems if XS broken...
}
sub find_sub {
my $subr = shift;
return unless defined &$subr;
my $data;
# Old stupid way...
my $s;
for (keys %sub) {
$s = $_, last if $subr eq \&$_;
}
$sub{$s} if $s;
}
}
sub methods {
my $class = shift;
local %seen;
local %packs;
}
sub methods_via {
my $class = shift;
my $prefix = shift;
my $name;
for $name (grep {defined &{${"${class}::"}{$_}}}
sort keys %{"${class}::"}) {
}
return unless shift; # Recurse?
for $name (@{"${class}::ISA"}) {
}
}
sub setman {
? "man" # O Happy Day!
: "perldoc"; # Alas, poor unfortunates
}
sub runman {
my $page = shift;
unless ($page) {
&system("$doccmd $doccmd");
return;
}
# this way user can override, like with $doccmd="man -Mwhatever"
# or even just "man " to disable the path check.
unless ($doccmd eq 'man') {
&system("$doccmd $page");
return;
}
require Config;
my $manpath = '';
# harmless if missing, I figure
if (system($doccmd,
# I just *know* there are men without -M
split ' ', $page) )
{
if (grep { $page eq $_ } qw{
})
{
system($doccmd,
$page);
}
}
}
if (defined $oldpath) {
} else {
}
}
# The following BEGIN is very handy if debugger goes havoc, debugging debugger?
BEGIN { # This does not compile, alas.
$sh = '!';
$rc = ',';
@hist = ('?');
$window = 10;
$preview = 3;
$sub = '';
# This may be enabled to debug debugger:
#$warnLevel = 1 unless defined $warnLevel;
#$dieLevel = 1 unless defined $dieLevel;
#$signalLevel = 1 unless defined $signalLevel;
# @stack and $doret are needed in sub sub, which is called for DB::postponed.
# Triggers bug (?) in perl is we postpone this until runtime:
$doret = -2;
$frame = 0;
}
BEGIN {$^W = $ini_warn;} # Switch warnings back
#use Carp; # This did break, left for debuggin
sub db_complete {
# Specific code for b c l V m f O, &blah, $blah, @blah, %blah
($text, "^\Q${'package'}::\E([^:]+)\$");
grep !/^main::/,
# packages
if ( $line =~ /^\|*f\s+(.*)/ ) { # Loaded files
# We may want to complete to (eval 9), so $text may be wrong
$text = $1;
return sort
}
$prefix = "&";
return sort map "$prefix$_",
grep /^\Q$text/,
(keys %sub),
(map { /$search/ ? ($1) : () }
keys %sub);
}
if ($text =~ /^[\$@%](.*)::(.*)/) { # symbols in a package
$text = $2;
my @out
}
return sort @out;
}
if ($text =~ /^[\$@%]/) { # symbols (in $package + packages in main)
( $pack eq '::' ? () : (grep /::$/, keys %::) ) ;
}
return sort @out;
}
my $out = '? ';
# Can do nothing better
} elsif ($val =~ /\s/) {
my $found;
foreach $l (split //, qq/\"\'\#\|/) {
}
} else {
$out = "=$val ";
}
# Default to value if one completion, to question if many
return sort @out;
}
}
sub end_report {
print $OUT "Use `q' to quit or `R' to restart. `h q' for details.\n"
}
END {
# Do not stop in at_exit() and destructors on exit:
}
sub at_exit {
"Debugged program terminated. Use `q' to quit or `R' to restart.";
}
package DB; # Do not trace this 1; below!
1;