considered to be a breakpoint; C<perl5db.pl> uses C<$break_condition\0$action>.
Values are magical in numeric context: 1 if the line is breakable, 0 if not.
-The scalar C<${"_<$filename"}> simply contains the string C<<< _<$filename> >>>.
+The scalar C<${"_<$filename"}> simply contains the string C<$filename>.
This is also the case for evaluated strings that contain subroutines, or
which are currently being executed. The $filename for C<eval>ed strings looks
like C<(eval 34).
use strict;
-BEGIN {eval 'use IO::Handle'}; # Needed for flush only? breaks under miniperl
+BEGIN {eval 'use IO::Handle'}; # Needed for flush only? breaks under miniperl
BEGIN {
require feature;
# Debugger for Perl 5.00x; perl5db.pl patch level:
use vars qw($VERSION $header);
-$VERSION = '1.39_04';
+$VERSION = '1.39_05';
$header = "perl5db.pl version $VERSION";
use vars qw(
@args
%break_on_load
- @cmdfhs
$CommandSet
$CreateTTY
$DBGR
$dbline
%dbline
$dieLevel
- $evalarg
$filename
- $frame
- $hist
$histfile
$histsize
- $ImmediateStop
$IN
$inhibit_exit
@ini_INC
$ini_warn
- $line
$maxtrace
$od
- $onetimeDump
- $onetimedumpDepth
- %option
@options
$osingle
$otrace
- $OUT
- $packname
$pager
$post
%postponed
@res
$rl
@saved
- $signal
$signalLevel
- $single
- $start
$sub
- %sub
- $subname
$term
- $trace
$usercontext
$warnLevel
- $window
+);
+
+our (
+ @cmdfhs,
+ $evalarg,
+ $frame,
+ $hist,
+ $ImmediateStop,
+ $line,
+ $onetimeDump,
+ $onetimedumpDepth,
+ %option,
+ $OUT,
+ $packname,
+ $signal,
+ $single,
+ $start,
+ %sub,
+ $subname,
+ $trace,
+ $window,
);
# Used to save @ARGV and extract any debugger-related flags.
# Used to prevent the debugger from running nonstop
# after a restart
-use vars qw($second_time);
+our ($second_time);
sub _calc_usercontext {
my ($package) = @_;
# Cancel strict completely for the evaluated code, so the code
# the user evaluates won't be affected by it. (Shlomi Fish)
- return 'no strict; ($@, $!, $^E, $,, $/, $\, $^W) = @saved;'
+ return 'no strict; ($@, $!, $^E, $,, $/, $\, $^W) = @DB::saved;'
. "package $package;"; # this won't let them modify, alas
}
# Since we're only saving $@, we only have to localize the array element
# that it will be stored in.
local $saved[0]; # Preserve the old value of $@
- eval { &DB::save };
+ eval { DB::save() };
# Now see whether we need to report an error back to the user.
if ($at) {
you of each new thread created. It will also indicate the thread id in which
we are currently running within the prompt like this:
- [tid] DB<$i>
+ [tid] DB<$i>
Where C<[tid]> is an integer thread id and C<$i> is the familiar debugger
command prompt. The prompt will show: C<[0]> when running under threads, but
=cut
BEGIN {
- # ensure we can share our non-threaded variables or no-op
- if ($ENV{PERL5DB_THREADED}) {
- require threads;
- require threads::shared;
- import threads::shared qw(share);
- $DBGR;
- share(\$DBGR);
- lock($DBGR);
- print "Threads support enabled\n";
- } else {
- *lock = sub(*) {};
- *share = sub(*) {};
- }
+ # ensure we can share our non-threaded variables or no-op
+ if ($ENV{PERL5DB_THREADED}) {
+ require threads;
+ require threads::shared;
+ import threads::shared qw(share);
+ $DBGR;
+ share(\$DBGR);
+ lock($DBGR);
+ print "Threads support enabled\n";
+ } else {
+ *share = sub(\[$@%]) {};
+ }
}
# These variables control the execution of 'dumpvar.pl'.
}
# without threads, $filename is not defined until DB::DB is called
-foreach my $k (keys (%INC)) {
- share(\$main::{'_<'.$filename}) if defined $filename;
-};
+share($main::{'_<'.$filename}) if defined $filename;
# Command-line + PERLLIB:
# Save the contents of @INC before they are modified elsewhere.
$pidprompt = '';
# Sets up $emacs as a synonym for $slave_editor.
-use vars qw($slave_editor);
+our ($slave_editor);
*emacs = $slave_editor if $slave_editor; # May be used in afterinit()...
=head2 READING THE RC FILE
=cut
-use vars qw(@hist @truehist %postponed_file @typeahead);
+use vars qw(%postponed_file @typeahead);
+
+our (@hist, @truehist);
sub _restore_shared_globals_after_restart
{
=cut
-use vars qw($notty $runnonstop $console $tty $LINEINFO);
+use vars qw($notty $console $tty $LINEINFO);
use vars qw($lineinfo $doccmd);
+our ($runnonstop);
+
if ($notty) {
$runnonstop = 1;
- share($runnonstop);
+ share($runnonstop);
}
=pod
# Is Perl being run from a slave editor or graphical debugger?
# If so, don't use readline, and set $slave_editor = 1.
- $slave_editor =
- ( ( defined $main::ARGV[0] ) and ( $main::ARGV[0] eq '-emacs' ) );
- $rl = 0, shift(@main::ARGV) if $slave_editor;
+ if ($slave_editor = ( @main::ARGV && ( $main::ARGV[0] eq '-emacs' ) )) {
+ $rl = 0;
+ shift(@main::ARGV);
+ }
#require Term::ReadLine;
# Keep copies of the filehandles so that when the pager runs, it
# can close standard input without clobbering ours.
- $IN = \*IN, $OUT = \*OUT if $console or not defined $console;
+ if ($console or (not defined($console))) {
+ $IN = \*IN;
+ $OUT = \*OUT;
+ }
} ## end elsif (from if(defined $remoteport))
# Unbuffer DB::OUT. We need to see responses right away.
# and a I/O description to keep track of.
$LINEINFO = $OUT unless defined $LINEINFO;
$lineinfo = $console unless defined $lineinfo;
- # share($LINEINFO); # <- unable to share globs
- share($lineinfo); #
+ # share($LINEINFO); # <- unable to share globs
+ share($lineinfo); #
=pod
# If there was an afterinit() sub defined, call it. It will get
# executed in our scope, so it can fiddle with debugger globals.
if ( defined &afterinit ) { # May be defined in $rcfile
- &afterinit();
+ afterinit();
}
# Inform us about "Stack dump during die enabled ..." in dieLevel().
=cut
+# $cmd cannot be an our() variable unfortunately (possible perl bug?).
+
use vars qw(
$action
- %alias
$cmd
- $doret
- $fall_off_end
$file
$filename_ini
$finished
%had_breakpoints
- $incr
- $laststep
$level
$max
- @old_watch
$package
- $rc
- $sh
- @stack
- $stack_depth
- @to_watch
$try
- $end
);
-sub DB {
+our (
+ %alias,
+ $doret,
+ $end,
+ $fall_off_end,
+ $incr,
+ $laststep,
+ $rc,
+ $sh,
+ $stack_depth,
+ @stack,
+ @to_watch,
+ @old_watch,
+);
- # lock the debugger and get the thread id for the prompt
- lock($DBGR);
- my $tid;
- my $position;
- my ($prefix, $after, $infix);
- my $pat;
+sub _DB__determine_if_we_should_break
+{
+ # if we have something here, see if we should break.
+ # $stop is lexical and local to this block - $action on the other hand
+ # is global.
+ my $stop;
- if ($ENV{PERL5DB_THREADED}) {
- $tid = eval { "[".threads->tid."]" };
- }
+ if ( $dbline{$line}
+ && _is_breakpoint_enabled($filename, $line)
+ && (( $stop, $action ) = split( /\0/, $dbline{$line} ) ) )
+ {
- # Check for whether we should be running continuously or not.
- # _After_ the perl program is compiled, $single is set to 1:
- if ( $single and not $second_time++ ) {
+ # Stop if the stop criterion says to just stop.
+ if ( $stop eq '1' ) {
+ $signal |= 1;
+ }
- # Options say run non-stop. Run until we get an interrupt.
- if ($runnonstop) { # Disable until signal
- # If there's any call stack in place, turn off single
- # stepping into subs throughout the stack.
- for my $i (0 .. $stack_depth) {
- $stack[ $i ] &= ~1;
+ # It's a conditional stop; eval it in the user's context and
+ # see if we should stop. If so, remove the one-time sigil.
+ elsif ($stop) {
+ $evalarg = "\$DB::signal |= 1 if do {$stop}";
+ DB::eval();
+ # If the breakpoint is temporary, then delete its enabled status.
+ if ($dbline{$line} =~ s/;9($|\0)/$1/) {
+ _cancel_breakpoint_temp_enabled_status($filename, $line);
}
+ }
+ } ## end if ($dbline{$line} && ...
+}
- # And we are now no longer in single-step mode.
- $single = 0;
-
- # If we simply returned at this point, we wouldn't get
- # the trace info. Fall on through.
- # return;
- } ## end if ($runnonstop)
+sub _DB__is_finished {
+ if ($finished and $level <= 1) {
+ end_report();
+ return 1;
+ }
+ else {
+ return;
+ }
+}
- elsif ($ImmediateStop) {
+sub _DB__read_next_cmd
+{
+ my ($tid) = @_;
- # We are supposed to stop here; XXX probably a break.
- $ImmediateStop = 0; # We've processed it; turn it off
- $signal = 1; # Simulate an interrupt to force
- # us into the command loop
- }
- } ## end if ($single and not $second_time...
+ # We have a terminal, or can get one ...
+ if (!$term) {
+ setterm();
+ }
- # If we're in single-step mode, or an interrupt (real or fake)
- # has occurred, turn off non-stop mode.
- $runnonstop = 0 if $single or $signal;
+ # ... and it belogs to this PID or we get one for this PID ...
+ if ($term_pid != $$) {
+ resetterm(1);
+ }
- # Preserve current values of $@, $!, $^E, $,, $/, $\, $^W.
- # The code being debugged may have altered them.
- &save;
+ # ... and we got a line of command input ...
+ $cmd = DB::readline(
+ "$pidprompt $tid DB"
+ . ( '<' x $level )
+ . ( $#hist + 1 )
+ . ( '>' x $level ) . " "
+ );
- # Since DB::DB gets called after every line, we can use caller() to
- # figure out where we last were executing. Sneaky, eh? This works because
- # caller is returning all the extra information when called from the
- # debugger.
- local ( $package, $filename, $line ) = caller;
- $filename_ini = $filename;
+ return defined($cmd);
+}
- # set up the context for DB::eval, so it can properly execute
- # code on behalf of the user. We add the package in so that the
- # code is eval'ed in the proper package (not in the debugger!).
- local $usercontext = _calc_usercontext($package);
+sub _DB__trim_command_and_return_first_component {
+ $cmd =~ s/\A\s+//s; # trim annoying leading whitespace
+ $cmd =~ s/\s+\z//s; # trim annoying trailing whitespace
- # Create an alias to the active file magical array to simplify
- # the code here.
- local (*dbline) = $main::{ '_<' . $filename };
+ $cmd =~ m{\A(\S*)};
+ return $1;
+}
- # Last line in the program.
- $max = $#dbline;
+sub _DB__handle_f_command {
+ if (($file) = $cmd =~ /\Af\b\s*(.*)/) {
+ $file =~ s/\s+$//;
- # if we have something here, see if we should break.
- {
- # $stop is lexical and local to this block - $action on the other hand
- # is global.
- my $stop;
+ # help for no arguments (old-style was return from sub).
+ if ( !$file ) {
+ print $OUT
+ "The old f command is now the r command.\n"; # hint
+ print $OUT "The new f command switches filenames.\n";
+ next CMD;
+ } ## end if (!$file)
+
+ # if not in magic file list, try a close match.
+ if ( !defined $main::{ '_<' . $file } ) {
+ if ( ($try) = grep( m#^_<.*$file#, keys %main:: ) ) {
+ {
+ $try = substr( $try, 2 );
+ print $OUT "Choosing $try matching '$file':\n";
+ $file = $try;
+ }
+ } ## end if (($try) = grep(m#^_<.*$file#...
+ } ## end if (!defined $main::{ ...
- if ( $dbline{$line}
- && _is_breakpoint_enabled($filename, $line)
- && (( $stop, $action ) = split( /\0/, $dbline{$line} ) ) )
- {
+ # If not successfully switched now, we failed.
+ if ( !defined $main::{ '_<' . $file } ) {
+ print $OUT "No file matching '$file' is loaded.\n";
+ next CMD;
+ }
- # Stop if the stop criterion says to just stop.
- if ( $stop eq '1' ) {
- $signal |= 1;
- }
+ # We switched, so switch the debugger internals around.
+ elsif ( $file ne $filename ) {
+ *dbline = $main::{ '_<' . $file };
+ $max = $#dbline;
+ $filename = $file;
+ $start = 1;
+ $cmd = "l";
+ } ## end elsif ($file ne $filename)
- # It's a conditional stop; eval it in the user's context and
- # see if we should stop. If so, remove the one-time sigil.
- elsif ($stop) {
- $evalarg = "\$DB::signal |= 1 if do {$stop}";
- &eval;
- # If the breakpoint is temporary, then delete its enabled status.
- if ($dbline{$line} =~ s/;9($|\0)/$1/) {
- _cancel_breakpoint_temp_enabled_status($filename, $line);
- }
- }
- } ## end if ($dbline{$line} && ...
+ # We didn't switch; say we didn't.
+ else {
+ print $OUT "Already in $file.\n";
+ next CMD;
+ }
}
- # Preserve the current stop-or-not, and see if any of the W
- # (watch expressions) has changed.
- my $was_signal = $signal;
+ return;
+}
- # If we have any watch expressions ...
- if ( $trace & 2 ) {
- for my $n (0 .. $#to_watch) {
- $evalarg = $to_watch[$n];
- local $onetimeDump; # Tell DB::eval() to not output results
+sub _DB__handle_dot_command {
+ my ($obj) = @_;
- # Fix context DB::eval() wants to return an array, but
- # we need a scalar here.
- my ($val) = join( "', '", &eval );
- $val = ( ( defined $val ) ? "'$val'" : 'undef' );
+ # . command.
+ if ($cmd eq '.') {
+ $incr = -1; # stay at current line
- # Did it change?
- if ( $val ne $old_watch[$n] ) {
+ # Reset everything to the old location.
+ $start = $line;
+ $filename = $filename_ini;
+ *dbline = $main::{ '_<' . $filename };
+ $max = $#dbline;
- # Yep! Show the difference, and fake an interrupt.
- $signal = 1;
- print $OUT <<EOP;
-Watchpoint $n:\t$to_watch[$n] changed:
- old value:\t$old_watch[$n]
- new value:\t$val
-EOP
- $old_watch[$n] = $val;
- } ## end if ($val ne $old_watch...
- } ## end for my $n (0 ..
- } ## end if ($trace & 2)
+ # Now where are we?
+ print_lineinfo($obj->position());
+ next CMD;
+ }
-=head2 C<watchfunction()>
+ return;
+}
-C<watchfunction()> is a function that can be defined by the user; it is a
-function which will be run on each entry to C<DB::DB>; it gets the
-current package, filename, and line as its parameters.
+sub _DB__handle_y_command {
+ my ($obj) = @_;
-The watchfunction can do anything it likes; it is executing in the
-debugger's context, so it has access to all of the debugger's internal
-data structures and functions.
+ if (my ($match_level, $match_vars)
+ = $cmd =~ /^y(?:\s+(\d*)\s*(.*))?$/) {
-C<watchfunction()> can control the debugger's actions. Any of the following
-will cause the debugger to return control to the user's program after
-C<watchfunction()> executes:
+ # See if we've got the necessary support.
+ if (!eval { require PadWalker; PadWalker->VERSION(0.08) }) {
+ my $Err = $@;
+ DB::warn(
+ $Err =~ /locate/
+ ? "PadWalker module not found - please install\n"
+ : $Err
+ );
+ next CMD;
+ }
-=over 4
+ # Load up dumpvar if we don't have it. If we can, that is.
+ do 'dumpvar.pl' || die $@ unless defined &main::dumpvar;
+ defined &main::dumpvar
+ or print $OUT "dumpvar.pl not available.\n"
+ and next CMD;
-=item *
+ # Got all the modules we need. Find them and print them.
+ my @vars = split( ' ', $match_vars || '' );
-Returning a false value from the C<watchfunction()> itself.
+ # Find the pad.
+ my $h = eval { PadWalker::peek_my( ( $match_level || 0 ) + 1 ) };
-=item *
+ # Oops. Can't find it.
+ if (my $Err = $@) {
+ $Err =~ s/ at .*//;
+ DB::warn($Err);
+ next CMD;
+ }
-Altering C<$single> to a false value.
+ # Show the desired vars with dumplex().
+ my $savout = select($OUT);
-=item *
+ # Have dumplex dump the lexicals.
+ foreach my $key (sort keys %$h) {
+ dumpvar::dumplex( $key, $h->{$key},
+ defined $option{dumpDepth} ? $option{dumpDepth} : -1,
+ @vars );
+ }
+ select($savout);
+ next CMD;
+ }
+}
-Altering C<$signal> to a false value.
+sub _DB__handle_c_command {
+ my ($obj) = @_;
-=item *
+ if (my ($new_i) = $cmd =~ m#\Ac\b\s*([\w:]*)\s*\z#) {
-Turning off the C<4> bit in C<$trace> (this also disables the
-check for C<watchfunction()>. This can be done with
+ $obj->i_cmd($new_i);
- $trace &= ~4;
+ # Hey, show's over. The debugged program finished
+ # executing already.
+ next CMD if _DB__is_finished();
-=back
+ # Capture the place to put a one-time break.
+ $subname = $obj->i_cmd;
-=cut
+ # Probably not needed, since we finish an interactive
+ # sub-session anyway...
+ # local $filename = $filename;
+ # local *dbline = *dbline; # XXX Would this work?!
+ #
+ # The above question wonders if localizing the alias
+ # to the magic array works or not. Since it's commented
+ # out, we'll just leave that to speculation for now.
+
+ # If the "subname" isn't all digits, we'll assume it
+ # is a subroutine name, and try to find it.
+ if ( $subname =~ /\D/ ) { # subroutine name
+ # Qualify it to the current package unless it's
+ # already qualified.
+ $subname = $package . "::" . $subname
+ unless $subname =~ /::/;
+
+ # find_sub will return "file:line_number" corresponding
+ # to where the subroutine is defined; we call find_sub,
+ # break up the return value, and assign it in one
+ # operation.
+ ( $file, $new_i ) = ( find_sub($subname) =~ /^(.*):(.*)$/ );
+
+ # Force the line number to be numeric.
+ $obj->i_cmd($new_i + 0);
+
+ # If we got a line number, we found the sub.
+ if ($obj->i_cmd) {
+
+ # Switch all the debugger's internals around so
+ # we're actually working with that file.
+ $filename = $file;
+ *dbline = $main::{ '_<' . $filename };
+
+ # Mark that there's a breakpoint in this file.
+ $had_breakpoints{$filename} |= 1;
+
+ # Scan forward to the first executable line
+ # after the 'sub whatever' line.
+ $max = $#dbline;
+ my $ii = $obj->i_cmd;
+ ++$ii while $dbline[$ii] == 0 && $ii < $max;
+ $obj->i_cmd($ii);
+ } ## end if ($i)
+
+ # We didn't find a sub by that name.
+ else {
+ print $OUT "Subroutine $subname not found.\n";
+ next CMD;
+ }
+ } ## end if ($subname =~ /\D/)
- # If there's a user-defined DB::watchfunction, call it with the
- # current package, filename, and line. The function executes in
- # the DB:: package.
- if ( $trace & 4 ) { # User-installed watch
- return
- if watchfunction( $package, $filename, $line )
- and not $single
- and not $was_signal
- and not( $trace & ~4 );
- } ## end if ($trace & 4)
+ # At this point, either the subname was all digits (an
+ # absolute line-break request) or we've scanned through
+ # the code following the definition of the sub, looking
+ # for an executable, which we may or may not have found.
+ #
+ # If $i (which we set $subname from) is non-zero, we
+ # got a request to break at some line somewhere. On
+ # one hand, if there wasn't any real subroutine name
+ # involved, this will be a request to break in the current
+ # file at the specified line, so we have to check to make
+ # sure that the line specified really is breakable.
+ #
+ # On the other hand, if there was a subname supplied, the
+ # preceding block has moved us to the proper file and
+ # location within that file, and then scanned forward
+ # looking for the next executable line. We have to make
+ # sure that one was found.
+ #
+ # On the gripping hand, we can't do anything unless the
+ # current value of $i points to a valid breakable line.
+ # Check that.
+ if ($obj->i_cmd) {
+
+ # Breakable?
+ if ( $dbline[$obj->i_cmd] == 0 ) {
+ print $OUT "Line " . $obj->i_cmd . " not breakable.\n";
+ next CMD;
+ }
- # Pick up any alteration to $signal in the watchfunction, and
- # turn off the signal now.
- $was_signal = $signal;
- $signal = 0;
+ # Yes. Set up the one-time-break sigil.
+ $dbline{$obj->i_cmd} =~ s/($|\0)/;9$1/; # add one-time-only b.p.
+ _enable_breakpoint_temp_enabled_status($filename, $obj->i_cmd);
+ } ## end if ($i)
-=head2 GETTING READY TO EXECUTE COMMANDS
+ # Turn off stack tracing from here up.
+ for my $i (0 .. $stack_depth) {
+ $stack[ $i ] &= ~1;
+ }
+ last CMD;
+ }
-The debugger decides to take control if single-step mode is on, the
-C<t> command was entered, or the user generated a signal. If the program
-has fallen off the end, we set things up so that entering further commands
-won't cause trouble, and we say that the program is over.
+ return;
+}
-=cut
+sub _DB__handle_forward_slash_command {
+ my ($obj) = @_;
- # Make sure that we always print if asked for explicitly regardless
- # of $trace_to_depth .
- my $explicit_stop = ($single || $was_signal);
+ # The pattern as a string.
+ use vars qw($inpat);
- # Check to see if we should grab control ($single true,
- # trace set appropriately, or we got a signal).
- if ( $explicit_stop || ( $trace & 1 ) ) {
+ if (($inpat) = $cmd =~ m#\A/(.*)\z#) {
- # Yes, grab control.
- if ($slave_editor) {
+ # Remove the final slash.
+ $inpat =~ s:([^\\])/$:$1:;
- # Tell the editor to update its position.
- $position = "\032\032$filename:$line:0\n";
- print_lineinfo($position);
- }
+ # If the pattern isn't null ...
+ if ( $inpat ne "" ) {
-=pod
+ # Turn of warn and die procesing for a bit.
+ local $SIG{__DIE__};
+ local $SIG{__WARN__};
-Special check: if we're in package C<DB::fake>, we've gone through the
-C<END> block at least once. We set up everything so that we can continue
-to enter commands and have a valid context to be in.
+ # Create the pattern.
+ eval 'no strict q/vars/; $inpat =~ m' . "\a$inpat\a";
+ if ( $@ ne "" ) {
-=cut
+ # Oops. Bad pattern. No biscuit.
+ # Print the eval error and go back for more
+ # commands.
+ print $OUT "$@";
+ next CMD;
+ }
+ $obj->pat($inpat);
+ } ## end if ($inpat ne "")
- elsif ( $package eq 'DB::fake' ) {
+ # Set up to stop on wrap-around.
+ $end = $start;
- # Fallen off the end already.
- $term || &setterm;
- print_help(<<EOP);
-Debugged program terminated. Use B<q> to quit or B<R> to restart,
- use B<o> I<inhibit_exit> to avoid stopping after program termination,
- B<h q>, B<h R> or B<h o> to get additional info.
-EOP
+ # Don't move off the current line.
+ $incr = -1;
- # Set the DB::eval context appropriately.
- $package = 'main';
- $usercontext = _calc_usercontext($package);
- } ## end elsif ($package eq 'DB::fake')
+ my $pat = $obj->pat;
-=pod
+ # Done in eval so nothing breaks if the pattern
+ # does something weird.
+ eval
+ {
+ no strict q/vars/;
+ for (;;) {
+ # Move ahead one line.
+ ++$start;
+
+ # Wrap if we pass the last line.
+ $start = 1 if ($start > $max);
+
+ # Stop if we have gotten back to this line again,
+ last if ($start == $end);
+
+ # A hit! (Note, though, that we are doing
+ # case-insensitive matching. Maybe a qr//
+ # expression would be better, so the user could
+ # do case-sensitive matching if desired.
+ if ($dbline[$start] =~ m/$pat/i) {
+ if ($slave_editor) {
+ # Handle proper escaping in the slave.
+ print $OUT "\032\032$filename:$start:0\n";
+ }
+ else {
+ # Just print the line normally.
+ print $OUT "$start:\t",$dbline[$start],"\n";
+ }
+ # And quit since we found something.
+ last;
+ }
+ }
+ };
-If the program hasn't finished executing, we scan forward to the
-next executable line, print that out, build the prompt from the file and line
-number information, and print that.
+ if ($@) {
+ warn $@;
+ }
-=cut
+ # If we wrapped, there never was a match.
+ if ( $start == $end ) {
+ print {$OUT} "/$pat/: not found\n";
+ }
+ next CMD;
+ }
- else {
+ return;
+}
+sub _DB__handle_question_mark_command {
+ my ($obj) = @_;
- # Still somewhere in the midst of execution. Set up the
- # debugger prompt.
- $sub =~ s/\'/::/; # Swap Perl 4 package separators (') to
- # Perl 5 ones (sorry, we don't print Klingon
- #module names)
+ # ? - backward pattern search.
+ if (my ($inpat) = $cmd =~ m#\A\?(.*)\z#) {
- $prefix = $sub =~ /::/ ? "" : ($package . '::');
- $prefix .= "$sub($filename:";
- $after = ( $dbline[$line] =~ /\n$/ ? '' : "\n" );
+ # Get the pattern, remove trailing question mark.
+ $inpat =~ s:([^\\])\?$:$1:;
- # Break up the prompt if it's really long.
- 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 we've got one ...
+ if ( $inpat ne "" ) {
- # Print current line info, indenting if necessary.
- if ($frame) {
- print_lineinfo( ' ' x $stack_depth,
- "$line:\t$dbline[$line]$after" );
- }
- else {
- depth_print_lineinfo($explicit_stop, $position);
+ # Turn off die & warn handlers.
+ local $SIG{__DIE__};
+ local $SIG{__WARN__};
+ eval '$inpat =~ m' . "\a$inpat\a";
+
+ if ( $@ ne "" ) {
+
+ # Ouch. Not good. Print the error.
+ print $OUT $@;
+ next CMD;
}
+ $obj->pat($inpat);
+ } ## end if ($inpat ne "")
- # Scan forward, stopping at either the end or the next
- # unbreakable line.
- for ( my $i = $line + 1 ; $i <= $max && $dbline[$i] == 0 ; ++$i )
- { #{ vi
+ # Where we are now is where to stop after wraparound.
+ $end = $start;
- # Drop out on null statements, block closers, and comments.
- last if $dbline[$i] =~ /^\s*[\;\}\#\n]/;
+ # Don't move away from this line.
+ $incr = -1;
- # Drop out if the user interrupted us.
- last if $signal;
+ my $pat = $obj->pat;
+ # Search inside the eval to prevent pattern badness
+ # from killing us.
+ eval {
+ no strict q/vars/;
+ for (;;) {
+ # Back up a line.
+ --$start;
- # Append a newline if the line doesn't have one. Can happen
- # in eval'ed text, for instance.
- $after = ( $dbline[$i] =~ /\n$/ ? '' : "\n" );
+ # Wrap if we pass the first line.
- # Next executable line.
- my $incr_pos = "$prefix$i$infix$dbline[$i]$after";
- $position .= $incr_pos;
- if ($frame) {
+ $start = $max if ($start <= 0);
- # Print it indented if tracing is on.
- print_lineinfo( ' ' x $stack_depth,
- "$i:\t$dbline[$i]$after" );
- }
- else {
- depth_print_lineinfo($explicit_stop, $incr_pos);
+ # Quit if we get back where we started,
+ last if ($start == $end);
+
+ # Match?
+ if ($dbline[$start] =~ m/$pat/i) {
+ if ($slave_editor) {
+ # Yep, follow slave editor requirements.
+ print $OUT "\032\032$filename:$start:0\n";
+ }
+ else {
+ # Yep, just print normally.
+ print $OUT "$start:\t",$dbline[$start],"\n";
+ }
+
+ # Found, so done.
+ last;
}
- } ## end for ($i = $line + 1 ; $i...
- } ## end else [ if ($slave_editor)
- } ## end if ($single || ($trace...
+ }
+ };
-=pod
+ # Say we failed if the loop never found anything,
+ if ( $start == $end ) {
+ print {$OUT} "?$pat?: not found\n";
+ }
+ next CMD;
+ }
-If there's an action to be executed for the line we stopped at, execute it.
-If there are any preprompt actions, execute those as well.
+ return;
+}
-=cut
+sub _DB__handle_restart_and_rerun_commands {
+ my ($obj) = @_;
- # If there's an action, do it now.
- $evalarg = $action, &eval if $action;
+ # R - restart execution.
+ # rerun - controlled restart execution.
+ if (my ($cmd_cmd, $cmd_params) =
+ $cmd =~ /\A((?:R)|(?:rerun\s*(.*)))\z/) {
+ my @args = ($cmd_cmd eq 'R' ? restart() : rerun($cmd_params));
- # Are we nested another level (e.g., did we evaluate a function
- # that had a breakpoint in it at the debugger prompt)?
- if ( $single || $was_signal ) {
+ # Close all non-system fds for a clean restart. A more
+ # correct method would be to close all fds that were not
+ # open when the process started, but this seems to be
+ # hard. See "debugger 'R'estart and open database
+ # connections" on p5p.
- # Yes, go down a level.
- local $level = $level + 1;
+ my $max_fd = 1024; # default if POSIX can't be loaded
+ if (eval { require POSIX }) {
+ eval { $max_fd = POSIX::sysconf(POSIX::_SC_OPEN_MAX()) };
+ }
- # Do any pre-prompt actions.
- foreach $evalarg (@$pre) {
- &eval;
+ if (defined $max_fd) {
+ foreach ($^F+1 .. $max_fd-1) {
+ next unless open FD_TO_CLOSE, "<&=$_";
+ close(FD_TO_CLOSE);
+ }
}
- # Complain about too much recursion if we passed the limit.
- print $OUT $stack_depth . " levels deep in subroutine calls!\n"
- if $single & 4;
+ # And run Perl again. We use exec() to keep the
+ # PID stable (and that way $ini_pids is still valid).
+ exec(@args) or print {$OUT} "exec failed: $!\n";
+
+ last CMD;
+ }
+
+ return;
+}
+
+sub _DB__handle_run_command_in_pager_command {
+ my ($obj) = @_;
+
+ if ($cmd =~ m#\A\|\|?\s*[^|]#) {
+ if ( $pager =~ /^\|/ ) {
+
+ # Default pager is into a pipe. Redirect I/O.
+ open( SAVEOUT, ">&STDOUT" )
+ || DB::warn("Can't save STDOUT");
+ open( STDOUT, ">&OUT" )
+ || DB::warn("Can't redirect STDOUT");
+ } ## end if ($pager =~ /^\|/)
+ else {
+
+ # Not into a pipe. STDOUT is safe.
+ open( SAVEOUT, ">&OUT" ) || &warn("Can't save DB::OUT");
+ }
+
+ # Fix up environment to record we have less if so.
+ fix_less();
+
+ unless ( $obj->piped(scalar ( open( OUT, $pager ) ) ) ) {
+
+ # Couldn't open pipe to pager.
+ DB::warn("Can't pipe output to '$pager'");
+ if ( $pager =~ /^\|/ ) {
+
+ # Redirect I/O back again.
+ open( OUT, ">&STDOUT" ) # XXX: lost message
+ || DB::warn("Can't restore DB::OUT");
+ open( STDOUT, ">&SAVEOUT" )
+ || DB::warn("Can't restore STDOUT");
+ close(SAVEOUT);
+ } ## end if ($pager =~ /^\|/)
+ else {
+
+ # Redirect I/O. STDOUT already safe.
+ open( OUT, ">&STDOUT" ) # XXX: lost message
+ || DB::warn("Can't restore DB::OUT");
+ }
+ next CMD;
+ } ## end unless ($piped = open(OUT,...
+
+ # Set up broken-pipe handler if necessary.
+ $SIG{PIPE} = \&DB::catch
+ if $pager =~ /^\|/
+ && ( "" eq $SIG{PIPE} || "DEFAULT" eq $SIG{PIPE} );
+
+ OUT->autoflush(1);
+ # Save current filehandle, and put it back.
+ $obj->selected(scalar( select(OUT) ));
+ # Don't put it back if pager was a pipe.
+ if ($cmd !~ /\A\|\|/)
+ {
+ select($obj->selected());
+ $obj->selected("");
+ }
+
+ # Trim off the pipe symbols and run the command now.
+ $cmd =~ s#\A\|+\s*##;
+ redo PIPE;
+ }
+
+ return;
+}
+
+sub _DB__handle_m_command {
+ my ($obj) = @_;
+
+ if ($cmd =~ s#\Am\s+([\w:]+)\s*\z# #) {
+ methods($1);
+ next CMD;
+ }
+
+ # m expr - set up DB::eval to do the work
+ if ($cmd =~ s#\Am\b# #) { # Rest gets done by DB::eval()
+ $onetimeDump = 'methods'; # method output gets used there
+ }
+
+ return;
+}
+
+sub _DB__at_end_of_every_command {
+ my ($obj) = @_;
+
+ # At the end of every command:
+ if ($obj->piped) {
+
+ # Unhook the pipe mechanism now.
+ if ( $pager =~ /^\|/ ) {
+
+ # No error from the child.
+ $? = 0;
+
+ # we cannot warn here: the handle is missing --tchrist
+ close(OUT) || print SAVEOUT "\nCan't close DB::OUT\n";
+
+ # most of the $? crud was coping with broken cshisms
+ # $? is explicitly set to 0, so this never runs.
+ 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 {
+ print SAVEOUT "status ", ( $? >> 8 ), "\n";
+ }
+ } ## end if ($?)
+
+ # Reopen filehandle for our output (if we can) and
+ # restore STDOUT (if we can).
+ open( OUT, ">&STDOUT" ) || &warn("Can't restore DB::OUT");
+ open( STDOUT, ">&SAVEOUT" )
+ || &warn("Can't restore STDOUT");
+
+ # Turn off pipe exception handler if necessary.
+ $SIG{PIPE} = "DEFAULT" if $SIG{PIPE} eq \&DB::catch;
+
+ # Will stop ignoring SIGPIPE if done like nohup(1)
+ # does SIGINT but Perl doesn't give us a choice.
+ } ## end if ($pager =~ /^\|/)
+ else {
+
+ # Non-piped "pager". Just restore STDOUT.
+ open( OUT, ">&SAVEOUT" ) || &warn("Can't restore DB::OUT");
+ }
+
+ # Close filehandle pager was using, restore the normal one
+ # if necessary,
+ close(SAVEOUT);
+
+ if ($obj->selected() ne "") {
+ select($obj->selected);
+ $obj->selected("");
+ }
+
+ # No pipes now.
+ $obj->piped("");
+ } ## end if ($piped)
+
+ return;
+}
+
+sub DB {
+
+ # lock the debugger and get the thread id for the prompt
+ lock($DBGR);
+ my $tid;
+ my $position;
+ my ($prefix, $after, $infix);
+ my $pat;
+ my $explicit_stop;
+ my $piped;
+ my $selected;
+
+ if ($ENV{PERL5DB_THREADED}) {
+ $tid = eval { "[".threads->tid."]" };
+ }
+
+ my $i;
+
+ my $obj = DB::Obj->new(
+ {
+ position => \$position,
+ prefix => \$prefix,
+ after => \$after,
+ explicit_stop => \$explicit_stop,
+ infix => \$infix,
+ i_cmd => \$i,
+ pat => \$pat,
+ piped => \$piped,
+ selected => \$selected,
+ },
+ );
+
+ $obj->_DB_on_init__initialize_globals(@_);
+
+ # Preserve current values of $@, $!, $^E, $,, $/, $\, $^W.
+ # The code being debugged may have altered them.
+ &save;
+
+ # Since DB::DB gets called after every line, we can use caller() to
+ # figure out where we last were executing. Sneaky, eh? This works because
+ # caller is returning all the extra information when called from the
+ # debugger.
+ local ( $package, $filename, $line ) = caller;
+ $filename_ini = $filename;
+
+ # set up the context for DB::eval, so it can properly execute
+ # code on behalf of the user. We add the package in so that the
+ # code is eval'ed in the proper package (not in the debugger!).
+ local $usercontext = _calc_usercontext($package);
+
+ # Create an alias to the active file magical array to simplify
+ # the code here.
+ local (*dbline) = $main::{ '_<' . $filename };
+
+ # Last line in the program.
+ $max = $#dbline;
+
+ _DB__determine_if_we_should_break(@_);
+
+ # Preserve the current stop-or-not, and see if any of the W
+ # (watch expressions) has changed.
+ my $was_signal = $signal;
+
+ # If we have any watch expressions ...
+ $obj->_DB__handle_watch_expressions(@_);
+
+=head2 C<watchfunction()>
+
+C<watchfunction()> is a function that can be defined by the user; it is a
+function which will be run on each entry to C<DB::DB>; it gets the
+current package, filename, and line as its parameters.
+
+The watchfunction can do anything it likes; it is executing in the
+debugger's context, so it has access to all of the debugger's internal
+data structures and functions.
+
+C<watchfunction()> can control the debugger's actions. Any of the following
+will cause the debugger to return control to the user's program after
+C<watchfunction()> executes:
+
+=over 4
+
+=item *
+
+Returning a false value from the C<watchfunction()> itself.
+
+=item *
+
+Altering C<$single> to a false value.
+
+=item *
+
+Altering C<$signal> to a false value.
+
+=item *
+
+Turning off the C<4> bit in C<$trace> (this also disables the
+check for C<watchfunction()>. This can be done with
+
+ $trace &= ~4;
+
+=back
+
+=cut
+
+ # If there's a user-defined DB::watchfunction, call it with the
+ # current package, filename, and line. The function executes in
+ # the DB:: package.
+ if ( $trace & 4 ) { # User-installed watch
+ return
+ if watchfunction( $package, $filename, $line )
+ and not $single
+ and not $was_signal
+ and not( $trace & ~4 );
+ } ## end if ($trace & 4)
+
+ # Pick up any alteration to $signal in the watchfunction, and
+ # turn off the signal now.
+ $was_signal = $signal;
+ $signal = 0;
+
+=head2 GETTING READY TO EXECUTE COMMANDS
+
+The debugger decides to take control if single-step mode is on, the
+C<t> command was entered, or the user generated a signal. If the program
+has fallen off the end, we set things up so that entering further commands
+won't cause trouble, and we say that the program is over.
+
+=cut
+
+ # Make sure that we always print if asked for explicitly regardless
+ # of $trace_to_depth .
+ $explicit_stop = ($single || $was_signal);
+
+ # Check to see if we should grab control ($single true,
+ # trace set appropriately, or we got a signal).
+ if ( $explicit_stop || ( $trace & 1 ) ) {
+ $obj->_DB__grab_control(@_);
+ } ## end if ($single || ($trace...
+
+=pod
+
+If there's an action to be executed for the line we stopped at, execute it.
+If there are any preprompt actions, execute those as well.
+
+=cut
+
+ # If there's an action, do it now.
+ if ($action) {
+ $evalarg = $action;
+ DB::eval();
+ }
+
+ # Are we nested another level (e.g., did we evaluate a function
+ # that had a breakpoint in it at the debugger prompt)?
+ if ( $single || $was_signal ) {
+
+ # Yes, go down a level.
+ local $level = $level + 1;
+
+ # Do any pre-prompt actions.
+ foreach $evalarg (@$pre) {
+ DB::eval();
+ }
+
+ # Complain about too much recursion if we passed the limit.
+ if ($single & 4) {
+ print $OUT $stack_depth . " levels deep in subroutine calls!\n";
+ }
# The line we're currently on. Set $incr to -1 to stay here
# until we get a command that tells us to advance.
#
# If we have a terminal for input, and we get something back
# from readline(), keep on processing.
- my $piped;
- my $selected;
CMD:
- while (
-
- # We have a terminal, or can get one ...
- ( $term || &setterm ),
-
- # ... and it belogs to this PID or we get one for this PID ...
- ( $term_pid == $$ or resetterm(1) ),
-
- # ... and we got a line of command input ...
- defined(
- $cmd = &readline(
- "$pidprompt $tid DB"
- . ( '<' x $level )
- . ( $#hist + 1 )
- . ( '>' x $level ) . " "
- )
- )
- )
+ while (_DB__read_next_cmd($tid))
{
- share($cmd);
+ share($cmd);
# ... try to execute the input as debugger commands.
# Don't stop running.
# Handle continued commands (ending with \):
if ($cmd =~ s/\\\z/\n/) {
- $cmd .= &readline(" cont: ");
+ $cmd .= DB::readline(" cont: ");
redo CMD;
}
=cut
# Empty input means repeat the last command.
- $cmd =~ /^$/ && ( $cmd = $laststep );
+ if ($cmd eq '') {
+ $cmd = $laststep;
+ }
chomp($cmd); # get rid of the annoying extra newline
- push( @hist, $cmd ) if length($cmd) > 1;
+ if (length($cmd) >= 2) {
+ push( @hist, $cmd );
+ }
push( @truehist, $cmd );
- share(@hist);
- share(@truehist);
+ share(@hist);
+ share(@truehist);
# This is a restart point for commands that didn't arrive
# via direct user input. It allows us to 'redo PIPE' to
# re-execute command processing without reading a new command.
PIPE: {
- $cmd =~ s/^\s+//s; # trim annoying leading whitespace
- $cmd =~ s/\s+$//s; # trim annoying trailing whitespace
- my ($i) = split( /\s+/, $cmd );
+ $i = _DB__trim_command_and_return_first_component();
=head3 COMMAND ALIASES
=cut
- if ($cmd eq 'q') {
- $fall_off_end = 1;
- clean_ENV();
- exit $?;
- }
+ $obj->_handle_q_command;
=head4 C<t> - trace [n]
=cut
- if (my ($levels) = $cmd =~ /\At(?:\s+(\d+))?\z/) {
- $trace ^= 1;
- local $\ = '';
- $trace_to_depth = $levels ? $stack_depth + $levels : 1E9;
- print $OUT "Trace = "
- . ( ( $trace & 1 )
- ? ( $levels ? "on (to level $trace_to_depth)" : "on" )
- : "off" ) . "\n";
- next CMD;
- }
+ $obj->_handle_t_command;
=head4 C<S> - list subroutines matching/not matching a pattern
=cut
- if (my ($print_all_subs, $should_reverse, $Spatt)
- = $cmd =~ /\AS(\s+(!)?(.+))?\z/) {
- # $Spatt is the pattern (if any) to use.
- # Reverse scan?
- my $Srev = defined $should_reverse;
- # No args - print all subs.
- my $Snocheck = !defined $print_all_subs;
-
- # Need to make these sane here.
- local $\ = '';
- local $, = '';
-
- # Search through the debugger's magical hash of subs.
- # If $nocheck is true, just print the sub name.
- # Otherwise, check it against the pattern. We then use
- # the XOR trick to reverse the condition as required.
- foreach $subname ( sort( keys %sub ) ) {
- if ( $Snocheck or $Srev ^ ( $subname =~ /$Spatt/ ) ) {
- print $OUT $subname, "\n";
- }
- }
- next CMD;
- }
+ $obj->_handle_S_command;
=head4 C<X> - list variables in current package
Since the C<V> command actually processes this, just change this to the
appropriate C<V> command and fall through.
-=cut
-
- $cmd =~ s/^X\b/V $package/;
-
=head4 C<V> - list variables
Uses C<dumpvar.pl> to dump out the current values for selected variables.
=cut
- # Bare V commands get the currently-being-debugged package
- # added.
- if ($cmd eq "V") {
- $cmd = "V $package";
- }
-
- # V - show variables in package.
- if (my ($new_packname, $new_vars_str) =
- $cmd =~ /\AV\b\s*(\S+)\s*(.*)/) {
-
- # Save the currently selected filehandle and
- # force output to debugger's filehandle (dumpvar
- # just does "print" for output).
- my $savout = select($OUT);
-
- # Grab package name and variables to dump.
- $packname = $new_packname;
- my @vars = split( ' ', $new_vars_str );
-
- # If main::dumpvar isn't here, get it.
- do 'dumpvar.pl' || die $@ unless defined &main::dumpvar;
- if ( defined &main::dumpvar ) {
-
- # We got it. Turn off subroutine entry/exit messages
- # for the moment, along with return values.
- local $frame = 0;
- local $doret = -2;
-
- # must detect sigpipe failures - not catching
- # then will cause the debugger to die.
- eval {
- &main::dumpvar(
- $packname,
- defined $option{dumpDepth}
- ? $option{dumpDepth}
- : -1, # assume -1 unless specified
- @vars
- );
- };
-
- # The die doesn't need to include the $@, because
- # it will automatically get propagated for us.
- if ($@) {
- die unless $@ =~ /dumpvar print failed/;
- }
- } ## end if (defined &main::dumpvar)
- else {
-
- # Couldn't load dumpvar.
- print $OUT "dumpvar.pl not available.\n";
- }
-
- # Restore the output filehandle, and go round again.
- select($savout);
- next CMD;
- }
+ $obj->_handle_V_command_and_X_command;
=head4 C<x> - evaluate and print an expression
=cut
- if ($cmd =~ s#\Ax\b# #) { # Remainder gets done by DB::eval()
- $onetimeDump = 'dump'; # main::dumpvar shows the output
-
- # handle special "x 3 blah" syntax XXX propagate
- # doc back to special variables.
- if ( $cmd =~ s#\A\s*(\d+)(?=\s)# #) {
- $onetimedumpDepth = $1;
- }
- }
+ $obj->_handle_x_command;
=head4 C<m> - print methods
=cut
- if ($cmd =~ s#\Am\s+([\w:]+)\s*\z# #) {
- methods($1);
- next CMD;
- }
-
- # m expr - set up DB::eval to do the work
- if ($cmd =~ s#\Am\b# #) { # Rest gets done by DB::eval()
- $onetimeDump = 'methods'; # method output gets used there
- }
+ _DB__handle_m_command($obj);
=head4 C<f> - switch files
=cut
- if (($file) = $cmd =~ /\Af\b\s*(.*)/) {
- $file =~ s/\s+$//;
-
- # help for no arguments (old-style was return from sub).
- if ( !$file ) {
- print $OUT
- "The old f command is now the r command.\n"; # hint
- print $OUT "The new f command switches filenames.\n";
- next CMD;
- } ## end if (!$file)
-
- # if not in magic file list, try a close match.
- if ( !defined $main::{ '_<' . $file } ) {
- if ( ($try) = grep( m#^_<.*$file#, keys %main:: ) ) {
- {
- $try = substr( $try, 2 );
- print $OUT "Choosing $try matching '$file':\n";
- $file = $try;
- }
- } ## end if (($try) = grep(m#^_<.*$file#...
- } ## end if (!defined $main::{ ...
-
- # If not successfully switched now, we failed.
- if ( !defined $main::{ '_<' . $file } ) {
- print $OUT "No file matching '$file' is loaded.\n";
- next CMD;
- }
-
- # We switched, so switch the debugger internals around.
- elsif ( $file ne $filename ) {
- *dbline = $main::{ '_<' . $file };
- $max = $#dbline;
- $filename = $file;
- $start = 1;
- $cmd = "l";
- } ## end elsif ($file ne $filename)
-
- # We didn't switch; say we didn't.
- else {
- print $OUT "Already in $file.\n";
- next CMD;
- }
- }
+ _DB__handle_f_command($obj);
=head4 C<.> - return to last-executed line.
=cut
- # . command.
- if ($cmd eq '.') {
- $incr = -1; # stay at current line
-
- # Reset everything to the old location.
- $start = $line;
- $filename = $filename_ini;
- *dbline = $main::{ '_<' . $filename };
- $max = $#dbline;
-
- # Now where are we?
- print_lineinfo($position);
- next CMD;
- }
+ _DB__handle_dot_command($obj);
=head4 C<-> - back one window
=cut
# - - back a window.
- if ($cmd eq '-') {
-
- # back up by a window; go to 1 if back too far.
- $start -= $incr + $window + 1;
- $start = 1 if $start <= 0;
- $incr = $window - 1;
-
- # Generate and execute a "l +" command (handled below).
- $cmd = 'l ' . ($start) . '+';
- }
+ $obj->_handle_dash_command;
-=head3 PRE-580 COMMANDS VS. NEW COMMANDS: C<a, A, b, B, h, l, L, M, o, O, P, v, w, W, E<lt>, E<lt>E<lt>, {, {{>
+=head3 PRE-580 COMMANDS VS. NEW COMMANDS: C<a, A, b, B, h, l, L, M, o, O, P, v, w, W, E<lt>, E<lt>E<lt>, E<0x7B>, E<0x7B>E<0x7B>>
In Perl 5.8.0, a realignment of the commands was done to fix up a number of
problems, most notably that the default case of several commands destroying
# All of these commands were remapped in perl 5.8.0;
# we send them off to the secondary dispatcher (see below).
- if (my ($cmd_letter, $my_arg) = $cmd =~ /\A([aAbBeEhilLMoOPvwW]\b|[<>\{]{1,2})\s*(.*)/so) {
- &cmd_wrapper( $cmd_letter, $my_arg, $line );
- next CMD;
- }
+ $obj->_handle_cmd_wrapper_commands;
=head4 C<y> - List lexicals in higher scope
=cut
- if (my ($match_level, $match_vars)
- = $cmd =~ /^y(?:\s+(\d*)\s*(.*))?$/) {
-
- # See if we've got the necessary support.
- eval { require PadWalker; PadWalker->VERSION(0.08) }
- or &warn(
- $@ =~ /locate/
- ? "PadWalker module not found - please install\n"
- : $@
- )
- and next CMD;
-
- # Load up dumpvar if we don't have it. If we can, that is.
- do 'dumpvar.pl' || die $@ unless defined &main::dumpvar;
- defined &main::dumpvar
- or print $OUT "dumpvar.pl not available.\n"
- and next CMD;
-
- # Got all the modules we need. Find them and print them.
- my @vars = split( ' ', $match_vars || '' );
-
- # Find the pad.
- my $h = eval { PadWalker::peek_my( ( $match_level || 0 ) + 1 ) };
-
- # Oops. Can't find it.
- $@ and $@ =~ s/ at .*//, &warn($@), next CMD;
-
- # Show the desired vars with dumplex().
- my $savout = select($OUT);
-
- # Have dumplex dump the lexicals.
- dumpvar::dumplex( $_, $h->{$_},
- defined $option{dumpDepth} ? $option{dumpDepth} : -1,
- @vars )
- for sort keys %$h;
- select($savout);
- next CMD;
- }
+ _DB__handle_y_command($obj);
=head3 COMMANDS NOT WORKING AFTER PROGRAM ENDS
=cut
# n - next
- if ($cmd eq 'n') {
- end_report(), next CMD if $finished and $level <= 1;
-
- # Single step, but don't enter subs.
- $single = 2;
-
- # Save for empty command (repeat last).
- $laststep = $cmd;
- last CMD;
- }
+ $obj->_handle_n_command;
=head4 C<s> - single-step, entering subs
=cut
- # s - single step.
- if ($cmd eq 's') {
-
- # Get out and restart the command loop if program
- # has finished.
- end_report(), next CMD if $finished and $level <= 1;
-
- # Single step should enter subs.
- $single = 1;
-
- # Save for empty command (repeat last).
- $laststep = $cmd;
- last CMD;
- }
+ $obj->_handle_s_command;
=head4 C<c> - run continuously, setting an optional breakpoint
=cut
# c - start continuous execution.
- if (($i) = $cmd =~ m#\Ac\b\s*([\w:]*)\s*\z#) {
-
- # Hey, show's over. The debugged program finished
- # executing already.
- end_report(), next CMD if $finished and $level <= 1;
-
- # Capture the place to put a one-time break.
- $subname = $i;
-
- # Probably not needed, since we finish an interactive
- # sub-session anyway...
- # local $filename = $filename;
- # local *dbline = *dbline; # XXX Would this work?!
- #
- # The above question wonders if localizing the alias
- # to the magic array works or not. Since it's commented
- # out, we'll just leave that to speculation for now.
-
- # If the "subname" isn't all digits, we'll assume it
- # is a subroutine name, and try to find it.
- if ( $subname =~ /\D/ ) { # subroutine name
- # Qualify it to the current package unless it's
- # already qualified.
- $subname = $package . "::" . $subname
- unless $subname =~ /::/;
-
- # find_sub will return "file:line_number" corresponding
- # to where the subroutine is defined; we call find_sub,
- # break up the return value, and assign it in one
- # operation.
- ( $file, $i ) = ( find_sub($subname) =~ /^(.*):(.*)$/ );
-
- # Force the line number to be numeric.
- $i += 0;
-
- # If we got a line number, we found the sub.
- if ($i) {
-
- # Switch all the debugger's internals around so
- # we're actually working with that file.
- $filename = $file;
- *dbline = $main::{ '_<' . $filename };
-
- # Mark that there's a breakpoint in this file.
- $had_breakpoints{$filename} |= 1;
-
- # Scan forward to the first executable line
- # after the 'sub whatever' line.
- $max = $#dbline;
- ++$i while $dbline[$i] == 0 && $i < $max;
- } ## end if ($i)
-
- # We didn't find a sub by that name.
- else {
- print $OUT "Subroutine $subname not found.\n";
- next CMD;
- }
- } ## end if ($subname =~ /\D/)
-
- # At this point, either the subname was all digits (an
- # absolute line-break request) or we've scanned through
- # the code following the definition of the sub, looking
- # for an executable, which we may or may not have found.
- #
- # If $i (which we set $subname from) is non-zero, we
- # got a request to break at some line somewhere. On
- # one hand, if there wasn't any real subroutine name
- # involved, this will be a request to break in the current
- # file at the specified line, so we have to check to make
- # sure that the line specified really is breakable.
- #
- # On the other hand, if there was a subname supplied, the
- # preceding block has moved us to the proper file and
- # location within that file, and then scanned forward
- # looking for the next executable line. We have to make
- # sure that one was found.
- #
- # On the gripping hand, we can't do anything unless the
- # current value of $i points to a valid breakable line.
- # Check that.
- if ($i) {
-
- # Breakable?
- if ( $dbline[$i] == 0 ) {
- print $OUT "Line $i not breakable.\n";
- next CMD;
- }
-
- # Yes. Set up the one-time-break sigil.
- $dbline{$i} =~ s/($|\0)/;9$1/; # add one-time-only b.p.
- _enable_breakpoint_temp_enabled_status($filename, $i);
- } ## end if ($i)
-
- # Turn off stack tracing from here up.
- for my $i (0 .. $stack_depth) {
- $stack[ $i ] &= ~1;
- }
- last CMD;
- }
+ _DB__handle_c_command($obj);
=head4 C<r> - return from a subroutine
=cut
# r - return from the current subroutine.
- if ($cmd eq 'r') {
+ $obj->_handle_r_command;
- # Can't do anything if the program's over.
- end_report(), next CMD if $finished and $level <= 1;
-
- # Turn on stack trace.
- $stack[$stack_depth] |= 1;
-
- # Print return value unless the stack is empty.
- $doret = $option{PrintRet} ? $stack_depth - 1 : -2;
- last CMD;
- }
-
-=head4 C<T> - stack trace
+=head4 C<T> - stack trace
Just calls C<DB::print_trace>.
=cut
- if ($cmd eq 'T') {
- print_trace( $OUT, 1 ); # skip DB
- next CMD;
- }
+ $obj->_handle_T_command;
=head4 C<w> - List window around current line.
=cut
- if (my ($arg) = $cmd =~ /\Aw\b\s*(.*)/s) {
- &cmd_w( 'w', $arg );
- next CMD;
- }
+ $obj->_handle_w_command;
=head4 C<W> - watch-expression processing.
=cut
- if (my ($arg) = $cmd =~ /\AW\b\s*(.*)/s) {
- &cmd_W( 'W', $arg );
- next CMD;
- }
+ $obj->_handle_W_command;
=head4 C</> - search forward for a string in the source
=cut
- # The pattern as a string.
- use vars qw($inpat);
-
- if (($inpat) = $cmd =~ m#\A/(.*)\z#) {
-
- # Remove the final slash.
- $inpat =~ s:([^\\])/$:$1:;
-
- # If the pattern isn't null ...
- if ( $inpat ne "" ) {
-
- # Turn of warn and die procesing for a bit.
- local $SIG{__DIE__};
- local $SIG{__WARN__};
-
- # Create the pattern.
- eval '$inpat =~ m' . "\a$inpat\a";
- if ( $@ ne "" ) {
-
- # Oops. Bad pattern. No biscuit.
- # Print the eval error and go back for more
- # commands.
- print $OUT "$@";
- next CMD;
- }
- $pat = $inpat;
- } ## end if ($inpat ne "")
-
- # Set up to stop on wrap-around.
- $end = $start;
-
- # Don't move off the current line.
- $incr = -1;
-
- # Done in eval so nothing breaks if the pattern
- # does something weird.
- eval '
- for (;;) {
- # Move ahead one line.
- ++$start;
-
- # Wrap if we pass the last line.
- $start = 1 if ($start > $max);
-
- # Stop if we have gotten back to this line again,
- last if ($start == $end);
-
- # A hit! (Note, though, that we are doing
- # case-insensitive matching. Maybe a qr//
- # expression would be better, so the user could
- # do case-sensitive matching if desired.
- if ($dbline[$start] =~ m' . "\a$pat\a" . 'i) {
- if ($slave_editor) {
- # Handle proper escaping in the slave.
- print $OUT "\032\032$filename:$start:0\n";
- }
- else {
- # Just print the line normally.
- print $OUT "$start:\t",$dbline[$start],"\n";
- }
- # And quit since we found something.
- last;
- }
- } ';
-
- # If we wrapped, there never was a match.
- print $OUT "/$pat/: not found\n" if ( $start == $end );
- next CMD;
- }
+ _DB__handle_forward_slash_command($obj);
=head4 C<?> - search backward for a string in the source
=cut
- # ? - backward pattern search.
- if (my ($inpat) = $cmd =~ m#\A\?(.*)\z#) {
+ _DB__handle_question_mark_command($obj);
- # Get the pattern, remove trailing question mark.
- $inpat =~ s:([^\\])\?$:$1:;
+=head4 C<$rc> - Recall command
- # If we've got one ...
- if ( $inpat ne "" ) {
+Manages the commands in C<@hist> (which is created if C<Term::ReadLine> reports
+that the terminal supports history). It find the the command required, puts it
+into C<$cmd>, and redoes the loop to execute it.
- # Turn off die & warn handlers.
- local $SIG{__DIE__};
- local $SIG{__WARN__};
- eval '$inpat =~ m' . "\a$inpat\a";
+=cut
- if ( $@ ne "" ) {
+ # $rc - recall command.
+ $obj->_handle_rc_recall_command;
- # Ouch. Not good. Print the error.
- print $OUT $@;
- next CMD;
- }
- $pat = $inpat;
- } ## end if ($inpat ne "")
+=head4 C<$sh$sh> - C<system()> command
- # Where we are now is where to stop after wraparound.
- $end = $start;
+Calls the C<DB::system()> to handle the command. This keeps the C<STDIN> and
+C<STDOUT> from getting messed up.
- # Don't move away from this line.
- $incr = -1;
+=cut
- # Search inside the eval to prevent pattern badness
- # from killing us.
- eval '
- for (;;) {
- # Back up a line.
- --$start;
+ $obj->_handle_sh_command;
- # Wrap if we pass the first line.
+=head4 C<$rc I<pattern> $rc> - Search command history
- $start = $max if ($start <= 0);
+Another command to manipulate C<@hist>: this one searches it with a pattern.
+If a command is found, it is placed in C<$cmd> and executed via C<redo>.
- # Quit if we get back where we started,
- last if ($start == $end);
+=cut
- # Match?
- if ($dbline[$start] =~ m' . "\a$pat\a" . 'i) {
- if ($slave_editor) {
- # Yep, follow slave editor requirements.
- print $OUT "\032\032$filename:$start:0\n";
- }
- else {
- # Yep, just print normally.
- print $OUT "$start:\t",$dbline[$start],"\n";
- }
+ $obj->_handle_rc_search_history_command;
- # Found, so done.
- last;
- }
- } ';
+=head4 C<$sh> - Invoke a shell
- # Say we failed if the loop never found anything,
- print $OUT "?$pat?: not found\n" if ( $start == $end );
- next CMD;
- }
+Uses C<DB::system> to invoke a shell.
-=head4 C<$rc> - Recall command
+=cut
-Manages the commands in C<@hist> (which is created if C<Term::ReadLine> reports
-that the terminal supports history). It find the the command required, puts it
-into C<$cmd>, and redoes the loop to execute it.
+=head4 C<$sh I<command>> - Force execution of a command in a shell
+
+Like the above, but the command is passed to the shell. Again, we use
+C<DB::system> to avoid problems with C<STDIN> and C<STDOUT>.
+
+=head4 C<H> - display commands in history
+
+Prints the contents of C<@hist> (if any).
=cut
- # $rc - recall command.
- if (my ($minus, $arg) = $cmd =~ m#\A$rc+\s*(-)?(\d+)?\z#) {
+ $obj->_handle_H_command;
- # No arguments, take one thing off history.
- pop(@hist) if length($cmd) > 1;
+=head4 C<man, doc, perldoc> - look up documentation
- # Relative (- found)?
- # Y - index back from most recent (by 1 if bare minus)
- # N - go to that particular command slot or the last
- # thing if nothing following.
- $i = $minus ? ( $#hist - ( $arg || 1 ) ) : ( $arg || $#hist );
+Just calls C<runman()> to print the appropriate document.
- # Pick out the command desired.
- $cmd = $hist[$i];
+=cut
- # Print the command to be executed and restart the loop
- # with that command in the buffer.
- print $OUT $cmd, "\n";
- redo CMD;
- }
+ $obj->_handle_doc_command;
-=head4 C<$sh$sh> - C<system()> command
+=head4 C<p> - print
-Calls the C<DB::system()> to handle the command. This keeps the C<STDIN> and
-C<STDOUT> from getting messed up.
+Builds a C<print EXPR> expression in the C<$cmd>; this will get executed at
+the bottom of the loop.
=cut
- # $sh$sh - run a shell command (if it's all ASCII).
- # Can't run shell commands with Unicode in the debugger, hmm.
- if (my ($arg) = $cmd =~ m#\A$sh$sh\s*(.*)#ms) {
-
- # System it.
- &system($arg);
- next CMD;
- }
+ $obj->_handle_p_command;
-=head4 C<$rc I<pattern> $rc> - Search command history
+=head4 C<=> - define command alias
-Another command to manipulate C<@hist>: this one searches it with a pattern.
-If a command is found, it is placed in C<$cmd> and executed via C<redo>.
+Manipulates C<%alias> to add or list command aliases.
=cut
- # $rc pattern $rc - find a command in the history.
- if (my ($arg) = $cmd =~ /\A$rc([^$rc].*)\z/) {
+ # = - set up a command alias.
+ $obj->_handle_equal_sign_command;
- # Create the pattern to use.
- $pat = "^$arg";
+=head4 C<source> - read commands from a file.
- # Toss off last entry if length is >1 (and it always is).
- pop(@hist) if length($cmd) > 1;
+Opens a lexical filehandle and stacks it on C<@cmdfhs>; C<DB::readline> will
+pick it up.
- # Look backward through the history.
- for ( $i = $#hist ; $i ; --$i ) {
- # Stop if we find it.
- last if $hist[$i] =~ /$pat/;
- }
+=cut
- if ( !$i ) {
+ $obj->_handle_source_command;
- # Never found it.
- print $OUT "No such command!\n\n";
- next CMD;
- }
+=head4 C<enable> C<disable> - enable or disable breakpoints
- # Found it. Put it in the buffer, print it, and process it.
- $cmd = $hist[$i];
- print $OUT $cmd, "\n";
- redo CMD;
- }
+This enables or disables breakpoints.
-=head4 C<$sh> - Invoke a shell
+=cut
-Uses C<DB::system> to invoke a shell.
+ $obj->_handle_enable_disable_commands;
+
+=head4 C<save> - send current history to a file
+
+Takes the complete history, (not the shrunken version you see with C<H>),
+and saves it to the given filename, so it can be replayed using C<source>.
+
+Note that all C<^(save|source)>'s are commented out with a view to minimise recursion.
=cut
- # $sh - start a shell.
- if ($cmd =~ /\A$sh\z/) {
+ # save source - write commands to a file for later use
+ $obj->_handle_save_command;
- # Run the user's shell. If none defined, run Bourne.
- # We resume execution when the shell terminates.
- &system( $ENV{SHELL} || "/bin/sh" );
- next CMD;
- }
+=head4 C<R> - restart
-=head4 C<$sh I<command>> - Force execution of a command in a shell
+Restart the debugger session.
-Like the above, but the command is passed to the shell. Again, we use
-C<DB::system> to avoid problems with C<STDIN> and C<STDOUT>.
+=head4 C<rerun> - rerun the current session
+
+Return to any given position in the B<true>-history list
=cut
- # $sh command - start a shell and run a command in it.
- if (my ($arg) = $cmd =~ m#\A$sh\s*(.*)#ms) {
+ # R - restart execution.
+ # rerun - controlled restart execution.
+ _DB__handle_restart_and_rerun_commands($obj);
- # XXX: using csh or tcsh destroys sigint retvals!
- #&system($1); # use this instead
+=head4 C<|, ||> - pipe output through the pager.
- # use the user's shell, or Bourne if none defined.
- &system( $ENV{SHELL} || "/bin/sh", "-c", $arg );
- next CMD;
- }
+For C<|>, we save C<OUT> (the debugger's output filehandle) and C<STDOUT>
+(the program's standard output). For C<||>, we only save C<OUT>. We open a
+pipe to the pager (restoring the output filehandles if this fails). If this
+is the C<|> command, we also set up a C<SIGPIPE> handler which will simply
+set C<$signal>, sending us back into the debugger.
-=head4 C<H> - display commands in history
+We then trim off the pipe symbols and C<redo> the command loop at the
+C<PIPE> label, causing us to evaluate the command in C<$cmd> without
+reading another.
-Prints the contents of C<@hist> (if any).
+=cut
+
+ # || - run command in the pager, with output to DB::OUT.
+ _DB__handle_run_command_in_pager_command($obj);
+
+=head3 END OF COMMAND PARSING
+
+Anything left in C<$cmd> at this point is a Perl expression that we want to
+evaluate. We'll always evaluate in the user's context, and fully qualify
+any variables we might want to address in the C<DB> package.
=cut
- if ($cmd =~ /\AH\b\s*\*/) {
- @hist = @truehist = ();
- print $OUT "History cleansed\n";
- next CMD;
- }
+ } # PIPE:
- if (my ($num)
- = $cmd =~ /\AH\b\s*(?:-(\d+))?/) {
+ # Make sure the flag that says "the debugger's running" is
+ # still on, to make sure we get control again.
+ $evalarg = "\$^D = \$^D | \$DB::db_stop;\n$cmd";
- # Anything other than negative numbers is ignored by
- # the (incorrect) pattern, so this test does nothing.
- $end = $num ? ( $#hist - $num ) : 0;
+ # Run *our* eval that executes in the caller's context.
+ DB::eval();
+
+ # Turn off the one-time-dump stuff now.
+ if ($onetimeDump) {
+ $onetimeDump = undef;
+ $onetimedumpDepth = undef;
+ }
+ elsif ( $term_pid == $$ ) {
+ eval { # May run under miniperl, when not available...
+ STDOUT->flush();
+ STDERR->flush();
+ };
- # Set to the minimum if less than zero.
- $hist = 0 if $hist < 0;
+ # XXX If this is the master pid, print a newline.
+ print {$OUT} "\n";
+ }
+ } ## end while (($term || &setterm...
- # Start at the end of the array.
- # Stay in while we're still above the ending value.
- # Tick back by one each time around the loop.
- for ( $i = $#hist ; $i > $end ; $i-- ) {
+=head3 POST-COMMAND PROCESSING
- # Print the command unless it has no arguments.
- print $OUT "$i: ", $hist[$i], "\n"
- unless $hist[$i] =~ /^.?$/;
- }
- next CMD;
- }
+After each command, we check to see if the command output was piped anywhere.
+If so, we go through the necessary code to unhook the pipe and go back to
+our standard filehandles for input and output.
-=head4 C<man, doc, perldoc> - look up documentation
+=cut
-Just calls C<runman()> to print the appropriate document.
+ continue { # CMD:
+ _DB__at_end_of_every_command($obj);
+ } # CMD:
+
+=head3 COMMAND LOOP TERMINATION
+
+When commands have finished executing, we come here. If the user closed the
+input filehandle, we turn on C<$fall_off_end> to emulate a C<q> command. We
+evaluate any post-prompt items. We restore C<$@>, C<$!>, C<$^E>, C<$,>, C<$/>,
+C<$\>, and C<$^W>, and return a null list as expected by the Perl interpreter.
+The interpreter will then execute the next line and then return control to us
+again.
=cut
- # man, perldoc, doc - show manual pages.
- if (my ($man_page)
- = $cmd =~ /\A(?:man|(?:perl)?doc)\b(?:\s+([^(]*))?\z/) {
- runman($man_page);
- next CMD;
- }
+ # No more commands? Quit.
+ $fall_off_end = 1 unless defined $cmd; # Emulate 'q' on EOF
-=head4 C<p> - print
+ # Evaluate post-prompt commands.
+ foreach $evalarg (@$post) {
+ DB::eval();
+ }
+ } # if ($single || $signal)
-Builds a C<print EXPR> expression in the C<$cmd>; this will get executed at
-the bottom of the loop.
+ # Put the user's globals back where you found them.
+ ( $@, $!, $^E, $,, $/, $\, $^W ) = @saved;
+ ();
+} ## end sub DB
+
+package DB::Obj;
+
+sub new {
+ my $class = shift;
+
+ my $self = bless {}, $class;
+
+ $self->_init(@_);
+
+ return $self;
+}
+
+sub _init {
+ my ($self, $args) = @_;
+
+ %{$self} = (%$self, %$args);
+
+ return;
+}
+
+{
+ no strict 'refs';
+ foreach my $slot_name (qw(
+ after explicit_stop infix pat piped position prefix selected i_cmd
+ )) {
+ my $slot = $slot_name;
+ *{$slot} = sub {
+ my $self = shift;
+
+ if (@_) {
+ ${ $self->{$slot} } = shift;
+ }
+
+ return ${ $self->{$slot} };
+ };
+
+ *{"append_to_$slot"} = sub {
+ my $self = shift;
+ my $s = shift;
+
+ return $self->$slot($self->$slot . $s);
+ };
+ }
+}
+
+sub _DB_on_init__initialize_globals
+{
+ my $self = shift;
+
+ # Check for whether we should be running continuously or not.
+ # _After_ the perl program is compiled, $single is set to 1:
+ if ( $single and not $second_time++ ) {
+
+ # Options say run non-stop. Run until we get an interrupt.
+ if ($runnonstop) { # Disable until signal
+ # If there's any call stack in place, turn off single
+ # stepping into subs throughout the stack.
+ for my $i (0 .. $stack_depth) {
+ $stack[ $i ] &= ~1;
+ }
+
+ # And we are now no longer in single-step mode.
+ $single = 0;
+
+ # If we simply returned at this point, we wouldn't get
+ # the trace info. Fall on through.
+ # return;
+ } ## end if ($runnonstop)
+
+ elsif ($ImmediateStop) {
+
+ # We are supposed to stop here; XXX probably a break.
+ $ImmediateStop = 0; # We've processed it; turn it off
+ $signal = 1; # Simulate an interrupt to force
+ # us into the command loop
+ }
+ } ## end if ($single and not $second_time...
+
+ # If we're in single-step mode, or an interrupt (real or fake)
+ # has occurred, turn off non-stop mode.
+ $runnonstop = 0 if $single or $signal;
+
+ return;
+}
+
+sub _DB__handle_watch_expressions
+{
+ my $self = shift;
+
+ if ( $trace & 2 ) {
+ for my $n (0 .. $#to_watch) {
+ $evalarg = $to_watch[$n];
+ local $onetimeDump; # Tell DB::eval() to not output results
+
+ # Fix context DB::eval() wants to return an array, but
+ # we need a scalar here.
+ my ($val) = join( "', '", DB::eval() );
+ $val = ( ( defined $val ) ? "'$val'" : 'undef' );
+
+ # Did it change?
+ if ( $val ne $old_watch[$n] ) {
+
+ # Yep! Show the difference, and fake an interrupt.
+ $signal = 1;
+ print {$OUT} <<EOP;
+Watchpoint $n:\t$to_watch[$n] changed:
+ old value:\t$old_watch[$n]
+ new value:\t$val
+EOP
+ $old_watch[$n] = $val;
+ } ## end if ($val ne $old_watch...
+ } ## end for my $n (0 ..
+ } ## end if ($trace & 2)
+
+ return;
+}
+
+sub _my_print_lineinfo
+{
+ my ($self, $i, $incr_pos) = @_;
+
+ if ($frame) {
+ # Print it indented if tracing is on.
+ DB::print_lineinfo( ' ' x $stack_depth,
+ "$i:\t$DB::dbline[$i]" . $self->after );
+ }
+ else {
+ DB::depth_print_lineinfo($self->explicit_stop, $incr_pos);
+ }
+}
+
+sub _curr_line {
+ return $DB::dbline[$line];
+}
+
+sub _DB__grab_control
+{
+ my $self = shift;
+
+ # Yes, grab control.
+ if ($slave_editor) {
+
+ # Tell the editor to update its position.
+ $self->position("\032\032${DB::filename}:$line:0\n");
+ DB::print_lineinfo($self->position());
+ }
+
+=pod
+
+Special check: if we're in package C<DB::fake>, we've gone through the
+C<END> block at least once. We set up everything so that we can continue
+to enter commands and have a valid context to be in.
=cut
- my $print_cmd = 'print {$DB::OUT} ';
- # p - print (no args): print $_.
- if ($cmd eq 'p') {
- $cmd = $print_cmd . '$_';
- }
+ elsif ( $DB::package eq 'DB::fake' ) {
- # p - print the given expression.
- $cmd =~ s/\Ap\b/$print_cmd /;
+ # Fallen off the end already.
+ if (!$DB::term) {
+ DB::setterm();
+ }
-=head4 C<=> - define command alias
+ DB::print_help(<<EOP);
+Debugged program terminated. Use B<q> to quit or B<R> to restart,
+use B<o> I<inhibit_exit> to avoid stopping after program termination,
+B<h q>, B<h R> or B<h o> to get additional info.
+EOP
-Manipulates C<%alias> to add or list command aliases.
+ # Set the DB::eval context appropriately.
+ $DB::package = 'main';
+ $DB::usercontext = DB::_calc_usercontext($DB::package);
+ } ## end elsif ($package eq 'DB::fake')
+
+=pod
+
+If the program hasn't finished executing, we scan forward to the
+next executable line, print that out, build the prompt from the file and line
+number information, and print that.
=cut
- # = - set up a command alias.
- if ($cmd =~ s/\A=\s*//) {
- my @keys;
- if ( length $cmd == 0 ) {
+ else {
- # No args, get current aliases.
- @keys = sort keys %alias;
- }
- elsif ( my ( $k, $v ) = ( $cmd =~ /^(\S+)\s+(\S.*)/ ) ) {
- # Creating a new alias. $k is alias name, $v is
- # alias value.
+ # Still somewhere in the midst of execution. Set up the
+ # debugger prompt.
+ $DB::sub =~ s/\'/::/; # Swap Perl 4 package separators (') to
+ # Perl 5 ones (sorry, we don't print Klingon
+ #module names)
- # can't use $_ or kill //g state
- for my $x ( $k, $v ) {
+ $self->prefix($DB::sub =~ /::/ ? "" : ($DB::package . '::'));
+ $self->append_to_prefix( "$DB::sub(${DB::filename}:" );
+ $self->after( $self->_curr_line =~ /\n$/ ? '' : "\n" );
- # Escape "alarm" characters.
- $x =~ s/\a/\\a/g;
- }
+ # Break up the prompt if it's really long.
+ if ( length($self->prefix()) > 30 ) {
+ $self->position($self->prefix . "$line):\n$line:\t" . $self->_curr_line . $self->after);
+ $self->prefix("");
+ $self->infix(":\t");
+ }
+ else {
+ $self->infix("):\t");
+ $self->position(
+ $self->prefix . $line. $self->infix
+ . $self->_curr_line . $self->after
+ );
+ }
- # Substitute key for value, using alarm chars
- # as separators (which is why we escaped them in
- # the command).
- $alias{$k} = "s\a$k\a$v\a";
+ # Print current line info, indenting if necessary.
+ $self->_my_print_lineinfo($line, $self->position);
- # Turn off standard warn and die behavior.
- local $SIG{__DIE__};
- local $SIG{__WARN__};
+ my $i;
+ my $line_i = sub { return $DB::dbline[$i]; };
- # Is it valid Perl?
- unless ( eval "sub { s\a$k\a$v\a }; 1" ) {
+ # Scan forward, stopping at either the end or the next
+ # unbreakable line.
+ for ( $i = $line + 1 ; $i <= $DB::max && $line_i->() == 0 ; ++$i )
+ { #{ vi
- # Nope. Bad alias. Say so and get out.
- print $OUT "Can't alias $k to $v: $@\n";
- delete $alias{$k};
- next CMD;
- }
+ # Drop out on null statements, block closers, and comments.
+ last if $line_i->() =~ /^\s*[\;\}\#\n]/;
- # We'll only list the new one.
- @keys = ($k);
- } ## end elsif (my ($k, $v) = ($cmd...
+ # Drop out if the user interrupted us.
+ last if $signal;
- # The argument is the alias to list.
- else {
- @keys = ($cmd);
- }
+ # Append a newline if the line doesn't have one. Can happen
+ # in eval'ed text, for instance.
+ $self->after( $line_i->() =~ /\n$/ ? '' : "\n" );
- # List aliases.
- for my $k (@keys) {
-
- # Messy metaquoting: Trim the substitution code off.
- # We use control-G as the delimiter because it's not
- # likely to appear in the alias.
- if ( ( my $v = $alias{$k} ) =~ s\as\a$k\a(.*)\a$\a1\a ) {
-
- # Print the alias.
- print $OUT "$k\t= $1\n";
- }
- elsif ( defined $alias{$k} ) {
-
- # Couldn't trim it off; just print the alias code.
- print $OUT "$k\t$alias{$k}\n";
- }
- else {
-
- # No such, dude.
- print "No alias for $k\n";
- }
- } ## end for my $k (@keys)
- next CMD;
- }
+ # Next executable line.
+ my $incr_pos = $self->prefix . $i . $self->infix . $line_i->()
+ . $self->after;
+ $self->append_to_position($incr_pos);
+ $self->_my_print_lineinfo($i, $incr_pos);
+ } ## end for ($i = $line + 1 ; $i...
+ } ## end else [ if ($slave_editor)
-=head4 C<source> - read commands from a file.
+ return;
+}
-Opens a lexical filehandle and stacks it on C<@cmdfhs>; C<DB::readline> will
-pick it up.
+sub _handle_t_command {
+ if (my ($levels) = $DB::cmd =~ /\At(?:\s+(\d+))?\z/) {
+ $trace ^= 1;
+ local $\ = '';
+ $DB::trace_to_depth = $levels ? $stack_depth + $levels : 1E9;
+ print {$OUT} "Trace = "
+ . ( ( $trace & 1 )
+ ? ( $levels ? "on (to level $DB::trace_to_depth)" : "on" )
+ : "off" ) . "\n";
+ next CMD;
+ }
-=cut
+ return;
+}
- # source - read commands from a file (or pipe!) and execute.
- if (my ($sourced_fn) = $cmd =~ /\Asource\s+(.*\S)/) {
- if ( open my $fh, $sourced_fn ) {
- # Opened OK; stick it in the list of file handles.
- push @cmdfhs, $fh;
- }
- else {
+sub _handle_S_command {
+ if (my ($print_all_subs, $should_reverse, $Spatt)
+ = $DB::cmd =~ /\AS(\s+(!)?(.+))?\z/) {
+ # $Spatt is the pattern (if any) to use.
+ # Reverse scan?
+ my $Srev = defined $should_reverse;
+ # No args - print all subs.
+ my $Snocheck = !defined $print_all_subs;
- # Couldn't open it.
- &warn("Can't execute '$sourced_fn': $!\n");
- }
- next CMD;
- }
+ # Need to make these sane here.
+ local $\ = '';
+ local $, = '';
- if (my ($which_cmd, $position)
- = $cmd =~ /^(enable|disable)\s+(\S+)\s*$/) {
+ # Search through the debugger's magical hash of subs.
+ # If $nocheck is true, just print the sub name.
+ # Otherwise, check it against the pattern. We then use
+ # the XOR trick to reverse the condition as required.
+ foreach $subname ( sort( keys %sub ) ) {
+ if ( $Snocheck or $Srev ^ ( $subname =~ /$Spatt/ ) ) {
+ print $OUT $subname, "\n";
+ }
+ }
+ next CMD;
+ }
- my ($fn, $line_num);
- if ($position =~ m{\A\d+\z})
- {
- $fn = $filename;
- $line_num = $position;
- }
- elsif (my ($new_fn, $new_line_num)
- = $position =~ m{\A(.*):(\d+)\z}) {
- ($fn, $line_num) = ($new_fn, $new_line_num);
- }
- else
- {
- &warn("Wrong spec for enable/disable argument.\n");
- }
+ return;
+}
- if (defined($fn)) {
- if (_has_breakpoint_data_ref($fn, $line_num)) {
- _set_breakpoint_enabled_status($fn, $line_num,
- ($which_cmd eq 'enable' ? 1 : '')
- );
- }
- else {
- &warn("No breakpoint set at ${fn}:${line_num}\n");
- }
- }
+sub _handle_V_command_and_X_command {
- next CMD;
- }
+ $DB::cmd =~ s/^X\b/V $DB::package/;
-=head4 C<save> - send current history to a file
+ # Bare V commands get the currently-being-debugged package
+ # added.
+ if ($DB::cmd eq "V") {
+ $DB::cmd = "V $DB::package";
+ }
+
+ # V - show variables in package.
+ if (my ($new_packname, $new_vars_str) =
+ $DB::cmd =~ /\AV\b\s*(\S+)\s*(.*)/) {
+
+ # Save the currently selected filehandle and
+ # force output to debugger's filehandle (dumpvar
+ # just does "print" for output).
+ my $savout = select($OUT);
+
+ # Grab package name and variables to dump.
+ $packname = $new_packname;
+ my @vars = split( ' ', $new_vars_str );
+
+ # If main::dumpvar isn't here, get it.
+ do 'dumpvar.pl' || die $@ unless defined &main::dumpvar;
+ if ( defined &main::dumpvar ) {
+
+ # We got it. Turn off subroutine entry/exit messages
+ # for the moment, along with return values.
+ local $frame = 0;
+ local $doret = -2;
+
+ # must detect sigpipe failures - not catching
+ # then will cause the debugger to die.
+ eval {
+ &main::dumpvar(
+ $packname,
+ defined $option{dumpDepth}
+ ? $option{dumpDepth}
+ : -1, # assume -1 unless specified
+ @vars
+ );
+ };
+
+ # The die doesn't need to include the $@, because
+ # it will automatically get propagated for us.
+ if ($@) {
+ die unless $@ =~ /dumpvar print failed/;
+ }
+ } ## end if (defined &main::dumpvar)
+ else {
+
+ # Couldn't load dumpvar.
+ print $OUT "dumpvar.pl not available.\n";
+ }
+
+ # Restore the output filehandle, and go round again.
+ select($savout);
+ next CMD;
+ }
+
+ return;
+}
+
+sub _handle_dash_command {
+
+ if ($DB::cmd eq '-') {
+
+ # back up by a window; go to 1 if back too far.
+ $start -= $incr + $window + 1;
+ $start = 1 if $start <= 0;
+ $incr = $window - 1;
+
+ # Generate and execute a "l +" command (handled below).
+ $DB::cmd = 'l ' . ($start) . '+';
+ }
+ return;
+}
+
+sub _n_or_s_commands_generic {
+ my ($self, $new_val) = @_;
+ # n - next
+ next CMD if DB::_DB__is_finished();
+
+ # Single step, but don't enter subs.
+ $single = $new_val;
+
+ # Save for empty command (repeat last).
+ $laststep = $DB::cmd;
+ last CMD;
+}
+
+sub _n_or_s {
+ my ($self, $letter, $new_val) = @_;
+
+ if ($DB::cmd eq $letter) {
+ $self->_n_or_s_commands_generic($new_val);
+ }
+ elsif ($DB::cmd =~ m#\A\Q$letter\E\b#) {
+ $self->_n_or_s_and_arg_commands_generic($letter, $new_val);
+ }
+
+ return;
+}
+
+sub _handle_n_command {
+ my $self = shift;
+
+ return $self->_n_or_s('n', 2);
+}
+
+sub _handle_s_command {
+ my $self = shift;
+
+ return $self->_n_or_s('s', 1);
+}
+
+sub _handle_r_command {
+ my $self = shift;
+ # r - return from the current subroutine.
+ if ($DB::cmd eq 'r') {
+
+ # Can't do anything if the program's over.
+ next CMD if DB::_DB__is_finished();
+
+ # Turn on stack trace.
+ $stack[$stack_depth] |= 1;
+
+ # Print return value unless the stack is empty.
+ $doret = $option{PrintRet} ? $stack_depth - 1 : -2;
+ last CMD;
+ }
+
+ return;
+}
+
+sub _handle_T_command {
+ if ($DB::cmd eq 'T') {
+ DB::print_trace( $OUT, 1 ); # skip DB
+ next CMD;
+ }
+
+ return;
+}
+
+sub _handle_w_command {
+ if (my ($arg) = $DB::cmd =~ /\Aw\b\s*(.*)/s) {
+ DB::cmd_w( 'w', $arg );
+ next CMD;
+ }
+
+ return;
+}
+
+sub _handle_W_command {
+ if (my ($arg) = $DB::cmd =~ /\AW\b\s*(.*)/s) {
+ DB::cmd_W( 'W', $arg );
+ next CMD;
+ }
+
+ return;
+}
+
+sub _handle_rc_recall_command {
+ my $self = shift;
+
+ # $rc - recall command.
+ if (my ($minus, $arg) = $DB::cmd =~ m#\A$rc+\s*(-)?(\d+)?\z#) {
+
+ # No arguments, take one thing off history.
+ pop(@hist) if length($DB::cmd) > 1;
+
+ # Relative (- found)?
+ # Y - index back from most recent (by 1 if bare minus)
+ # N - go to that particular command slot or the last
+ # thing if nothing following.
+ my $new_i = $minus ? ( $#hist - ( $arg || 1 ) ) : ( $arg || $#hist );
+
+ $self->i_cmd($new_i);
+
+ # Pick out the command desired.
+ $DB::cmd = $hist[$self->i_cmd];
+
+ # Print the command to be executed and restart the loop
+ # with that command in the buffer.
+ print {$OUT} $DB::cmd, "\n";
+ redo CMD;
+ }
+
+ return;
+}
+
+sub _handle_rc_search_history_command {
+ my $self = shift;
+
+ # $rc pattern $rc - find a command in the history.
+ if (my ($arg) = $DB::cmd =~ /\A$rc([^$rc].*)\z/) {
+
+ # Create the pattern to use.
+ my $pat = "^$arg";
+ $self->pat($pat);
+
+ # Toss off last entry if length is >1 (and it always is).
+ pop(@hist) if length($DB::cmd) > 1;
+
+ my $i = $self->i_cmd;
+
+ # Look backward through the history.
+ SEARCH_HIST:
+ for ( $i = $#hist ; $i ; --$i ) {
+ # Stop if we find it.
+ last SEARCH_HIST if $hist[$i] =~ /$pat/;
+ }
+
+ $self->i_cmd($i);
+
+ if ( !$self->i_cmd ) {
+
+ # Never found it.
+ print $OUT "No such command!\n\n";
+ next CMD;
+ }
+
+ # Found it. Put it in the buffer, print it, and process it.
+ $DB::cmd = $hist[$self->i_cmd];
+ print $OUT $DB::cmd, "\n";
+ redo CMD;
+ }
+
+ return;
+}
+
+sub _handle_H_command {
+ my $self = shift;
+
+ if ($DB::cmd =~ /\AH\b\s*\*/) {
+ @hist = @truehist = ();
+ print $OUT "History cleansed\n";
+ next CMD;
+ }
+
+ if (my ($num)
+ = $DB::cmd =~ /\AH\b\s*(?:-(\d+))?/) {
+
+ # Anything other than negative numbers is ignored by
+ # the (incorrect) pattern, so this test does nothing.
+ $end = $num ? ( $#hist - $num ) : 0;
+
+ # Set to the minimum if less than zero.
+ $hist = 0 if $hist < 0;
+
+ # Start at the end of the array.
+ # Stay in while we're still above the ending value.
+ # Tick back by one each time around the loop.
+ my $i;
+
+ for ( $i = $#hist ; $i > $end ; $i-- ) {
+
+ # Print the command unless it has no arguments.
+ print $OUT "$i: ", $hist[$i], "\n"
+ unless $hist[$i] =~ /^.?$/;
+ }
+
+ $self->i_cmd($i);
+
+ next CMD;
+ }
+
+ return;
+}
+
+sub _handle_doc_command {
+ my $self = shift;
+
+ # man, perldoc, doc - show manual pages.
+ if (my ($man_page)
+ = $DB::cmd =~ /\A(?:man|(?:perl)?doc)\b(?:\s+([^(]*))?\z/) {
+ runman($man_page);
+ next CMD;
+ }
+
+ return;
+}
+
+sub _handle_p_command {
+ my $self = shift;
+
+ my $print_cmd = 'print {$DB::OUT} ';
+ # p - print (no args): print $_.
+ if ($DB::cmd eq 'p') {
+ $DB::cmd = $print_cmd . '$_';
+ }
+
+ # p - print the given expression.
+ $DB::cmd =~ s/\Ap\b/$print_cmd /;
+
+ return;
+}
-Takes the complete history, (not the shrunken version you see with C<H>),
-and saves it to the given filename, so it can be replayed using C<source>.
+sub _handle_equal_sign_command {
+ my $self = shift;
-Note that all C<^(save|source)>'s are commented out with a view to minimise recursion.
+ if ($DB::cmd =~ s/\A=\s*//) {
+ my @keys;
+ if ( length $DB::cmd == 0 ) {
-=cut
+ # No args, get current aliases.
+ @keys = sort keys %alias;
+ }
+ elsif ( my ( $k, $v ) = ( $DB::cmd =~ /^(\S+)\s+(\S.*)/ ) ) {
- # save source - write commands to a file for later use
- if (my ($new_fn) = $cmd =~ /\Asave\s*(.*)\z/) {
- my $filename = $new_fn || '.perl5dbrc'; # default?
- if ( open my $fh, '>', $filename ) {
-
- # chomp to remove extraneous newlines from source'd files
- chomp( my @truelist =
- map { m/^\s*(save|source)/ ? "#$_" : $_ }
- @truehist );
- print $fh join( "\n", @truelist );
- print "commands saved in $file\n";
- }
- else {
- &warn("Can't save debugger commands in '$new_fn': $!\n");
- }
- next CMD;
- }
+ # Creating a new alias. $k is alias name, $v is
+ # alias value.
-=head4 C<R> - restart
+ # can't use $_ or kill //g state
+ for my $x ( $k, $v ) {
-Restart the debugger session.
+ # Escape "alarm" characters.
+ $x =~ s/\a/\\a/g;
+ }
-=head4 C<rerun> - rerun the current session
+ # Substitute key for value, using alarm chars
+ # as separators (which is why we escaped them in
+ # the command).
+ $alias{$k} = "s\a$k\a$v\a";
-Return to any given position in the B<true>-history list
+ # Turn off standard warn and die behavior.
+ local $SIG{__DIE__};
+ local $SIG{__WARN__};
-=cut
+ # Is it valid Perl?
+ unless ( eval "sub { s\a$k\a$v\a }; 1" ) {
- # R - restart execution.
- # rerun - controlled restart execution.
- if (my ($cmd_cmd, $cmd_params) =
- $cmd =~ /\A((?:R)|(?:rerun\s*(.*)))\z/) {
- my @args = ($cmd_cmd eq 'R' ? restart() : rerun($cmd_params));
-
- # Close all non-system fds for a clean restart. A more
- # correct method would be to close all fds that were not
- # open when the process started, but this seems to be
- # hard. See "debugger 'R'estart and open database
- # connections" on p5p.
-
- my $max_fd = 1024; # default if POSIX can't be loaded
- if (eval { require POSIX }) {
- eval { $max_fd = POSIX::sysconf(POSIX::_SC_OPEN_MAX()) };
- }
+ # Nope. Bad alias. Say so and get out.
+ print $OUT "Can't alias $k to $v: $@\n";
+ delete $alias{$k};
+ next CMD;
+ }
- if (defined $max_fd) {
- foreach ($^F+1 .. $max_fd-1) {
- next unless open FD_TO_CLOSE, "<&=$_";
- close(FD_TO_CLOSE);
- }
- }
+ # We'll only list the new one.
+ @keys = ($k);
+ } ## end elsif (my ($k, $v) = ($DB::cmd...
- # And run Perl again. We use exec() to keep the
- # PID stable (and that way $ini_pids is still valid).
- exec(@args) || print $OUT "exec failed: $!\n";
+ # The argument is the alias to list.
+ else {
+ @keys = ($DB::cmd);
+ }
- last CMD;
- }
+ # List aliases.
+ for my $k (@keys) {
-=head4 C<|, ||> - pipe output through the pager.
+ # Messy metaquoting: Trim the substitution code off.
+ # We use control-G as the delimiter because it's not
+ # likely to appear in the alias.
+ if ( ( my $v = $alias{$k} ) =~ s\as\a$k\a(.*)\a$\a1\a ) {
-For C<|>, we save C<OUT> (the debugger's output filehandle) and C<STDOUT>
-(the program's standard output). For C<||>, we only save C<OUT>. We open a
-pipe to the pager (restoring the output filehandles if this fails). If this
-is the C<|> command, we also set up a C<SIGPIPE> handler which will simply
-set C<$signal>, sending us back into the debugger.
+ # Print the alias.
+ print $OUT "$k\t= $1\n";
+ }
+ elsif ( defined $alias{$k} ) {
-We then trim off the pipe symbols and C<redo> the command loop at the
-C<PIPE> label, causing us to evaluate the command in C<$cmd> without
-reading another.
+ # Couldn't trim it off; just print the alias code.
+ print $OUT "$k\t$alias{$k}\n";
+ }
+ else {
-=cut
+ # No such, dude.
+ print "No alias for $k\n";
+ }
+ } ## end for my $k (@keys)
+ next CMD;
+ }
- # || - run command in the pager, with output to DB::OUT.
- if ($cmd =~ m#\A\|\|?\s*[^|]#) {
- if ( $pager =~ /^\|/ ) {
-
- # Default pager is into a pipe. Redirect I/O.
- open( SAVEOUT, ">&STDOUT" )
- || &warn("Can't save STDOUT");
- open( STDOUT, ">&OUT" )
- || &warn("Can't redirect STDOUT");
- } ## end if ($pager =~ /^\|/)
- else {
+ return;
+}
- # Not into a pipe. STDOUT is safe.
- open( SAVEOUT, ">&OUT" ) || &warn("Can't save DB::OUT");
- }
+sub _handle_source_command {
+ my $self = shift;
- # Fix up environment to record we have less if so.
- fix_less();
+ # source - read commands from a file (or pipe!) and execute.
+ if (my ($sourced_fn) = $DB::cmd =~ /\Asource\s+(.*\S)/) {
+ if ( open my $fh, $sourced_fn ) {
- unless ( $piped = open( OUT, $pager ) ) {
+ # Opened OK; stick it in the list of file handles.
+ push @cmdfhs, $fh;
+ }
+ else {
- # Couldn't open pipe to pager.
- &warn("Can't pipe output to '$pager'");
- if ( $pager =~ /^\|/ ) {
+ # Couldn't open it.
+ DB::warn("Can't execute '$sourced_fn': $!\n");
+ }
+ next CMD;
+ }
- # Redirect I/O back again.
- open( OUT, ">&STDOUT" ) # XXX: lost message
- || &warn("Can't restore DB::OUT");
- open( STDOUT, ">&SAVEOUT" )
- || &warn("Can't restore STDOUT");
- close(SAVEOUT);
- } ## end if ($pager =~ /^\|/)
- else {
+ return;
+}
- # Redirect I/O. STDOUT already safe.
- open( OUT, ">&STDOUT" ) # XXX: lost message
- || &warn("Can't restore DB::OUT");
- }
- next CMD;
- } ## end unless ($piped = open(OUT,...
-
- # Set up broken-pipe handler if necessary.
- $SIG{PIPE} = \&DB::catch
- if $pager =~ /^\|/
- && ( "" eq $SIG{PIPE} || "DEFAULT" eq $SIG{PIPE} );
-
- OUT->autoflush(1);
- # Save current filehandle, and put it back.
- $selected = select(OUT);
- # Don't put it back if pager was a pipe.
- select($selected), $selected = "" unless $cmd =~ /^\|\|/;
-
- # Trim off the pipe symbols and run the command now.
- $cmd =~ s#\A\|+\s*##;
- redo PIPE;
- }
+sub _handle_enable_disable_commands {
+ my $self = shift;
-=head3 END OF COMMAND PARSING
+ if (my ($which_cmd, $position)
+ = $DB::cmd =~ /\A(enable|disable)\s+(\S+)\s*\z/) {
-Anything left in C<$cmd> at this point is a Perl expression that we want to
-evaluate. We'll always evaluate in the user's context, and fully qualify
-any variables we might want to address in the C<DB> package.
+ my ($fn, $line_num);
+ if ($position =~ m{\A\d+\z})
+ {
+ $fn = $DB::filename;
+ $line_num = $position;
+ }
+ elsif (my ($new_fn, $new_line_num)
+ = $position =~ m{\A(.*):(\d+)\z}) {
+ ($fn, $line_num) = ($new_fn, $new_line_num);
+ }
+ else
+ {
+ DB::warn("Wrong spec for enable/disable argument.\n");
+ }
-=cut
+ if (defined($fn)) {
+ if (DB::_has_breakpoint_data_ref($fn, $line_num)) {
+ DB::_set_breakpoint_enabled_status($fn, $line_num,
+ ($which_cmd eq 'enable' ? 1 : '')
+ );
+ }
+ else {
+ DB::warn("No breakpoint set at ${fn}:${line_num}\n");
+ }
+ }
- # t - turn trace on.
- if ($cmd =~ s#\At\s+(\d+)?#\$DB::trace |= 1;\n#) {
- my $trace_arg = $1;
- $trace_to_depth = $trace_arg ? $stack_depth||0 + $1 : 1E9;
- }
+ next CMD;
+ }
- # s - single-step. Remember the last command was 's'.
- if ($cmd =~ s/\As\s/\$DB::single = 1;\n/) {
- $laststep = 's';
- }
+ return;
+}
- # n - single-step, but not into subs. Remember last command
- # was 'n'.
- if ($cmd =~ s#\An\s#\$DB::single = 2;\n#) {
- $laststep = 'n';
- }
+sub _handle_save_command {
+ my $self = shift;
- } # PIPE:
+ if (my ($new_fn) = $DB::cmd =~ /\Asave\s*(.*)\z/) {
+ my $filename = $new_fn || '.perl5dbrc'; # default?
+ if ( open my $fh, '>', $filename ) {
- # Make sure the flag that says "the debugger's running" is
- # still on, to make sure we get control again.
- $evalarg = "\$^D = \$^D | \$DB::db_stop;\n$cmd";
+ # chomp to remove extraneous newlines from source'd files
+ chomp( my @truelist =
+ map { m/\A\s*(save|source)/ ? "#$_" : $_ }
+ @truehist );
+ print {$fh} join( "\n", @truelist );
+ print "commands saved in $filename\n";
+ }
+ else {
+ DB::warn("Can't save debugger commands in '$new_fn': $!\n");
+ }
+ next CMD;
+ }
- # Run *our* eval that executes in the caller's context.
- &eval;
+ return;
+}
- # Turn off the one-time-dump stuff now.
- if ($onetimeDump) {
- $onetimeDump = undef;
- $onetimedumpDepth = undef;
- }
- elsif ( $term_pid == $$ ) {
- eval { # May run under miniperl, when not available...
- STDOUT->flush();
- STDERR->flush();
- };
+sub _n_or_s_and_arg_commands_generic {
+ my ($self, $letter, $new_val) = @_;
- # XXX If this is the master pid, print a newline.
- print $OUT "\n";
- }
- } ## end while (($term || &setterm...
+ # s - single-step. Remember the last command was 's'.
+ if ($DB::cmd =~ s#\A\Q$letter\E\s#\$DB::single = $new_val;\n#) {
+ $laststep = $letter;
+ }
-=head3 POST-COMMAND PROCESSING
+ return;
+}
-After each command, we check to see if the command output was piped anywhere.
-If so, we go through the necessary code to unhook the pipe and go back to
-our standard filehandles for input and output.
+sub _handle_sh_command {
+ my $self = shift;
-=cut
+ # $sh$sh - run a shell command (if it's all ASCII).
+ # Can't run shell commands with Unicode in the debugger, hmm.
+ my $my_cmd = $DB::cmd;
+ if ($my_cmd =~ m#\A$sh#gms) {
- continue { # CMD:
+ if ($my_cmd =~ m#\G\z#cgms) {
+ # Run the user's shell. If none defined, run Bourne.
+ # We resume execution when the shell terminates.
+ DB::system( $ENV{SHELL} || "/bin/sh" );
+ next CMD;
+ }
+ elsif ($my_cmd =~ m#\G$sh\s*(.*)#cgms) {
+ # System it.
+ DB::system($1);
+ next CMD;
+ }
+ elsif ($my_cmd =~ m#\G\s*(.*)#cgms) {
+ DB::system( $ENV{SHELL} || "/bin/sh", "-c", $1 );
+ next CMD;
+ }
+ }
+}
- # At the end of every command:
- if ($piped) {
-
- # Unhook the pipe mechanism now.
- if ( $pager =~ /^\|/ ) {
-
- # No error from the child.
- $? = 0;
-
- # we cannot warn here: the handle is missing --tchrist
- close(OUT) || print SAVEOUT "\nCan't close DB::OUT\n";
-
- # most of the $? crud was coping with broken cshisms
- # $? is explicitly set to 0, so this never runs.
- 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 {
- print SAVEOUT "status ", ( $? >> 8 ), "\n";
- }
- } ## end if ($?)
-
- # Reopen filehandle for our output (if we can) and
- # restore STDOUT (if we can).
- open( OUT, ">&STDOUT" ) || &warn("Can't restore DB::OUT");
- open( STDOUT, ">&SAVEOUT" )
- || &warn("Can't restore STDOUT");
-
- # Turn off pipe exception handler if necessary.
- $SIG{PIPE} = "DEFAULT" if $SIG{PIPE} eq \&DB::catch;
-
- # Will stop ignoring SIGPIPE if done like nohup(1)
- # does SIGINT but Perl doesn't give us a choice.
- } ## end if ($pager =~ /^\|/)
- else {
+sub _handle_x_command {
+ my $self = shift;
- # Non-piped "pager". Just restore STDOUT.
- open( OUT, ">&SAVEOUT" ) || &warn("Can't restore DB::OUT");
- }
+ if ($DB::cmd =~ s#\Ax\b# #) { # Remainder gets done by DB::eval()
+ $onetimeDump = 'dump'; # main::dumpvar shows the output
- # Close filehandle pager was using, restore the normal one
- # if necessary,
- close(SAVEOUT);
- select($selected), $selected = "" unless $selected eq "";
+ # handle special "x 3 blah" syntax XXX propagate
+ # doc back to special variables.
+ if ( $DB::cmd =~ s#\A\s*(\d+)(?=\s)# #) {
+ $onetimedumpDepth = $1;
+ }
+ }
- # No pipes now.
- $piped = "";
- } ## end if ($piped)
- } # CMD:
+ return;
+}
-=head3 COMMAND LOOP TERMINATION
+sub _handle_q_command {
+ my $self = shift;
-When commands have finished executing, we come here. If the user closed the
-input filehandle, we turn on C<$fall_off_end> to emulate a C<q> command. We
-evaluate any post-prompt items. We restore C<$@>, C<$!>, C<$^E>, C<$,>, C<$/>,
-C<$\>, and C<$^W>, and return a null list as expected by the Perl interpreter.
-The interpreter will then execute the next line and then return control to us
-again.
+ if ($DB::cmd eq 'q') {
+ $fall_off_end = 1;
+ DB::clean_ENV();
+ exit $?;
+ }
-=cut
+ return;
+}
- # No more commands? Quit.
- $fall_off_end = 1 unless defined $cmd; # Emulate 'q' on EOF
+sub _handle_cmd_wrapper_commands {
+ my $self = shift;
- # Evaluate post-prompt commands.
- foreach $evalarg (@$post) {
- &eval;
- }
- } # if ($single || $signal)
+ # All of these commands were remapped in perl 5.8.0;
+ # we send them off to the secondary dispatcher (see below).
+ if (my ($cmd_letter, $my_arg) = $DB::cmd =~ /\A([aAbBeEhilLMoOPvwW]\b|[<>\{]{1,2})\s*(.*)/so) {
+ DB::cmd_wrapper( $cmd_letter, $my_arg, $line );
+ next CMD;
+ }
- # Put the user's globals back where you found them.
- ( $@, $!, $^E, $,, $/, $\, $^W ) = @saved;
- ();
-} ## end sub DB
+ return;
+}
+package DB;
# The following code may be executed now:
# BEGIN {warn 4}
# We need to fully qualify the name ("DB::sub") to make "use strict;"
# happy. -- Shlomi Fish
sub DB::sub {
- # Do not use a regex in this subroutine -> results in corrupted memory
- # See: [perl #66110]
+ # Do not use a regex in this subroutine -> results in corrupted memory
+ # See: [perl #66110]
- # lock ourselves under threads
- lock($DBGR);
+ # lock ourselves under threads
+ lock($DBGR);
# Whether or not the autoloader was running, a scalar to put the
# sub's return value in (if needed), and an array to put the sub's
# return value in (if needed).
my ( $al, $ret, @ret ) = "";
- if ($sub eq 'threads::new' && $ENV{PERL5DB_THREADED}) {
- print "creating new thread\n";
- }
+ if ($sub eq 'threads::new' && $ENV{PERL5DB_THREADED}) {
+ print "creating new thread\n";
+ }
# If the last ten characters are '::AUTOLOAD', note we've traced
# into AUTOLOAD for $sub.
# Scalar context.
else {
- if ( defined wantarray ) {
- no strict 'refs';
- # Save the value if it's wanted at all.
- $ret = &$sub;
- }
- else {
- no strict 'refs';
- # Void return, explicitly.
- &$sub;
- undef $ret;
- }
+ if ( defined wantarray ) {
+ no strict 'refs';
+ # Save the value if it's wanted at all.
+ $ret = &$sub;
+ }
+ else {
+ no strict 'refs';
+ # Void return, explicitly.
+ &$sub;
+ undef $ret;
+ }
# Pop the single-step value off the stack.
$single |= $stack[ $stack_depth-- ];
no strict 'refs';
- # lock ourselves under threads
- lock($DBGR);
+ # lock ourselves under threads
+ lock($DBGR);
# Whether or not the autoloader was running, a scalar to put the
# sub's return value in (if needed), and an array to put the sub's
# return value in (if needed).
my ( $al, $ret, @ret ) = "";
- if ($sub =~ /^threads::new$/ && $ENV{PERL5DB_THREADED}) {
- print "creating new thread\n";
- }
+ if ($sub =~ /^threads::new$/ && $ENV{PERL5DB_THREADED}) {
+ print "creating new thread\n";
+ }
# If the last ten characters are C'::AUTOLOAD', note we've traced
# into AUTOLOAD for $sub.
# default to the older version of the command.
my $call = 'cmd_'
. ( $set{$CommandSet}{$cmd}
- || ( $cmd =~ /^[<>{]+/o ? 'prepost' : $cmd ) );
+ || ( $cmd =~ /\A[<>{]+/o ? 'prepost' : $cmd ) );
# Call the command subroutine, call it by name.
return __PACKAGE__->can($call)->( $cmd, $line, $dblineno );
my $dbline = shift;
# If it's dot (here), or not all digits, use the current line.
- $line =~ s/^(\.|(?:[^\d]))/$dbline/;
+ $line =~ s/\A\./$dbline/;
# Should be a line number followed by an expression.
- if ( $line =~ /^\s*(\d*)\s*(\S.+)/ ) {
- my ( $lineno, $expr ) = ( $1, $2 );
+ if ( my ($lineno, $expr) = $line =~ /^\s*(\d*)\s*(\S.+)/ ) {
+
+ if (! length($lineno)) {
+ $lineno = $dbline;
+ }
# If we have an expression ...
if ( length $expr ) {
# if delete_action blows up for some reason, in which case
# we print $@ and get out.
if ( $line eq '*' ) {
- eval { &delete_action(); 1 } or print $OUT $@ and return;
+ if (! eval { _delete_all_actions(); 1 }) {
+ print {$OUT} $@;
+ return;
+ }
}
# There's a real line number. Pass it to delete_action.
# Error trapping is as above.
elsif ( $line =~ /^(\S.*)/ ) {
- eval { &delete_action($1); 1 } or print $OUT $@ and return;
+ if (! eval { delete_action($1); 1 }) {
+ print {$OUT} $@;
+ return;
+ }
}
# Swing and a miss. Bad syntax.
=cut
+sub _remove_action_from_dbline {
+ my $i = shift;
+
+ $dbline{$i} =~ s/\0[^\0]*//; # \^a
+ delete $dbline{$i} if $dbline{$i} eq '';
+
+ return;
+}
+
+sub _delete_all_actions {
+ print {$OUT} "Deleting all actions...\n";
+
+ for my $file ( keys %had_breakpoints ) {
+ local *dbline = $main::{ '_<' . $file };
+ $max = $#dbline;
+ my $was;
+ for my $i (1 .. $max) {
+ if ( defined $dbline{$i} ) {
+ _remove_action_from_dbline($i);
+ }
+ }
+
+ unless ( $had_breakpoints{$file} &= ~2 ) {
+ delete $had_breakpoints{$file};
+ }
+ }
+
+ return;
+}
+
sub delete_action {
my $i = shift;
- if ( defined($i) ) {
+ if ( defined($i) ) {
# Can there be one?
die "Line $i has no action .\n" if $dbline[$i] == 0;
# Nuke whatever's there.
- $dbline{$i} =~ s/\0[^\0]*//; # \^a
- delete $dbline{$i} if $dbline{$i} eq '';
+ _remove_action_from_dbline($i);
}
else {
- print $OUT "Deleting all actions...\n";
- for my $file ( keys %had_breakpoints ) {
- local *dbline = $main::{ '_<' . $file };
- $max = $#dbline;
- my $was;
- for $i (1 .. $max) {
- if ( defined $dbline{$i} ) {
- $dbline{$i} =~ s/\0[^\0]*//;
- delete $dbline{$i} if $dbline{$i} eq '';
- }
- unless ( $had_breakpoints{$file} &= ~2 ) {
- delete $had_breakpoints{$file};
- }
- } ## end for ($i = 1 .. $max)
- } ## end for my $file (keys %had_breakpoints)
- } ## end else [ if (defined($i))
-} ## end sub delete_action
+ _delete_all_actions();
+ }
+}
=head3 C<cmd_b> (command)
my $line = shift; # [.|line] [cond]
my $dbline = shift;
+ my $default_cond = sub {
+ my $cond = shift;
+ return length($cond) ? $cond : '1';
+ };
+
# Make . the current line number if it's there..
$line =~ s/^\.(\s|\z)/$dbline$1/;
# No line number, no condition. Simple break on current line.
if ( $line =~ /^\s*$/ ) {
- &cmd_b_line( $dbline, 1 );
+ cmd_b_line( $dbline, 1 );
}
# Break on load for a file.
- elsif ( $line =~ /^load\b\s*(.*)/ ) {
- my $file = $1;
- $file =~ s/\s+$//;
- &cmd_b_load($file);
+ elsif ( my ($file) = $line =~ /^load\b\s*(.*)/ ) {
+ $file =~ s/\s+\z//;
+ cmd_b_load($file);
}
# b compile|postpone <some sub> [<condition>]
# The interpreter actually traps this one for us; we just put the
# necessary condition in the %postponed hash.
- elsif ( $line =~ /^(postpone|compile)\b\s*([':A-Za-z_][':\w]*)\s*(.*)/ ) {
-
- # Capture the condition if there is one. Make it true if none.
- my $cond = length $3 ? $3 : '1';
-
- # Save the sub name and set $break to 1 if $1 was 'postpone', 0
- # if it was 'compile'.
- my ( $subname, $break ) = ( $2, $1 eq 'postpone' );
+ elsif ( my ($action, $subname, $cond)
+ = $line =~ /^(postpone|compile)\b\s*([':A-Za-z_][':\w]*)\s*(.*)/ ) {
# De-Perl4-ify the name - ' separators to ::.
- $subname =~ s/\'/::/g;
+ $subname =~ s/'/::/g;
# Qualify it into the current package unless it's already qualified.
$subname = "${package}::" . $subname unless $subname =~ /::/;
$subname = "main" . $subname if substr( $subname, 0, 2 ) eq "::";
# Save the break type for this sub.
- $postponed{$subname} = $break ? "break +0 if $cond" : "compile";
+ $postponed{$subname} = (($action eq 'postpone')
+ ? ( "break +0 if " . $default_cond->($cond) )
+ : "compile");
} ## end elsif ($line =~ ...
# b <filename>:<line> [<condition>]
- elsif ($line =~ /\A(\S+[^:]):(\d+)\s*(.*)/ms) {
- my ($filename, $line_num, $cond) = ($1, $2, $3);
+ elsif (my ($filename, $line_num, $cond)
+ = $line =~ /\A(\S+[^:]):(\d+)\s*(.*)/ms) {
cmd_b_filename_line(
$filename,
$line_num,
);
}
# b <sub name> [<condition>]
- elsif ( $line =~ /^([':A-Za-z_][':\w]*(?:\[.*\])?)\s*(.*)/ ) {
+ elsif ( my ($new_subname, $new_cond) =
+ $line =~ /^([':A-Za-z_][':\w]*(?:\[.*\])?)\s*(.*)/ ) {
#
- $subname = $1;
- my $cond = length $2 ? $2 : '1';
- &cmd_b_sub( $subname, $cond );
+ $subname = $new_subname;
+ cmd_b_sub( $subname, $default_cond->($new_cond) );
}
# b <line> [<condition>].
- elsif ( $line =~ /^(\d*)\s*(.*)/ ) {
+ elsif ( my ($line_n, $cond) = $line =~ /^(\d*)\s*(.*)/ ) {
# Capture the line. If none, it's the current line.
- $line = $1 || $dbline;
-
- # If there's no condition, make it '1'.
- my $cond = length $2 ? $2 : '1';
+ $line = $line_n || $dbline;
# Break on line.
- &cmd_b_line( $line, $cond );
+ cmd_b_line( $line, $default_cond->($cond) );
}
# Line didn't make sense.
else {
print "confused by line($line)?\n";
}
+
+ return;
} ## end sub cmd_b
=head3 C<break_on_load> (API)
=cut
sub break_on_line {
- my ( $i, $cond ) = @_;
-
- # Always true if no condition supplied.
- $cond = 1 unless @_ >= 2;
+ my $i = shift;
+ my $cond = @_ ? shift(@_) : 1;
my $inii = $i;
my $after = '';
_set_breakpoint_enabled_status($filename, $i, 1);
}
+
+ return;
} ## end sub break_on_line
=head3 cmd_b_line(line, [condition]) (command)
=cut
sub break_on_filename_line {
- my ( $f, $i, $cond ) = @_;
-
- # Always true if condition left off.
- $cond = 1 unless @_ >= 3;
+ my $f = shift;
+ my $i = shift;
+ my $cond = @_ ? shift(@_) : 1;
# Switch the magical hash temporarily.
local *dbline = $main::{ '_<' . $f };
# Add the breakpoint.
break_on_line( $i, $cond );
+
+ return;
} ## end sub break_on_filename_line
=head3 break_on_filename_line_range(file, from, to, [condition]) (API)
=cut
sub break_on_filename_line_range {
- my ( $f, $from, $to, $cond ) = @_;
+ my $f = shift;
+ my $from = shift;
+ my $to = shift;
+ my $cond = @_ ? shift(@_) : 1;
# Find a breakable line if there is one.
my $i = breakable_line_in_filename( $f, $from, $to );
- # Always true if missing.
- $cond = 1 unless @_ >= 3;
-
# Add the breakpoint.
break_on_filename_line( $f, $i, $cond );
+
+ return;
} ## end sub break_on_filename_line_range
=head3 subroutine_filename_lines(subname, [condition]) (API)
=cut
sub subroutine_filename_lines {
- my ( $subname, $cond ) = @_;
+ my ( $subname ) = @_;
# Returned value from find_sub() is fullpathname:startline-endline.
- # The match creates the list (fullpathname, start, end). Falling off
- # the end of the subroutine returns this implicitly.
- find_sub($subname) =~ /^(.*):(\d+)-(\d+)$/;
+ # The match creates the list (fullpathname, start, end).
+ return (find_sub($subname) =~ /^(.*):(\d+)-(\d+)$/);
} ## end sub subroutine_filename_lines
=head3 break_subroutine(subname) (API)
# Put a break the first place possible in the range of lines
# that make up this subroutine.
break_on_filename_line_range( $file, $s, $e, $cond );
+
+ return;
} ## end sub break_subroutine
=head3 cmd_b_sub(subname, [condition]) (command)
Display the current thread id:
- e
+ e
This could be how (when implemented) to send commands to this thread id (e cmd)
or that thread id (e tid cmd).
sub cmd_e {
my $cmd = shift;
my $line = shift;
- unless (exists($INC{'threads.pm'})) {
- print "threads not loaded($ENV{PERL5DB_THREADED})
- please run the debugger with PERL5DB_THREADED=1 set in the environment\n";
- } else {
- my $tid = threads->tid;
- print "thread id: $tid\n";
- }
+ unless (exists($INC{'threads.pm'})) {
+ print "threads not loaded($ENV{PERL5DB_THREADED})
+ please run the debugger with PERL5DB_THREADED=1 set in the environment\n";
+ } else {
+ my $tid = threads->tid;
+ print "thread id: $tid\n";
+ }
} ## end sub cmd_e
=head3 C<cmd_E> - list of thread ids
Display the list of available thread ids:
- E
+ E
This could be used (when implemented) to send commands to all threads (E cmd).
sub cmd_E {
my $cmd = shift;
my $line = shift;
- unless (exists($INC{'threads.pm'})) {
- print "threads not loaded($ENV{PERL5DB_THREADED})
- please run the debugger with PERL5DB_THREADED=1 set in the environment\n";
- } else {
- my $tid = threads->tid;
- print "thread ids: ".join(', ',
- map { ($tid == $_->tid ? '<'.$_->tid.'>' : $_->tid) } threads->list
- )."\n";
- }
+ unless (exists($INC{'threads.pm'})) {
+ print "threads not loaded($ENV{PERL5DB_THREADED})
+ please run the debugger with PERL5DB_THREADED=1 set in the environment\n";
+ } else {
+ my $tid = threads->tid;
+ print "thread ids: ".join(', ',
+ map { ($tid == $_->tid ? '<'.$_->tid.'>' : $_->tid) } threads->list
+ )."\n";
+ }
} ## end sub cmd_E
=head3 C<cmd_h> - help command (command)
my $line = shift;
foreach my $isa ( split( /\s+/, $line ) ) {
$evalarg = $isa;
- ($isa) = &eval;
+ ($isa) = DB::eval();
no strict 'refs';
print join(
', ',
# Set up for DB::eval() - evaluate in *user* context.
$evalarg = $1;
# $evalarg = $2;
- my ($s) = &eval;
+ my ($s) = DB::eval();
# Ooops. Bad scalar.
if ($@) {
=cut
+sub _add_watch_expr {
+ my $expr = shift;
+
+ # ... save it.
+ push @to_watch, $expr;
+
+ # Parameterize DB::eval and call it to get the expression's value
+ # in the user's context. This version can handle expressions which
+ # return a list value.
+ $evalarg = $expr;
+ my ($val) = join( ' ', DB::eval() );
+ $val = ( defined $val ) ? "'$val'" : 'undef';
+
+ # Save the current value of the expression.
+ push @old_watch, $val;
+
+ # We are now watching expressions.
+ $trace |= 2;
+
+ return;
+}
+
sub cmd_w {
my $cmd = shift;
my $expr = shift || '';
# If expression is not null ...
- if ( $expr =~ /^(\S.*)/ ) {
-
- # ... save it.
- push @to_watch, $expr;
-
- # Parameterize DB::eval and call it to get the expression's value
- # in the user's context. This version can handle expressions which
- # return a list value.
- $evalarg = $expr;
- my ($val) = join( ' ', &eval );
- $val = ( defined $val ) ? "'$val'" : 'undef';
-
- # Save the current value of the expression.
- push @old_watch, $val;
-
- # We are now watching expressions.
- $trace |= 2;
+ if ( $expr =~ /\A\S/ ) {
+ _add_watch_expr($expr);
} ## end if ($expr =~ /^(\S.*)/)
# You have to give one to get one.
else {
print $OUT "Adding a watch-expression requires an expression\n"; # hint
}
-} ## end sub cmd_w
+
+ return;
+}
=head3 C<cmd_W> - delete watch expressions (command)
$file = $file eq '-e' ? $file : "file '$file'" unless $short;
# Get the actual sub's name, and shorten to $maxtrace's requirement.
- $s = $sub[$i]{sub};
+ $s = $sub[$i]{'sub'};
$s = ( substr $s, 0, $maxtrace - 3 ) . '...' if length $s > $maxtrace;
# Short report uses trimmed file and sub names.
# We save, change, then restore STDIN and STDOUT to avoid fork() since
# some non-Unix systems can do system() but have problems with fork().
- open( SAVEIN, "<&STDIN" ) || &warn("Can't save STDIN");
- open( SAVEOUT, ">&STDOUT" ) || &warn("Can't save STDOUT");
- open( STDIN, "<&IN" ) || &warn("Can't redirect STDIN");
- open( STDOUT, ">&OUT" ) || &warn("Can't redirect STDOUT");
+ open( SAVEIN, "<&STDIN" ) || DB::warn("Can't save STDIN");
+ open( SAVEOUT, ">&STDOUT" ) || DB::warn("Can't save STDOUT");
+ open( STDIN, "<&IN" ) || DB::warn("Can't redirect STDIN");
+ open( STDOUT, ">&OUT" ) || DB::warn("Can't redirect STDOUT");
# XXX: using csh or tcsh destroys sigint retvals!
system(@_);
- open( STDIN, "<&SAVEIN" ) || &warn("Can't restore STDIN");
- open( STDOUT, ">&SAVEOUT" ) || &warn("Can't restore STDOUT");
+ open( STDIN, "<&SAVEIN" ) || DB::warn("Can't restore STDIN");
+ open( STDOUT, ">&SAVEOUT" ) || DB::warn("Can't restore STDOUT");
close(SAVEIN);
close(SAVEOUT);
# most of the $? crud was coping with broken cshisms
if ( $? >> 8 ) {
- &warn( "(Command exited ", ( $? >> 8 ), ")\n" );
+ DB::warn( "(Command exited ", ( $? >> 8 ), ")\n" );
}
elsif ($?) {
- &warn(
+ DB::warn(
"(Command died of SIG#",
( $? & 127 ),
( ( $? & 128 ) ? " -- core dumped" : "" ),
sub os2_get_fork_TTY { # A simplification of the following (and works without):
local $\ = '';
( my $name = $0 ) =~ s,^.*[/\\],,s;
- my %opt = ( title => "Daughter Perl debugger $pids $name",
- ($rl ? (read_by_key => 1) : ()) );
+ my %opt = ( title => "Daughter Perl debugger $pids $name",
+ ($rl ? (read_by_key => 1) : ()) );
require OS2::Process;
my ($in, $out, $pid) = eval { OS2::Process::io_term(related => 0, %opt) }
or return;
return unless $version=$ENV{TERM_PROGRAM_VERSION};
foreach my $entry (@script_versions) {
- if ($version>=$entry->[0]) {
- $script=$entry->[1];
- last;
- }
+ if ($version>=$entry->[0]) {
+ $script=$entry->[1];
+ last;
+ }
}
return unless defined($script);
return unless open($pipe,'-|','/usr/bin/osascript','-e',$script);
=cut
sub print_help {
- my $help_str = (@_);
+ my $help_str = shift;
# Restore proper alignment destroyed by eeevil I<> and B<>
# ornaments: A pox on both their houses!
# Extract from all the symbols in this class.
my $class_ref = do { no strict "refs"; \%{$class . '::'} };
while (my ($name, $glob) = each %$class_ref) {
- # references directly in the symbol table are Proxy Constant
- # Subroutines, and are by their very nature defined
- # Otherwise, check if the thing is a typeglob, and if it is, it decays
- # to a subroutine reference, which can be tested by defined.
- # $glob might also be the value -1 (from sub foo;)
- # or (say) '$$' (from sub foo ($$);)
- # \$glob will be SCALAR in both cases.
- if ((ref $glob || ($glob && ref \$glob eq 'GLOB' && defined &$glob))
- && !$seen{$name}++) {
- push @to_print, "$prepend$name\n";
- }
+ # references directly in the symbol table are Proxy Constant
+ # Subroutines, and are by their very nature defined
+ # Otherwise, check if the thing is a typeglob, and if it is, it decays
+ # to a subroutine reference, which can be tested by defined.
+ # $glob might also be the value -1 (from sub foo;)
+ # or (say) '$$' (from sub foo ($$);)
+ # \$glob will be SCALAR in both cases.
+ if ((ref $glob || ($glob && ref \$glob eq 'GLOB' && defined &$glob))
+ && !$seen{$name}++) {
+ push @to_print, "$prepend$name\n";
+ }
}
{
- local $\ = '';
- local $, = '';
- print $DB::OUT $_ foreach sort @to_print;
+ local $\ = '';
+ local $, = '';
+ print $DB::OUT $_ foreach sort @to_print;
}
# If the $crawl_upward argument is false, just quit here.
# Get the current value of the expression.
# Doesn't handle expressions returning list values!
$evalarg = $1;
- my ($val) = &eval;
+ my ($val) = DB::eval();
$val = ( defined $val ) ? "'$val'" : 'undef';
# Save it.