This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Unicode: add the case folding table.
[perl5.git] / lib / perl5db.pl
index 7b0567c..aab1a68 100644 (file)
@@ -2,17 +2,9 @@ package DB;
 
 # Debugger for Perl 5.00x; perl5db.pl patch level:
 
-$VERSION = 1.0403;
+$VERSION = 1.14;
 $header = "perl5db.pl version $VERSION";
 
-# Enhanced by ilya@math.ohio-state.edu (Ilya Zakharevich)
-# Latest version available: ftp://ftp.math.ohio-state.edu/pub/users/ilya/perl
-
-# modified Perl debugger, to be run from Emacs in perldb-mode
-# Ray Lischner (uunet!mntgfx!lisch) as of 5 Nov 1990
-# Johan Vromans -- upgrade to 4.0 pl 10
-# Ilya Zakharevich -- patches after 5.001 (and some before ;-)
-
 #
 # This file is automatically included if you do perl -d.
 # It's probably not useful to include this yourself.
@@ -33,7 +25,7 @@ $header = "perl5db.pl version $VERSION";
 # if caller() is called from the package DB, it provides some
 # additional data.
 #
-# The array @{$main::{'_<'.$filename} is the line-by-line contents of
+# The array @{$main::{'_<'.$filename}} is the line-by-line contents of
 # $filename.
 #
 # The hash %{'_<'.$filename} contains breakpoints and action (it is
@@ -42,7 +34,7 @@ $header = "perl5db.pl version $VERSION";
 # interpreter, though the values used by perl5db.pl have the form
 # "$break_condition\0$action". Values are magical in numeric context.
 #
-# The scalar ${'_<'.$filename} contains "_<$filename".
+# The scalar ${'_<'.$filename} contains $filename.
 #
 # Note that no subroutine call is possible until &DB::sub is defined
 # (for subroutines defined outside of the package DB). In fact the same is
@@ -76,6 +68,8 @@ $header = "perl5db.pl version $VERSION";
 # LineInfo - file or pipe to print line number info to.  If it is a
 # pipe, a short "emacs like" message is used.
 #
+# RemotePort - host:port to connect to on remote host for remote debugging.
+#
 # Example $rcfile: (delete leading hashes!)
 #
 # &parse_options("NonStop=1 LineInfo=db.out");
@@ -86,6 +80,14 @@ $header = "perl5db.pl version $VERSION";
 # reset LineInfo to something "interactive"!)
 #
 ##################################################################
+
+# Enhanced by ilya@math.ohio-state.edu (Ilya Zakharevich)
+
+# modified Perl debugger, to be run from Emacs in perldb-mode
+# Ray Lischner (uunet!mntgfx!lisch) as of 5 Nov 1990
+# Johan Vromans -- upgrade to 4.0 pl 10
+# Ilya Zakharevich -- patches after 5.001 (and some before ;-)
+
 # Changelog:
 
 # A lot of things changed after 0.94. First of all, core now informs
@@ -116,7 +118,7 @@ $header = "perl5db.pl version $VERSION";
 #      Some additional words on internal work of debugger.
 #      `b load filename' implemented.
 #      `b postpone subr' implemented.
-#      now only `q' exits debugger (overwriteable on $inhibit_exit).
+#      now only `q' exits debugger (overwritable on $inhibit_exit).
 #      When restarting debugger breakpoints/actions persist.
 #     Buglet: When restarting debugger only one breakpoint/action per 
 #              autoloaded function persists.
@@ -126,7 +128,7 @@ $header = "perl5db.pl version $VERSION";
 #      new `inhibitExit' option.
 #      printing of a very long statement interruptible.
 # Changes: 0.98: New command `m' for printing possible methods
-#      'l -' is a synonim for `-'.
+#      'l -' is a synonym for `-'.
 #      Cosmetic bugs in printing stack trace.
 #      `frame' & 8 to print "expanded args" in stack trace.
 #      Can list/break in imported subs.
@@ -142,7 +144,116 @@ $header = "perl5db.pl version $VERSION";
 #      `b load' strips trailing whitespace.
 #      completion ignores leading `|'; takes into account current package
 #      when completing a subroutine name (same for `l').
-
+# Changes: 1.07: Many fixed by tchrist 13-March-2000
+#   BUG FIXES:
+#   + Added bare minimal security checks on perldb rc files, plus
+#     comments on what else is needed.
+#   + Fixed the ornaments that made "|h" completely unusable.
+#     They are not used in print_help if they will hurt.  Strip pod
+#     if we're paging to less.
+#   + Fixed mis-formatting of help messages caused by ornaments
+#     to restore Larry's original formatting.  
+#   + Fixed many other formatting errors.  The code is still suboptimal, 
+#     and needs a lot of work at restructuring.  It's also misindented
+#     in many places.
+#   + Fixed bug where trying to look at an option like your pager
+#     shows "1".  
+#   + Fixed some $? processing.  Note: if you use csh or tcsh, you will
+#     lose.  You should consider shell escapes not using their shell,
+#     or else not caring about detailed status.  This should really be
+#     unified into one place, too.
+#   + Fixed bug where invisible trailing whitespace on commands hoses you,
+#     tricking Perl into thinking you weren't calling a debugger command!
+#   + Fixed bug where leading whitespace on commands hoses you.  (One
+#     suggests a leading semicolon or any other irrelevant non-whitespace
+#     to indicate literal Perl code.)
+#   + Fixed bugs that ate warnings due to wrong selected handle.
+#   + Fixed a precedence bug on signal stuff.
+#   + Fixed some unseemly wording.
+#   + Fixed bug in help command trying to call perl method code.
+#   + Fixed to call dumpvar from exception handler.  SIGPIPE killed us.
+#   ENHANCEMENTS:
+#   + Added some comments.  This code is still nasty spaghetti.
+#   + Added message if you clear your pre/post command stacks which was
+#     very easy to do if you just typed a bare >, <, or {.  (A command
+#     without an argument should *never* be a destructive action; this
+#     API is fundamentally screwed up; likewise option setting, which
+#     is equally buggered.)
+#   + Added command stack dump on argument of "?" for >, <, or {.
+#   + Added a semi-built-in doc viewer command that calls man with the
+#     proper %Config::Config path (and thus gets caching, man -k, etc),
+#     or else perldoc on obstreperous platforms.
+#   + Added to and rearranged the help information.
+#   + Detected apparent misuse of { ... } to declare a block; this used
+#     to work but now is a command, and mysteriously gave no complaint.
+#
+# Changes: 1.08: Apr 25, 2001  Jon Eveland <jweveland@yahoo.com>
+#   BUG FIX:
+#   + This patch to perl5db.pl cleans up formatting issues on the help
+#     summary (h h) screen in the debugger.  Mostly columnar alignment
+#     issues, plus converted the printed text to use all spaces, since
+#     tabs don't seem to help much here.
+#
+# Changes: 1.09: May 19, 2001  Ilya Zakharevich <ilya@math.ohio-state.edu>
+#   0) Minor bugs corrected;
+#   a) Support for auto-creation of new TTY window on startup, either
+#      unconditionally, or if started as a kid of another debugger session;
+#   b) New `O'ption CreateTTY
+#       I<CreateTTY>       bits control attempts to create a new TTY on events:
+#                          1: on fork()   2: debugger is started inside debugger
+#                          4: on startup
+#   c) Code to auto-create a new TTY window on OS/2 (currently one one
+#      extra window per session - need named pipes to have more...);
+#   d) Simplified interface for custom createTTY functions (with a backward
+#      compatibility hack); now returns the TTY name to use; return of ''
+#      means that the function reset the I/O handles itself;
+#   d') Better message on the semantic of custom createTTY function;
+#   e) Convert the existing code to create a TTY into a custom createTTY
+#      function;
+#   f) Consistent support for TTY names of the form "TTYin,TTYout";
+#   g) Switch line-tracing output too to the created TTY window;
+#   h) make `b fork' DWIM with CORE::GLOBAL::fork;
+#   i) High-level debugger API cmd_*():
+#      cmd_b_load($filenamepart)            # b load filenamepart
+#      cmd_b_line($lineno [, $cond])        # b lineno [cond]
+#      cmd_b_sub($sub [, $cond])            # b sub [cond]
+#      cmd_stop()                           # Control-C
+#      cmd_d($lineno)                       # d lineno
+#      The cmd_*() API returns FALSE on failure; in this case it outputs
+#      the error message to the debugging output.
+#   j) Low-level debugger API
+#      break_on_load($filename)             # b load filename
+#      @files = report_break_on_load()      # List files with load-breakpoints
+#      breakable_line_in_filename($name, $from [, $to])
+#                                           # First breakable line in the
+#                                           # range $from .. $to.  $to defaults
+#                                           # to $from, and may be less than $to
+#      breakable_line($from [, $to])        # Same for the current file
+#      break_on_filename_line($name, $lineno [, $cond])
+#                                           # Set breakpoint,$cond defaults to 1
+#      break_on_filename_line_range($name, $from, $to [, $cond])
+#                                           # As above, on the first
+#                                           # breakable line in range
+#      break_on_line($lineno [, $cond])     # As above, in the current file
+#      break_subroutine($sub [, $cond])     # break on the first breakable line
+#      ($name, $from, $to) = subroutine_filename_lines($sub)
+#                                           # The range of lines of the text
+#      The low-level API returns TRUE on success, and die()s on failure.
+#
+# Changes: 1.10: May 23, 2001  Daniel Lewart <d-lewart@uiuc.edu>
+#   BUG FIXES:
+#   + Fixed warnings generated by "perl -dWe 42"
+#   + Corrected spelling errors
+#   + Squeezed Help (h) output into 80 columns
+#
+# Changes: 1.11: May 24, 2001  David Dyck <dcd@tc.fluke.com>
+#   + Made "x @INC" work like it used to
+#
+# Changes: 1.12: May 24, 2001  Daniel Lewart <d-lewart@uiuc.edu>
+#   + Fixed warnings generated by "O" (Show debugger options)
+#   + Fixed warnings generated by "p 42" (Print expression)
+# Changes: 1.13: Jun 19, 2001 Scott.L.Miller@compaq.com
+#   + Added windowSize option 
 ####################################################################
 
 # Needed for the statement after exec():
@@ -179,7 +290,8 @@ $inhibit_exit = $option{PrintRet} = 1;
                  TTY noTTY ReadLine NonStop LineInfo maxTraceLen
                  recallCommand ShellBang pager tkRunning ornaments
                  signalLevel warnLevel dieLevel inhibit_exit
-                 ImmediateStop bareStringify);
+                 ImmediateStop bareStringify CreateTTY
+                 RemotePort windowSize);
 
 %optionVars    = (
                 hashDepth      => \$dumpvar::hashDepth,
@@ -190,13 +302,16 @@ $inhibit_exit = $option{PrintRet} = 1;
                 HighBit        => \$dumpvar::quoteHighBit,
                 undefPrint     => \$dumpvar::printUndef,
                 globPrint      => \$dumpvar::globPrint,
-                UsageOnly      => \$dumpvar::usageOnly,     
+                UsageOnly      => \$dumpvar::usageOnly,
+                CreateTTY      => \$CreateTTY,
                 bareStringify  => \$dumpvar::bareStringify,
                 frame          => \$frame,
                 AutoTrace      => \$trace,
                 inhibit_exit   => \$inhibit_exit,
                 maxTraceLen    => \$maxtrace,
                 ImmediateStop  => \$ImmediateStop,
+                RemotePort     => \$remoteport,
+                windowSize     => \$window,
 );
 
 %optionAction  = (
@@ -216,6 +331,7 @@ $inhibit_exit = $option{PrintRet} = 1;
                  dieLevel      => \&dieLevel,
                  tkRunning     => \&tkRunning,
                  ornaments     => \&ornaments,
+                 RemotePort    => \&RemotePort,
                 );
 
 %optionRequire = (
@@ -225,43 +341,114 @@ $inhibit_exit = $option{PrintRet} = 1;
                 );
 
 # These guys may be defined in $ENV{PERL5DB} :
-$rl = 1 unless defined $rl;
-$warnLevel = 1 unless defined $warnLevel;
-$dieLevel = 1 unless defined $dieLevel;
-$signalLevel = 1 unless defined $signalLevel;
-$pre = [] unless defined $pre;
-$post = [] unless defined $post;
-$pretype = [] unless defined $pretype;
+$rl            = 1     unless defined $rl;
+$warnLevel     = 1     unless defined $warnLevel;
+$dieLevel      = 1     unless defined $dieLevel;
+$signalLevel   = 1     unless defined $signalLevel;
+$pre           = []    unless defined $pre;
+$post          = []    unless defined $post;
+$pretype       = []    unless defined $pretype;
+$CreateTTY     = 3     unless defined $CreateTTY;
+
 warnLevel($warnLevel);
 dieLevel($dieLevel);
 signalLevel($signalLevel);
-&pager((defined($ENV{PAGER}) 
+
+&pager(
+    (defined($ENV{PAGER}) 
        ? $ENV{PAGER}
        : ($^O eq 'os2' 
           ? 'cmd /c more' 
           : 'more'))) unless defined $pager;
+setman();
 &recallCommand("!") unless defined $prc;
 &shellBang("!") unless defined $psh;
+sethelp();
 $maxtrace = 400 unless defined $maxtrace;
+$ini_pids = $ENV{PERLDB_PIDS};
+if (defined $ENV{PERLDB_PIDS}) {
+  $pids = "[$ENV{PERLDB_PIDS}]";
+  $ENV{PERLDB_PIDS} .= "->$$";
+  $term_pid = -1;
+} else {
+  $ENV{PERLDB_PIDS} = "$$";
+  $pids = '';
+  $term_pid = $$;
+}
+$pidprompt = '';
+*emacs = $slave_editor if $slave_editor;       # May be used in afterinit()...
 
-if (-e "/dev/tty") {
+if (-e "/dev/tty") {  # this is the wrong metric!
   $rcfile=".perldb";
 } else {
   $rcfile="perldb.ini";
 }
 
+# This isn't really safe, because there's a race
+# between checking and opening.  The solution is to
+# open and fstat the handle, but then you have to read and
+# eval the contents.  But then the silly thing gets
+# your lexical scope, which is unfortunately at best.
+sub safe_do { 
+    my $file = shift;
+
+    # Just exactly what part of the word "CORE::" don't you understand?
+    local $SIG{__WARN__};  
+    local $SIG{__DIE__};    
+
+    unless (is_safe_file($file)) {
+       CORE::warn <<EO_GRIPE;
+perldb: Must not source insecure rcfile $file.
+        You or the superuser must be the owner, and it must not 
+       be writable by anyone but its owner.
+EO_GRIPE
+       return;
+    } 
+
+    do $file;
+    CORE::warn("perldb: couldn't parse $file: $@") if $@;
+}
+
+
+# Verifies that owner is either real user or superuser and that no
+# one but owner may write to it.  This function is of limited use
+# when called on a path instead of upon a handle, because there are
+# no guarantees that filename (by dirent) whose file (by ino) is
+# eventually accessed is the same as the one tested. 
+# Assumes that the file's existence is not in doubt.
+sub is_safe_file {
+    my $path = shift;
+    stat($path) || return;     # mysteriously vaporized
+    my($dev,$ino,$mode,$nlink,$uid,$gid) = stat(_);
+
+    return 0 if $uid != 0 && $uid != $<;
+    return 0 if $mode & 022;
+    return 1;
+}
+
 if (-f $rcfile) {
-    do "./$rcfile";
-} elsif (defined $ENV{LOGDIR} and -f "$ENV{LOGDIR}/$rcfile") {
-    do "$ENV{LOGDIR}/$rcfile";
-} elsif (defined $ENV{HOME} and -f "$ENV{HOME}/$rcfile") {
-    do "$ENV{HOME}/$rcfile";
+    safe_do("./$rcfile");
+} 
+elsif (defined $ENV{HOME} && -f "$ENV{HOME}/$rcfile") {
+    safe_do("$ENV{HOME}/$rcfile");
+}
+elsif (defined $ENV{LOGDIR} && -f "$ENV{LOGDIR}/$rcfile") {
+    safe_do("$ENV{LOGDIR}/$rcfile");
 }
 
 if (defined $ENV{PERLDB_OPTS}) {
   parse_options($ENV{PERLDB_OPTS});
 }
 
+if ( not defined &get_fork_TTY and defined $ENV{TERM} and $ENV{TERM} eq 'xterm'
+     and defined $ENV{WINDOWID} and defined $ENV{DISPLAY} ) { # _inside_ XTERM?
+    *get_fork_TTY = \&xterm_get_fork_TTY;
+} elsif ($^O eq 'os2') {
+    *get_fork_TTY = \&os2_get_fork_TTY;
+}
+
+# Here begin the unreadable code.  It needs fixing.
+
 if (exists $ENV{PERLDB_RESTART}) {
   delete $ENV{PERLDB_RESTART};
   # $restart = 1;
@@ -290,29 +477,39 @@ if (exists $ENV{PERLDB_RESTART}) {
 if ($notty) {
   $runnonstop = 1;
 } else {
-  # Is Perl being run from Emacs?
-  $emacs = ((defined $main::ARGV[0]) and ($main::ARGV[0] eq '-emacs'));
-  $rl = 0, shift(@main::ARGV) if $emacs;
+  # Is Perl being run from a slave editor or graphical debugger?
+  $slave_editor = ((defined $main::ARGV[0]) and ($main::ARGV[0] eq '-emacs'));
+  $rl = 0, shift(@main::ARGV) if $slave_editor;
 
   #require Term::ReadLine;
 
-  if ($^O =~ /cygwin/) {
+  if ($^O eq 'cygwin') {
     # /dev/tty is binary. use stdin for textmode
     undef $console;
   } elsif (-e "/dev/tty") {
     $console = "/dev/tty";
   } elsif ($^O eq 'dos' or -e "con" or $^O eq 'MSWin32') {
     $console = "con";
+  } elsif ($^O eq 'MacOS') {
+    if ($MacPerl::Version !~ /MPW/) {
+      $console = "Dev:Console:Perl Debug"; # Separate window for application
+    } else {
+      $console = "Dev:Console";
+    }
   } else {
     $console = "sys\$command";
   }
 
-  if (($^O eq 'MSWin32') and ($emacs or defined $ENV{EMACS})) {
+  if (($^O eq 'MSWin32') and ($slave_editor or defined $ENV{EMACS})) {
     $console = undef;
   }
 
+  if ($^O eq 'NetWare') {
+       $console = undef;
+  }
+
   # Around a bug:
-  if (defined $ENV{OS2_SHELL} and ($emacs or $ENV{WINDOWID})) { # In OS/2
+  if (defined $ENV{OS2_SHELL} and ($slave_editor or $ENV{WINDOWID})) { # In OS/2
     $console = undef;
   }
 
@@ -322,35 +519,51 @@ if ($notty) {
 
   $console = $tty if defined $tty;
 
-  if (defined $console) {
-    open(IN,"+<$console") || open(IN,"<$console") || open(IN,"<&STDIN");
-    open(OUT,"+>$console") || open(OUT,">$console") || open(OUT,">&STDERR")
-      || open(OUT,">&STDOUT"); # so we don't dongle stdout
+  if (defined $remoteport) {
+    require IO::Socket;
+    $OUT = new IO::Socket::INET( Timeout  => '10',
+                                 PeerAddr => $remoteport,
+                                 Proto    => 'tcp',
+                               );
+    if (!$OUT) { die "Unable to connect to remote host: $remoteport\n"; }
+    $IN = $OUT;
+  } elsif ($CreateTTY & 4) {
+    create_IN_OUT(4);
   } else {
-    open(IN,"<&STDIN");
-    open(OUT,">&STDERR") || open(OUT,">&STDOUT"); # so we don't dongle stdout
-    $console = 'STDIN/OUT';
-  }
-  # so open("|more") can read from STDOUT and so we don't dingle stdin
-  $IN = \*IN;
+    if (defined $console) {
+      my ($i, $o) = split /,/, $console;
+      $o = $i unless defined $o;
+      open(IN,"+<$i") || open(IN,"<$i") || open(IN,"<&STDIN");
+      open(OUT,"+>$o") || open(OUT,">$o") || open(OUT,">&STDERR")
+        || open(OUT,">&STDOUT");       # so we don't dongle stdout
+    } else {
+      open(IN,"<&STDIN");
+      open(OUT,">&STDERR") || open(OUT,">&STDOUT"); # so we don't dongle stdout
+      $console = 'STDIN/OUT';
+    }
+    # so open("|more") can read from STDOUT and so we don't dingle stdin
+    $IN = \*IN;
 
-  $OUT = \*OUT;
-  select($OUT);
+    $OUT = \*OUT;
+  }
+  my $previous = select($OUT);
   $| = 1;                      # for DB::OUT
-  select(STDOUT);
+  select($previous);
 
   $LINEINFO = $OUT unless defined $LINEINFO;
   $lineinfo = $console unless defined $lineinfo;
 
-  $| = 1;                      # for real STDOUT
-
   $header =~ s/.Header: ([^,]+),v(\s+\S+\s+\S+).*$/$1$2/;
   unless ($runnonstop) {
-    print $OUT "\nLoading DB routines from $header\n";
-    print $OUT ("Emacs support ",
-               $emacs ? "enabled" : "available",
-               ".\n");
-    print $OUT "\nEnter h or `h h' for help, run `perldoc perldebug' for more help.\n\n";
+    if ($term_pid eq '-1') {
+      print $OUT "\nDaughter DB session started...\n";
+    } else {
+      print $OUT "\nLoading DB routines from $header\n";
+      print $OUT ("Editor support ",
+                 $slave_editor ? "enabled" : "available",
+                 ".\n");
+      print $OUT "\nEnter h or `h h' for help, or `$doccmd perldebug' for more help.\n\n";
+    }
   }
 }
 
@@ -390,7 +603,7 @@ sub DB {
       "package $package;";     # this won't let them modify, alas
     local(*dbline) = $main::{'_<' . $filename};
     $max = $#dbline;
-    if (($stop,$action) = split(/\0/,$dbline{$line})) {
+    if ($dbline{$line} && (($stop,$action) = split(/\0/,$dbline{$line}))) {
        if ($stop eq '1') {
            $signal |= 1;
        } elsif ($stop) {
@@ -423,9 +636,9 @@ EOP
     $was_signal = $signal;
     $signal = 0;
     if ($single || ($trace & 1) || $was_signal) {
-       if ($emacs) {
+       if ($slave_editor) {
            $position = "\032\032$filename:$line:0\n";
-           print $LINEINFO $position;
+           print_lineinfo($position);
        } elsif ($package eq 'DB::fake') {
          $term || &setterm;
          print_help(<<EOP);
@@ -434,7 +647,7 @@ Debugged program terminated.  Use B<q> to quit or B<R> to restart,
   B<h q>, B<h R> or B<h O> to get additional info.  
 EOP
          $package = 'main';
-         $usercontext = '($@, $!, $,, $/, $\, $^W) = @saved;' .
+         $usercontext = '($@, $!, $^E, $,, $/, $\, $^W) = @saved;' .
            "package $package;";        # this won't let them modify, alas
        } else {
            $sub =~ s/\'/::/;
@@ -450,9 +663,9 @@ EOP
                $position = "$prefix$line$infix$dbline[$line]$after";
            }
            if ($frame) {
-               print $LINEINFO ' ' x $stack_depth, "$line:\t$dbline[$line]$after";
+               print_lineinfo(' ' x $stack_depth, "$line:\t$dbline[$line]$after");
            } else {
-               print $LINEINFO $position;
+               print_lineinfo($position);
            }
            for ($i = $line + 1; $i <= $max && $dbline[$i] == 0; ++$i) { #{ vi
                last if $dbline[$i] =~ /^\s*[\;\}\#\n]/;
@@ -461,9 +674,9 @@ EOP
                $incr_pos = "$prefix$i$infix$dbline[$i]$after";
                $position .= $incr_pos;
                if ($frame) {
-                   print $LINEINFO ' ' x $stack_depth, "$i:\t$dbline[$i]$after";
+                   print_lineinfo(' ' x $stack_depth, "$i:\t$dbline[$i]$after");
                } else {
-                   print $LINEINFO $incr_pos;
+                   print_lineinfo($incr_pos);
                }
            }
        }
@@ -481,10 +694,11 @@ EOP
        @typeahead = (@$pretype, @typeahead);
       CMD:
        while (($term || &setterm),
-              ($term_pid == $$ or &resetterm),
-              defined ($cmd=&readline("  DB" . ('<' x $level) .
+              ($term_pid == $$ or resetterm(1)),
+              defined ($cmd=&readline("$pidprompt  DB" . ('<' x $level) .
                                       ($#hist+1) . ('>' x $level) .
-                                      " "))) {
+                                      " "))) 
+        {
                $single = 0;
                $signal = 0;
                $cmd =~ s/\\$/\n/ && do {
@@ -494,19 +708,34 @@ EOP
                $cmd =~ /^$/ && ($cmd = $laststep);
                push(@hist,$cmd) if length($cmd) > 1;
              PIPE: {
+                   $cmd =~ s/^\s+//s;   # trim annoying leading whitespace
+                   $cmd =~ s/\s+$//s;   # trim annoying trailing whitespace
                    ($i) = split(/\s+/,$cmd);
-                   eval "\$cmd =~ $alias{$i}", print $OUT $@ if $alias{$i};
-                   $cmd =~ /^q$/ && ($exiting = 1) && exit 0;
+                   if ($alias{$i}) { 
+                       # squelch the sigmangler
+                       local $SIG{__DIE__};
+                       local $SIG{__WARN__};
+                       eval "\$cmd =~ $alias{$i}";
+                       if ($@) {
+                           print $OUT "Couldn't evaluate `$i' alias: $@";
+                           next CMD;
+                       } 
+                   }
+                   $cmd =~ /^q$/ && ($fall_off_end = 1) && clean_ENV() && exit $?;
                    $cmd =~ /^h$/ && do {
                        print_help($help);
                        next CMD; };
                    $cmd =~ /^h\s+h$/ && do {
                        print_help($summary);
                        next CMD; };
-                   $cmd =~ /^h\s+(\S)$/ && do {
-                       my $asked = "\Q$1";
-                       if ($help =~ /^(?:[IB]<)$asked/m) {
-                         while ($help =~ /^((?:[IB]<)$asked([\s\S]*?)\n)(?!\s)/mg) {
+                   # support long commands; otherwise bogus errors
+                   # happen when you ask for h on <CR> for example
+                   $cmd =~ /^h\s+(\S.*)$/ && do {      
+                       my $asked = $1;                 # for proper errmsg
+                       my $qasked = quotemeta($asked); # for searching
+                       # XXX: finds CR but not <CR>
+                       if ($help =~ /^<?(?:[IB]<)$qasked/m) {
+                         while ($help =~ /^(<?(?:[IB]<)$qasked([\s\S]*?)\n)(?!\s)/mg) {
                            print_help($1);
                          }
                        } else {
@@ -514,7 +743,7 @@ EOP
                        }
                        next CMD; };
                    $cmd =~ /^t$/ && do {
-                       ($trace & 1) ? ($trace &= ~1) : ($trace |= 1);
+                       $trace ^= 1;
                        print $OUT "Trace = " .
                            (($trace & 1) ? "on" : "off" ) . "\n";
                        next CMD; };
@@ -539,7 +768,11 @@ EOP
                        if (defined &main::dumpvar) {
                            local $frame = 0;
                            local $doret = -2;
-                           &main::dumpvar($packname,@vars);
+                           # must detect sigpipe failures
+                           eval { &main::dumpvar($packname,@vars) };
+                           if ($@) {
+                               die unless $@ =~ /dumpvar print failed/;
+                           } 
                        } else {
                            print $OUT "dumpvar.pl not available.\n";
                        }
@@ -581,16 +814,29 @@ EOP
                          }
                      };
                    $cmd =~ s/^l\s+-\s*$/-/;
-                   $cmd =~ /^l\b\s*([\':A-Za-z_][\':\w]*)/ && do {
-                       $subname = $1;
+                   $cmd =~ /^([lb])\b\s*(\$.*)/s && do {
+                       $evalarg = $2;
+                       my ($s) = &eval;
+                       print($OUT "Error: $@\n"), next CMD if $@;
+                       $s = CvGV_name($s);
+                       print($OUT "Interpreted as: $1 $s\n");
+                       $cmd = "$1 $s";
+                   };
+                   $cmd =~ /^l\b\s*([\':A-Za-z_][\':\w]*(\[.*\])?)/s && do {
+                       my $s = $subname = $1;
                        $subname =~ s/\'/::/;
                        $subname = $package."::".$subname 
                          unless $subname =~ /::/;
+                       $subname = "CORE::GLOBAL::$s"
+                         if not defined &$subname and $s !~ /::/
+                            and defined &{"CORE::GLOBAL::$s"};
                        $subname = "main".$subname if substr($subname,0,2) eq "::";
-                       @pieces = split(/:/,find_sub($subname));
+                       @pieces = split(/:/,find_sub($subname) || $sub{$subname});
                        $subrange = pop @pieces;
                        $file = join(':', @pieces);
                        if ($file ne $filename) {
+                           print $OUT "Switching to file '$file'.\n"
+                               unless $slave_editor;
                            *dbline = $main::{'_<' . $file};
                            $max = $#dbline;
                            $filename = $file;
@@ -610,7 +856,7 @@ EOP
                        $filename = $filename_ini;
                        *dbline = $main::{'_<' . $filename};
                        $max = $#dbline;
-                       print $LINEINFO $position;
+                       print_lineinfo($position);
                        next CMD };
                    $cmd =~ /^w\b\s*(\d*)$/ && do {
                        $incr = $window - 1;
@@ -638,12 +884,14 @@ EOP
                        $i = $line if $i eq '.';
                        $i = 1 if $i < 1;
                        $incr = $end - $i;
-                       if ($emacs) {
+                       if ($slave_editor) {
                            print $OUT "\032\032$filename:$i:0\n";
                            $i = $end;
                        } else {
                            for (; $i <= $end; $i++) {
-                               ($stop,$action) = split(/\0/, $dbline{$i});
+                               my ($stop,$action);
+                               ($stop,$action) = split(/\0/, $dbline{$i}) if
+                                   $dbline{$i};
                                $arrow = ($i==$line 
                                          and $filename eq $filename_ini) 
                                  ?  '==>' 
@@ -674,11 +922,14 @@ EOP
                                }
                            }
                        }
+                       
+                       if (not $had_breakpoints{$file} &= ~1) {
+                           delete $had_breakpoints{$file};
+                       }
                      }
                      undef %postponed;
                      undef %postponed_file;
                      undef %break_on_load;
-                     undef %had_breakpoints;
                      next CMD; };
                    $cmd =~ /^L$/ && do {
                      my $file;
@@ -689,7 +940,7 @@ EOP
                        
                        for ($i = 1; $i <= $max; $i++) {
                            if (defined $dbline{$i}) {
-                               print "$file:\n" unless $was++;
+                               print $OUT "$file:\n" unless $was++;
                                print $OUT " $i:\t", $dbline[$i];
                                ($stop,$action) = split(/\0/, $dbline{$i});
                                print $OUT "   break if (", $stop, ")\n"
@@ -748,61 +999,33 @@ EOP
                      next CMD; };
                    $cmd =~ /^b\b\s*load\b\s*(.*)/ && do {
                        my $file = $1; $file =~ s/\s+$//;
-                       {
-                         $break_on_load{$file} = 1;
-                         $break_on_load{$::INC{$file}} = 1 if $::INC{$file};
-                         $file .= '.pm', redo unless $file =~ /\./;
-                       }
-                       $had_breakpoints{$file} = 1;
-                       print $OUT "Will stop on load of `@{[join '\', `', sort keys %break_on_load]}'.\n";
+                       cmd_b_load($file);
                        next CMD; };
                    $cmd =~ /^b\b\s*(postpone|compile)\b\s*([':A-Za-z_][':\w]*)\s*(.*)/ && do {
-                       my $cond = $3 || '1';
+                       my $cond = length $3 ? $3 : '1';
                        my ($subname, $break) = ($2, $1 eq 'postpone');
-                       $subname =~ s/\'/::/;
+                       $subname =~ s/\'/::/g;
                        $subname = "${'package'}::" . $subname
                          unless $subname =~ /::/;
                        $subname = "main".$subname if substr($subname,0,2) eq "::";
                        $postponed{$subname} = $break 
                          ? "break +0 if $cond" : "compile";
                        next CMD; };
-                   $cmd =~ /^b\b\s*([':A-Za-z_][':\w]*)\s*(.*)/ && do {
+                   $cmd =~ /^b\b\s*([':A-Za-z_][':\w]*(?:\[.*\])?)\s*(.*)/ && do {
                        $subname = $1;
-                       $cond = $2 || '1';
-                       $subname =~ s/\'/::/;
-                       $subname = "${'package'}::" . $subname
-                         unless $subname =~ /::/;
-                       $subname = "main".$subname if substr($subname,0,2) eq "::";
-                       # Filename below can contain ':'
-                       ($file,$i) = (find_sub($subname) =~ /^(.*):(.*)$/);
-                       $i += 0;
-                       if ($i) {
-                           $filename = $file;
-                           *dbline = $main::{'_<' . $filename};
-                           $had_breakpoints{$filename} = 1;
-                           $max = $#dbline;
-                           ++$i while $dbline[$i] == 0 && $i < $max;
-                           $dbline{$i} =~ s/^[^\0]*/$cond/;
-                       } else {
-                           print $OUT "Subroutine $subname not found.\n";
-                       }
+                       $cond = length $2 ? $2 : '1';
+                       cmd_b_sub($subname, $cond);
                        next CMD; };
                    $cmd =~ /^b\b\s*(\d*)\s*(.*)/ && do {
-                       $i = ($1?$1:$line);
-                       $cond = $2 || '1';
-                       if ($dbline[$i] == 0) {
-                           print $OUT "Line $i not breakable.\n";
-                       } else {
-                           $had_breakpoints{$filename} = 1;
-                           $dbline{$i} =~ s/^[^\0]*/$cond/;
-                       }
+                       $i = $1 || $line;
+                       $cond = length $2 ? $2 : '1';
+                       cmd_b_line($i, $cond);
                        next CMD; };
-                   $cmd =~ /^d\b\s*(\d+)?/ && do {
-                       $i = ($1?$1:$line);
-                       $dbline{$i} =~ s/^[^\0]*//;
-                       delete $dbline{$i} if $dbline{$i} eq '';
+                   $cmd =~ /^d\b\s*(\d*)/ && do {
+                       cmd_d($1 || $line);
                        next CMD; };
                    $cmd =~ /^A$/ && do {
+                     print $OUT "Deleting all actions...\n";
                      my $file;
                      for $file (keys %had_breakpoints) {
                        local *dbline = $main::{'_<' . $file};
@@ -815,6 +1038,10 @@ EOP
                                delete $dbline{$i} if $dbline{$i} eq '';
                            }
                        }
+                       
+                       unless ($had_breakpoints{$file} &= ~2) {
+                           delete $had_breakpoints{$file};
+                       }
                      }
                      next CMD; };
                    $cmd =~ /^O\s*$/ && do {
@@ -832,27 +1059,90 @@ EOP
                        push @$post, action($1);
                        next CMD; };
                    $cmd =~ /^<\s*(.*)/ && do {
-                       $pre = [], next CMD unless $1;
+                       unless ($1) {
+                           print $OUT "All < actions cleared.\n";
+                           $pre = [];
+                           next CMD;
+                       } 
+                       if ($1 eq '?') {
+                           unless (@$pre) {
+                               print $OUT "No pre-prompt Perl actions.\n";
+                               next CMD;
+                           } 
+                           print $OUT "Perl commands run before each prompt:\n";
+                           for my $action ( @$pre ) {
+                               print $OUT "\t< -- $action\n";
+                           } 
+                           next CMD;
+                       } 
                        $pre = [action($1)];
                        next CMD; };
                    $cmd =~ /^>\s*(.*)/ && do {
-                       $post = [], next CMD unless $1;
+                       unless ($1) {
+                           print $OUT "All > actions cleared.\n";
+                           $post = [];
+                           next CMD;
+                       }
+                       if ($1 eq '?') {
+                           unless (@$post) {
+                               print $OUT "No post-prompt Perl actions.\n";
+                               next CMD;
+                           } 
+                           print $OUT "Perl commands run after each prompt:\n";
+                           for my $action ( @$post ) {
+                               print $OUT "\t> -- $action\n";
+                           } 
+                           next CMD;
+                       } 
                        $post = [action($1)];
                        next CMD; };
                    $cmd =~ /^\{\{\s*(.*)/ && do {
+                       if ($cmd =~ /^\{.*\}$/ && unbalanced(substr($cmd,2))) { 
+                           print $OUT "{{ is now a debugger command\n",
+                               "use `;{{' if you mean Perl code\n";
+                           $cmd = "h {{";
+                           redo CMD;
+                       } 
                        push @$pretype, $1;
                        next CMD; };
                    $cmd =~ /^\{\s*(.*)/ && do {
-                       $pretype = [], next CMD unless $1;
+                       unless ($1) {
+                           print $OUT "All { actions cleared.\n";
+                           $pretype = [];
+                           next CMD;
+                       }
+                       if ($1 eq '?') {
+                           unless (@$pretype) {
+                               print $OUT "No pre-prompt debugger actions.\n";
+                               next CMD;
+                           } 
+                           print $OUT "Debugger commands run before each prompt:\n";
+                           for my $action ( @$pretype ) {
+                               print $OUT "\t{ -- $action\n";
+                           } 
+                           next CMD;
+                       } 
+                       if ($cmd =~ /^\{.*\}$/ && unbalanced(substr($cmd,1))) { 
+                           print $OUT "{ is now a debugger command\n",
+                               "use `;{' if you mean Perl code\n";
+                           $cmd = "h {";
+                           redo CMD;
+                       } 
                        $pretype = [$1];
                        next CMD; };
-                   $cmd =~ /^a\b\s*(\d+)(\s+(.*))?/ && do {
-                       $i = $1; $j = $3;
-                       if ($dbline[$i] == 0) {
-                           print $OUT "Line $i may not have an action.\n";
+                   $cmd =~ /^a\b\s*(\d*)\s*(.*)/ && do {
+                       $i = $1 || $line; $j = $2;
+                       if (length $j) {
+                           if ($dbline[$i] == 0) {
+                               print $OUT "Line $i may not have an action.\n";
+                           } else {
+                               $had_breakpoints{$filename} |= 2;
+                               $dbline{$i} =~ s/\0[^\0]*//;
+                               $dbline{$i} .= "\0" . action($j);
+                           }
                        } else {
                            $dbline{$i} =~ s/\0[^\0]*//;
-                           $dbline{$i} .= "\0" . action($j);
+                           delete $dbline{$i} if $dbline{$i} eq '';
                        }
                        next CMD; };
                    $cmd =~ /^n$/ && do {
@@ -868,6 +1158,10 @@ EOP
                    $cmd =~ /^c\b\s*([\w:]*)\s*$/ && do {
                        end_report(), next CMD if $finished and $level <= 1;
                        $subname = $i = $1;
+                       #  Probably not needed, since we finish an interactive
+                       #  sub-session anyway...
+                       # local $filename = $filename;
+                       # local *dbline = *dbline;      # XXX Would this work?!
                        if ($i =~ /\D/) { # subroutine name
                            $subname = $package."::".$subname 
                                unless $subname =~ /::/;
@@ -876,7 +1170,7 @@ EOP
                            if ($i) {
                                $filename = $file;
                                *dbline = $main::{'_<' . $filename};
-                               $had_breakpoints{$filename}++;
+                               $had_breakpoints{$filename} |= 1;
                                $max = $#dbline;
                                ++$i while $dbline[$i] == 0 && $i < $max;
                            } else {
@@ -913,7 +1207,7 @@ EOP
                        set_list("PERLDB_INC", @ini_INC);
                        if ($0 eq '-e') {
                          for (1..$#{'::_<-e'}) { # The first line is PERL5DB
-                           chomp ($cl =  $ {'::_<-e'}[$_]);
+                               chomp ($cl =  ${'::_<-e'}[$_]);
                            push @script, '-e', $cl;
                          }
                        } else {
@@ -932,7 +1226,7 @@ EOP
                          *dbline = $main::{'_<' . $file};
                          next unless %dbline or $postponed_file{$file};
                          (push @hard, $file), next 
-                           if $file =~ /^\(eval \d+\)$/;
+                           if $file =~ /^\(\w*eval/;
                          my @add;
                          @add = %{$postponed_file{$file}}
                            if $postponed_file{$file};
@@ -977,8 +1271,10 @@ EOP
                        set_list("PERLDB_POST", @$post);
                        set_list("PERLDB_TYPEAHEAD", @typeahead);
                        $ENV{PERLDB_RESTART} = 1;
-                       #print "$^X, '-d', @flags, @script, ($emacs ? '-emacs' : ()), @ARGS";
-                       exec $^X, '-d', @flags, @script, ($emacs ? '-emacs' : ()), @ARGS;
+                       delete $ENV{PERLDB_PIDS}; # Restore ini state
+                       $ENV{PERLDB_PIDS} = $ini_pids if defined $ini_pids;
+                       #print "$^X, '-d', @flags, @script, ($slave_editor ? '-emacs' : ()), @ARGS";
+                       exec($^X, '-d', @flags, @script, ($slave_editor ? '-emacs' : ()), @ARGS) ||
                        print $OUT "exec failed: $!\n";
                        last CMD; };
                    $cmd =~ /^T$/ && do {
@@ -1000,6 +1296,9 @@ EOP
                        $inpat = $1;
                        $inpat =~ s:([^\\])/$:$1:;
                        if ($inpat ne "") {
+                           # squelch the sigmangler
+                           local $SIG{__DIE__};
+                           local $SIG{__WARN__};
                            eval '$inpat =~ m'."\a$inpat\a";    
                            if ($@ ne "") {
                                print $OUT "$@";
@@ -1015,7 +1314,7 @@ EOP
                                $start = 1 if ($start > $max);
                                last if ($start == $end);
                                if ($dbline[$start] =~ m' . "\a$pat\a" . 'i) {
-                                   if ($emacs) {
+                                   if ($slave_editor) {
                                        print $OUT "\032\032$filename:$start:0\n";
                                    } else {
                                        print $OUT "$start:\t", $dbline[$start], "\n";
@@ -1029,9 +1328,12 @@ EOP
                        $inpat = $1;
                        $inpat =~ s:([^\\])\?$:$1:;
                        if ($inpat ne "") {
+                           # squelch the sigmangler
+                           local $SIG{__DIE__};
+                           local $SIG{__WARN__};
                            eval '$inpat =~ m'."\a$inpat\a";    
                            if ($@ ne "") {
-                               print $OUT "$@";
+                               print $OUT $@;
                                next CMD;
                            }
                            $pat = $inpat;
@@ -1044,7 +1346,7 @@ EOP
                                $start = $max if ($start <= 0);
                                last if ($start == $end);
                                if ($dbline[$start] =~ m' . "\a$pat\a" . 'i) {
-                                   if ($emacs) {
+                                   if ($slave_editor) {
                                        print $OUT "\032\032$filename:$start:0\n";
                                    } else {
                                        print $OUT "$start:\t", $dbline[$start], "\n";
@@ -1056,7 +1358,7 @@ EOP
                        next CMD; };
                    $cmd =~ /^$rc+\s*(-)?(\d+)?$/ && do {
                        pop(@hist) if length($cmd) > 1;
-                       $i = $1 ? ($#hist-($2?$2:1)) : ($2?$2:$#hist);
+                       $i = $1 ? ($#hist-($2||1)) : ($2||$#hist);
                        $cmd = $hist[$i];
                        print $OUT $cmd, "\n";
                        redo CMD; };
@@ -1080,32 +1382,65 @@ EOP
                        &system($ENV{SHELL}||"/bin/sh");
                        next CMD; };
                    $cmd =~ /^$sh\s*([\x00-\xff]*)/ && do {
+                       # XXX: using csh or tcsh destroys sigint retvals!
+                       #&system($1);  # use this instead
                        &system($ENV{SHELL}||"/bin/sh","-c",$1);
                        next CMD; };
                    $cmd =~ /^H\b\s*(-(\d+))?/ && do {
-                       $end = $2?($#hist-$2):0;
+                       $end = $2 ? ($#hist-$2) : 0;
                        $hist = 0 if $hist < 0;
                        for ($i=$#hist; $i>$end; $i--) {
                            print $OUT "$i: ",$hist[$i],"\n"
                              unless $hist[$i] =~ /^.?$/;
                        };
                        next CMD; };
+                   $cmd =~ /^(?:man|(?:perl)?doc)\b(?:\s+([^(]*))?$/ && do {
+                       runman($1);
+                       next CMD; };
                    $cmd =~ s/^p$/print {\$DB::OUT} \$_/;
                    $cmd =~ s/^p\b/print {\$DB::OUT} /;
-                   $cmd =~ /^=/ && do {
-                       if (local($k,$v) = ($cmd =~ /^=\s*(\S+)\s+(.*)/)) {
-                           $alias{$k}="s~$k~$v~";
-                           print $OUT "$k = $v\n";
-                       } elsif ($cmd =~ /^=\s*$/) {
-                           foreach $k (sort keys(%alias)) {
-                               if (($v = $alias{$k}) =~ s~^s\~$k\~(.*)\~$~$1~) {
-                                   print $OUT "$k = $v\n";
-                               } else {
+                   $cmd =~ s/^=\s*// && do {
+                       my @keys;
+                       if (length $cmd == 0) {
+                           @keys = sort keys %alias;
+                       } 
+                        elsif (my($k,$v) = ($cmd =~ /^(\S+)\s+(\S.*)/)) {
+                           # can't use $_ or kill //g state
+                           for my $x ($k, $v) { $x =~ s/\a/\\a/g }
+                           $alias{$k} = "s\a$k\a$v\a";
+                           # squelch the sigmangler
+                           local $SIG{__DIE__};
+                           local $SIG{__WARN__};
+                           unless (eval "sub { s\a$k\a$v\a }; 1") {
+                               print $OUT "Can't alias $k to $v: $@\n"; 
+                               delete $alias{$k};
+                               next CMD;
+                           } 
+                           @keys = ($k);
+                       } 
+                       else {
+                           @keys = ($cmd);
+                       } 
+                       for my $k (@keys) {
+                           if ((my $v = $alias{$k}) =~ s\as\a$k\a(.*)\a$\a1\a) {
+                               print $OUT "$k\t= $1\n";
+                           } 
+                           elsif (defined $alias{$k}) {
                                    print $OUT "$k\t$alias{$k}\n";
-                               };
-                           };
-                       };
+                           } 
+                           else {
+                               print "No alias for $k\n";
+                           } 
+                       }
                        next CMD; };
+                   $cmd =~ /^\@\s*(.*\S)/ && do {
+                     if (open my $fh, $1) {
+                       push @cmdfhs, $fh;
+                     }
+                     else {
+                       &warn("Can't execute `$1': $!\n");
+                     }
+                     next CMD; };
                    $cmd =~ /^\|\|?\s*[^|]/ && do {
                        if ($pager =~ /^\|/) {
                            open(SAVEOUT,">&STDOUT") || &warn("Can't save STDOUT");
@@ -1113,25 +1448,29 @@ EOP
                        } else {
                            open(SAVEOUT,">&OUT") || &warn("Can't save DB::OUT");
                        }
+                       fix_less();
                        unless ($piped=open(OUT,$pager)) {
                            &warn("Can't pipe output to `$pager'");
                            if ($pager =~ /^\|/) {
-                               open(OUT,">&STDOUT") || &warn("Can't restore DB::OUT");
+                               open(OUT,">&STDOUT") # XXX: lost message
+                                   || &warn("Can't restore DB::OUT");
                                open(STDOUT,">&SAVEOUT")
                                  || &warn("Can't restore STDOUT");
                                close(SAVEOUT);
                            } else {
-                               open(OUT,">&STDOUT") || &warn("Can't restore DB::OUT");
+                               open(OUT,">&STDOUT") # XXX: lost message
+                                   || &warn("Can't restore DB::OUT");
                            }
                            next CMD;
                        }
                        $SIG{PIPE}= \&DB::catch if $pager =~ /^\|/
-                         && "" eq $SIG{PIPE}  ||  "DEFAULT" eq $SIG{PIPE};
+                           && ("" eq $SIG{PIPE}  ||  "DEFAULT" eq $SIG{PIPE});
                        $selected= select(OUT);
                        $|= 1;
                        select( $selected ), $selected= "" unless $cmd =~ /^\|\|/;
                        $cmd =~ s/^\|+\s*//;
-                       redo PIPE; };
+                       redo PIPE; 
+                   };
                    # XXX Local variants do not work!
                    $cmd =~ s/^t\s/\$DB::trace |= 1;\n/;
                    $cmd =~ s/^s\s/\$DB::single = 1;\n/ && do {$laststep = 's'};
@@ -1146,14 +1485,27 @@ EOP
        } continue {            # CMD:
            if ($piped) {
                if ($pager =~ /^\|/) {
-                   $?= 0;  close(OUT) || &warn("Can't close DB::OUT");
-                   &warn( "Pager `$pager' failed: ",
-                         ($?>>8) > 128 ? ($?>>8)-256 : ($?>>8),
-                         ( $? & 128 ) ? " (core dumped)" : "",
-                         ( $? & 127 ) ? " (SIG ".($?&127).")" : "", "\n" ) if $?;
+                   $? = 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
+                   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";
+                       } 
+                   } 
+
                    open(OUT,">&STDOUT") || &warn("Can't restore DB::OUT");
                    open(STDOUT,">&SAVEOUT") || &warn("Can't restore STDOUT");
-                   $SIG{PIPE}= "DEFAULT" if $SIG{PIPE} eq \&DB::catch;
+                   $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.
                } else {
@@ -1164,7 +1516,7 @@ EOP
                $piped= "";
            }
        }                       # CMD:
-       $exiting = 1 unless defined $cmd;
+       $fall_off_end = 1 unless defined $cmd; # Emulate `q' on EOF
        foreach $evalarg (@$post) {
          &eval;
        }
@@ -1187,17 +1539,17 @@ sub sub {
     $single &= 1;
     $single |= 4 if $stack_depth == $deep;
     ($frame & 4 
-     ? ( (print $LINEINFO ' ' x ($stack_depth - 1), "in  "), 
+     ? ( print_lineinfo(' ' x ($stack_depth - 1), "in  "),
         # Why -1? But it works! :-(
         print_trace($LINEINFO, -1, 1, 1, "$sub$al") )
-     : print $LINEINFO ' ' x ($stack_depth - 1), "entering $sub$al\n") if $frame;
+     : print_lineinfo(' ' x ($stack_depth - 1), "entering $sub$al\n")) if $frame;
     if (wantarray) {
        @ret = &$sub;
        $single |= $stack[$stack_depth--];
        ($frame & 4 
-        ? ( (print $LINEINFO ' ' x $stack_depth, "out "), 
+        ? ( print_lineinfo(' ' x $stack_depth, "out "), 
             print_trace($LINEINFO, -1, 1, 1, "$sub$al") )
-        : print $LINEINFO ' ' x $stack_depth, "exited $sub$al\n") if $frame & 2;
+        : print_lineinfo(' ' x $stack_depth, "exited $sub$al\n")) if $frame & 2;
        if ($doret eq $stack_depth or $frame & 16) {
             my $fh = ($doret eq $stack_depth ? $OUT : $LINEINFO);
            print $fh ' ' x $stack_depth if $frame & 16;
@@ -1214,9 +1566,9 @@ sub sub {
         };
        $single |= $stack[$stack_depth--];
        ($frame & 4 
-        ? ( (print $LINEINFO ' ' x $stack_depth, "out "), 
+        ? (  print_lineinfo(' ' x $stack_depth, "out "),
              print_trace($LINEINFO, -1, 1, 1, "$sub$al") )
-        : print $LINEINFO ' ' x $stack_depth, "exited $sub$al\n") if $frame & 2;
+        : print_lineinfo(' ' x $stack_depth, "exited $sub$al\n")) if $frame & 2;
        if ($doret eq $stack_depth or $frame & 16 and defined wantarray) {
             my $fh = ($doret eq $stack_depth ? $OUT : $LINEINFO);
            print $fh (' ' x $stack_depth) if $frame & 16;
@@ -1230,19 +1582,159 @@ sub sub {
     }
 }
 
+### The API section
+
+### Functions with multiple modes of failure die on error, the rest
+### returns FALSE on error.
+### User-interface functions cmd_* output error message.
+
+sub break_on_load {
+  my $file = shift;
+  $break_on_load{$file} = 1;
+  $had_breakpoints{$file} |= 1;
+}
+
+sub report_break_on_load {
+  sort keys %break_on_load;
+}
+
+sub cmd_b_load {
+  my $file = shift;
+  my @files;
+  {
+    push @files, $file;
+    push @files, $::INC{$file} if $::INC{$file};
+    $file .= '.pm', redo unless $file =~ /\./;
+  }
+  break_on_load($_) for @files;
+  @files = report_break_on_load;
+  print $OUT "Will stop on load of `@files'.\n";
+}
+
+$filename_error = '';
+
+sub breakable_line {
+  my ($from, $to) = @_;
+  my $i = $from;
+  if (@_ >= 2) {
+    my $delta = $from < $to ? +1 : -1;
+    my $limit = $delta > 0 ? $#dbline : 1;
+    $limit = $to if ($limit - $to) * $delta > 0;
+    $i += $delta while $dbline[$i] == 0 and ($limit - $i) * $delta > 0;
+  }
+  return $i unless $dbline[$i] == 0;
+  my ($pl, $upto) = ('', '');
+  ($pl, $upto) = ('s', "..$to") if @_ >=2 and $from != $to;
+  die "Line$pl $from$upto$filename_error not breakable\n";
+}
+
+sub breakable_line_in_filename {
+  my ($f) = shift;
+  local *dbline = $main::{'_<' . $f};
+  local $filename_error = " of `$f'";
+  breakable_line(@_);
+}
+
+sub break_on_line {
+  my ($i, $cond) = @_;
+  $cond = 1 unless @_ >= 2;
+  my $inii = $i;
+  my $after = '';
+  my $pl = '';
+  die "Line $i$filename_error not breakable.\n" if $dbline[$i] == 0;
+  $had_breakpoints{$filename} |= 1;
+  if ($dbline{$i}) { $dbline{$i} =~ s/^[^\0]*/$cond/; }
+  else { $dbline{$i} = $cond; }
+}
+
+sub cmd_b_line {
+  eval { break_on_line(@_); 1 } or print $OUT $@ and return;
+}
+
+sub break_on_filename_line {
+  my ($f, $i, $cond) = @_;
+  $cond = 1 unless @_ >= 3;
+  local *dbline = $main::{'_<' . $f};
+  local $filename_error = " of `$f'";
+  local $filename = $f;
+  break_on_line($i, $cond);
+}
+
+sub break_on_filename_line_range {
+  my ($f, $from, $to, $cond) = @_;
+  my $i = breakable_line_in_filename($f, $from, $to);
+  $cond = 1 unless @_ >= 3;
+  break_on_filename_line($f,$i,$cond);
+}
+
+sub subroutine_filename_lines {
+  my ($subname,$cond) = @_;
+  # Filename below can contain ':'
+  find_sub($subname) =~ /^(.*):(\d+)-(\d+)$/;
+}
+
+sub break_subroutine {
+  my $subname = shift;
+  my ($file,$s,$e) = subroutine_filename_lines($subname) or
+    die "Subroutine $subname not found.\n";
+  $cond = 1 unless @_ >= 2;
+  break_on_filename_line_range($file,$s,$e,@_);
+}
+
+sub cmd_b_sub {
+  my ($subname,$cond) = @_;
+  $cond = 1 unless @_ >= 2;
+  unless (ref $subname eq 'CODE') {
+    $subname =~ s/\'/::/g;
+    my $s = $subname;
+    $subname = "${'package'}::" . $subname
+      unless $subname =~ /::/;
+    $subname = "CORE::GLOBAL::$s"
+      if not defined &$subname and $s !~ /::/ and defined &{"CORE::GLOBAL::$s"};
+    $subname = "main".$subname if substr($subname,0,2) eq "::";
+  }
+  eval { break_subroutine($subname,$cond); 1 } or print $OUT $@ and return;
+}
+
+sub cmd_stop {                 # As on ^C, but not signal-safy.
+  $signal = 1;
+}
+
+sub delete_breakpoint {
+  my $i = shift;
+  die "Line $i not breakable.\n" if $dbline[$i] == 0;
+  $dbline{$i} =~ s/^[^\0]*//;
+  delete $dbline{$i} if $dbline{$i} eq '';
+}
+
+sub cmd_d {
+  my $i = shift;
+  eval { delete_breakpoint $i; 1 } or print $OUT $@ and return;
+}
+
+### END of the API section
+
 sub save {
     @saved = ($@, $!, $^E, $,, $/, $\, $^W);
     $, = ""; $/ = "\n"; $\ = ""; $^W = 0;
 }
 
+sub print_lineinfo {
+  resetterm(1) if $LINEINFO eq $OUT and $term_pid != $$;
+  print $LINEINFO @_;
+}
+
 # The following takes its argument via $evalarg to preserve current @_
 
 sub eval {
-    my @res;
+    # 'my' would make it visible from user code
+    #    but so does local! --tchrist  [... into @DB::res, not @res. IZ]
+    local @res;
     {
-       my $otrace = $trace;
-       my $osingle = $single;
-       my $od = $^D;
+       local $otrace = $trace;
+       local $osingle = $single;
+       local $od = $^D;
+       { ($evalarg) = $evalarg =~ /(.*)/s; }
        @res = eval "$usercontext $evalarg;\n"; # '\n' for nice recursive debug
        $trace = $otrace;
        $single = $osingle;
@@ -1253,10 +1745,9 @@ sub eval {
     eval { &DB::save };
     if ($at) {
        print $OUT $at;
-    } elsif ($onetimeDump eq 'dump') {
-       dumpit($OUT, \@res);
-    } elsif ($onetimeDump eq 'methods') {
-       methods($res[0]);
+    } elsif ($onetimeDump) {
+       dumpit($OUT, \@res) if $onetimeDump eq 'dump';
+       methods($res[0])    if $onetimeDump eq 'methods';
     }
     @res;
 }
@@ -1271,7 +1762,7 @@ sub postponed_sub {
       $i += $offset;
       local *dbline = $main::{'_<' . $file};
       local $^W = 0;           # != 0 is magical below
-      $had_breakpoints{$file}++;
+      $had_breakpoints{$file} |= 1;
       my $max = $#dbline;
       ++$i until $dbline[$i] != 0 or $i >= $max;
       $dbline{$i} = delete $postponed{$subname};
@@ -1297,13 +1788,13 @@ sub postponed {
   $filename =~ s/^_<//;
   $signal = 1, print $OUT "'$filename' loaded...\n"
     if $break_on_load{$filename};
-  print $LINEINFO ' ' x $stack_depth, "Package $filename.\n" if $frame;
+  print_lineinfo(' ' x $stack_depth, "Package $filename.\n") if $frame;
   return unless $postponed_file{$filename};
-  $had_breakpoints{$filename}++;
+  $had_breakpoints{$filename} |= 1;
   #%dbline = %{$postponed_file{$filename}}; # Cannot be done: unsufficient magic
   my $key;
   for $key (keys %{$postponed_file{$filename}}) {
-    $dbline{$key} = $ {$postponed_file{$filename}}{$key};
+    $dbline{$key} = ${$postponed_file{$filename}}{$key};
   }
   delete $postponed_file{$filename};
 }
@@ -1332,6 +1823,7 @@ sub dumpit {
 
 sub print_trace {
   my $fh = shift;
+  resetterm(1) if $fh eq $LINEINFO and $LINEINFO eq $OUT and $term_pid != $$;
   my @sub = dump_trace($_[0] + 1, $_[1]);
   my $short = $_[2];           # Print short report, next one for sub name
   my $s;
@@ -1419,27 +1911,50 @@ sub action {
     $action;
 }
 
+sub unbalanced { 
+    # i hate using globals!
+    $balanced_brace_re ||= qr{ 
+       ^ \{
+             (?:
+                (?> [^{}] + )              # Non-parens without backtracking
+              |
+                (??{ $balanced_brace_re }) # Group with matching parens
+             ) *
+         \} $
+   }x;
+   return $_[0] !~ m/$balanced_brace_re/;
+}
+
 sub gets {
-    local($.);
-    #<IN>;
     &readline("cont: ");
 }
 
 sub system {
     # We save, change, then restore STDIN and STDOUT to avoid fork() since
-    # many non-Unix systems can do system() but have problems with fork().
+    # 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");
+
+    # 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");
-    close(SAVEIN); close(SAVEOUT);
-    &warn( "(Command returned ", ($?>>8) > 128 ? ($?>>8)-256 : ($?>>8), ")",
-         ( $? & 128 ) ? " (core dumped)" : "",
-         ( $? & 127 ) ? " (SIG ".($?&127).")" : "", "\n" ) if $?;
-    $?;
+    close(SAVEIN); 
+    close(SAVEOUT);
+
+
+    # most of the $? crud was coping with broken cshisms
+    if ($? >> 8) {
+       &warn("(Command exited ", ($? >> 8), ")\n");
+    } elsif ($?) { 
+       &warn( "(Command died of SIG#",  ($? & 127),
+           (($? & 128) ? " -- core dumped" : "") , ")", "\n");
+    } 
+
+    return $?;
+
 }
 
 sub setterm {
@@ -1448,21 +1963,26 @@ sub setterm {
     eval { require Term::ReadLine } or die $@;
     if ($notty) {
        if ($tty) {
-           open(IN,"<$tty") or die "Cannot open TTY `$TTY' for read: $!";
-           open(OUT,">$tty") or die "Cannot open TTY `$TTY' for write: $!";
+           my ($i, $o) = split $tty, /,/;
+           $o = $i unless defined $o;
+           open(IN,"<$i") or die "Cannot open TTY `$i' for read: $!";
+           open(OUT,">$o") or die "Cannot open TTY `$o' for write: $!";
            $IN = \*IN;
            $OUT = \*OUT;
            my $sel = select($OUT);
            $| = 1;
            select($sel);
        } else {
-           eval "require Term::Rendezvous;" or die $@;
+           eval "require Term::Rendezvous;" or die;
            my $rv = $ENV{PERLDB_NOTTY} || "/tmp/perldbtty$$";
            my $term_rv = new Term::Rendezvous $rv;
            $IN = $term_rv->IN;
            $OUT = $term_rv->OUT;
        }
     }
+    if ($term_pid eq '-1') {           # In a TTY with another debugger
+       resetterm(2);
+    }
     if (!$rl) {
        $term = new Term::ReadLine::Stub 'perldb', $IN, $OUT;
     } else {
@@ -1486,35 +2006,103 @@ sub setterm {
     $term_pid = $$;
 }
 
-sub resetterm {                        # We forked, so we need a different TTY
-    $term_pid = $$;
-    if (defined &get_fork_TTY) {
-      &get_fork_TTY;
-    } elsif (not defined $fork_TTY 
-            and defined $ENV{TERM} and $ENV{TERM} eq 'xterm' 
-            and defined $ENV{WINDOWID} and defined $ENV{DISPLAY}) { 
-        # Possibly _inside_ XTERM
-        open XT, q[3>&1 xterm -title 'Forked Perl debugger' -e sh -c 'tty 1>&3;\
+# Example get_fork_TTY functions
+sub xterm_get_fork_TTY {
+  (my $name = $0) =~ s,^.*[/\\],,s;
+  open XT, qq[3>&1 xterm -title "Daughter Perl debugger $pids $name" -e sh -c 'tty 1>&3;\
  sleep 10000000' |];
-        $fork_TTY = <XT>;
-        chomp $fork_TTY;
-    }
-    if (defined $fork_TTY) {
-      TTY($fork_TTY);
-      undef $fork_TTY;
-    } else {
+  my $tty = <XT>;
+  chomp $tty;
+  $pidprompt = '';             # Shown anyway in titlebar
+  return $tty;
+}
+
+# This one resets $IN, $OUT itself
+sub os2_get_fork_TTY {
+  $^F = 40;            # XXXX Fixme!
+  my ($in1, $out1, $in2, $out2);
+  # Having -d in PERL5OPT would lead to a disaster...
+  local $ENV{PERL5OPT} = $ENV{PERL5OPT}    if $ENV{PERL5OPT};
+  $ENV{PERL5OPT} =~ s/(?:^|(?<=\s))-d\b//  if $ENV{PERL5OPT};
+  $ENV{PERL5OPT} =~ s/(?:^|(?<=\s))-d\B/-/ if $ENV{PERL5OPT};
+  print $OUT "Making PERL5OPT->`$ENV{PERL5OPT}'.\n" if $ENV{PERL5OPT};
+  (my $name = $0) =~ s,^.*[/\\],,s;
+  if ( pipe $in1, $out1 and pipe $in2, $out2 and
+       # system P_SESSION will fail if there is another process
+       # in the same session with a "dependent" asynchronous child session.
+       (($kpid = CORE::system 4, $^X, '-we', <<'ES', fileno $in1, fileno $out2, "Daughter Perl debugger $pids $name") >= 0 or warn "system P_SESSION: $!, $^E" and 0) # P_SESSION
+use Term::ReadKey;
+use OS2::Process;
+
+my $in = shift;                # Read from here and pass through
+set_title pop;
+system P_NOWAIT, $^X, '-we', <<EOS or die "Cannot start a grandkid";
+  open IN, '<&=$in' or die "open <&=$in: \$!";
+  \$| = 1; print while sysread IN, \$_, 1<<16;
+EOS
+
+my $out = shift;
+open OUT, ">&=$out" or die "Cannot open &=$out for writing: $!";
+select OUT;    $| = 1;
+ReadMode 4;            # Nodelay on kbd.  Pipe is automatically nodelay...
+print while sysread STDIN, $_, 1<<16;
+ES
+       and close $in1 and close $out2 ) {
+      $pidprompt = '';         # Shown anyway in titlebar
+      reset_IN_OUT($in2, $out1);
+      $tty = '*reset*';
+      return '';                       # Indicate that reset_IN_OUT is called
+   }
+   return;
+}
+
+sub create_IN_OUT {    # Create a window with IN/OUT handles redirected there
+    my $in = &get_fork_TTY if defined &get_fork_TTY;
+    $in = $fork_TTY if defined $fork_TTY; # Backward compatibility
+    if (not defined $in) {
+      my $why = shift;
+      print_help(<<EOP) if $why == 1;
+I<#########> Forked, but do not know how to create a new B<TTY>. I<#########>
+EOP
+      print_help(<<EOP) if $why == 2;
+I<#########> Daughter session, do not know how to change a B<TTY>. I<#########>
+  This may be an asynchronous session, so the parent debugger may be active.
+EOP
+      print_help(<<EOP) if $why != 4;
+  Since two debuggers fight for the same TTY, input is severely entangled.
+
+EOP
       print_help(<<EOP);
-I<#########> Forked, but do not know how to change a B<TTY>. I<#########>
-  Define B<\$DB::fork_TTY> 
-       - or a function B<DB::get_fork_TTY()> which will set B<\$DB::fork_TTY>.
-  The value of B<\$DB::fork_TTY> should be the name of I<TTY> to use.
+  I know how to switch the output to a different window in xterms
+  and OS/2 consoles only.  For a manual switch, put the name of the created I<TTY>
+  in B<\$DB::fork_TTY>, or define a function B<DB::get_fork_TTY()> returning this.
+
   On I<UNIX>-like systems one can get the name of a I<TTY> for the given window
   by typing B<tty>, and disconnect the I<shell> from I<TTY> by B<sleep 1000000>.
+
 EOP
+    } elsif ($in ne '') {
+      TTY($in);
     }
+    undef $fork_TTY;
+}
+
+sub resetterm {                        # We forked, so we need a different TTY
+    my $in = shift;
+    my $systemed = $in > 1 ? '-' : '';
+    if ($pids) {
+      $pids =~ s/\]/$systemed->$$]/;
+    } else {
+      $pids = "[$term_pid->$$]";
+    }
+    $pidprompt = $pids;
+    $term_pid = $$;
+    return unless $CreateTTY & $in;
+    create_IN_OUT($in);
 }
 
 sub readline {
+  local $.;
   if (@typeahead) {
     my $left = @typeahead;
     my $got = shift @typeahead;
@@ -1525,7 +2113,20 @@ sub readline {
   }
   local $frame = 0;
   local $doret = -2;
-  $term->readline(@_);
+  while (@cmdfhs) {
+    my $line = CORE::readline($cmdfhs[-1]);
+    defined $line ? (print $OUT ">> $line" and return $line)
+                  : close pop @cmdfhs;
+  }
+  if (ref $OUT and UNIVERSAL::isa($OUT, 'IO::Socket::INET')) {
+    $OUT->write(join('', @_));
+    my $stuff;
+    $IN->recv( $stuff, 2048 );  # XXX: what's wrong with sysread?
+    $stuff;
+  }
+  else {
+    $term->readline(@_);
+  }
 }
 
 sub dump_option {
@@ -1539,26 +2140,35 @@ sub option_val {
     my ($opt, $default)= @_;
     my $val;
     if (defined $optionVars{$opt}
-       and defined $ {$optionVars{$opt}}) {
-       $val = $ {$optionVars{$opt}};
+       and defined ${$optionVars{$opt}}) {
+       $val = ${$optionVars{$opt}};
     } elsif (defined $optionAction{$opt}
        and defined &{$optionAction{$opt}}) {
        $val = &{$optionAction{$opt}}();
     } elsif (defined $optionAction{$opt}
             and not defined $option{$opt}
             or defined $optionVars{$opt}
-            and not defined $ {$optionVars{$opt}}) {
+            and not defined ${$optionVars{$opt}}) {
        $val = $default;
     } else {
        $val = $option{$opt};
     }
+    $val = $default unless defined $val;
     $val
 }
 
 sub parse_options {
     local($_)= @_;
-    while ($_ ne "") {
-       s/^(\w+)(\s*$|\W)// or print($OUT "Invalid option `$_'\n"), last;
+    # too dangerous to let intuitive usage overwrite important things
+    # defaultion should never be the default
+    my %opt_needs_val = map { ( $_ => 1 ) } qw{
+        arrayDepth hashDepth LineInfo maxTraceLen ornaments windowSize
+        pager quote ReadLine recallCommand RemotePort ShellBang TTY
+    };
+    while (length) {
+       my $val_defaulted;
+       s/^\s+// && next;
+       s/^(\w+)(\W?)// or print($OUT "Invalid option `$_'\n"), last;
        my ($opt,$sep) = ($1,$2);
        my $val;
        if ("?" eq $sep) {
@@ -1566,59 +2176,83 @@ sub parse_options {
              if /^\S/;
            #&dump_option($opt);
        } elsif ($sep !~ /\S/) {
-           $val = "1";
+           $val_defaulted = 1;
+           $val = "1";  #  this is an evil default; make 'em set it!
        } elsif ($sep eq "=") {
-           s/^(\S*)($|\s+)//;
+
+            if (s/ (["']) ( (?: \\. | (?! \1 ) [^\\] )* ) \1 //x) { 
+                my $quote = $1;
+                ($val = $2) =~ s/\\([$quote\\])/$1/g;
+           } else { 
+               s/^(\S*)//;
            $val = $1;
+               print OUT qq(Option better cleared using $opt=""\n)
+                   unless length $val;
+           }
+
        } else { #{ to "let some poor schmuck bounce on the % key in B<vi>."
            my ($end) = "\\" . substr( ")]>}$sep", index("([<{",$sep), 1 ); #}
            s/^(([^\\$end]|\\[\\$end])*)$end($|\s+)// or
              print($OUT "Unclosed option value `$opt$sep$_'\n"), last;
-           $val = $1;
-           $val =~ s/\\([\\$end])/$1/g;
+           ($val = $1) =~ s/\\([\\$end])/$1/g;
        }
-       my ($option);
-       my $matches =
-         grep(  /^\Q$opt/ && ($option = $_),  @options  );
-       $matches =  grep(  /^\Q$opt/i && ($option = $_),  @options  )
-         unless $matches;
-       print $OUT "Unknown option `$opt'\n" unless $matches;
-       print $OUT "Ambiguous option `$opt'\n" if $matches > 1;
-       $option{$option} = $val if $matches == 1 and defined $val;
-       eval "local \$frame = 0; local \$doret = -2; 
-             require '$optionRequire{$option}'"
-         if $matches == 1 and defined $optionRequire{$option} and defined $val;
-       $ {$optionVars{$option}} = $val 
-         if $matches == 1
-           and defined $optionVars{$option} and defined $val;
-       & {$optionAction{$option}} ($val) 
-         if $matches == 1
-           and defined $optionAction{$option}
-             and defined &{$optionAction{$option}} and defined $val;
-       &dump_option($option) if $matches == 1 && $OUT ne \*STDERR; # Not $rcfile
-        s/^\s+//;
+
+       my $option;
+       my $matches = grep( /^\Q$opt/  && ($option = $_),  @options  )
+                  || grep( /^\Q$opt/i && ($option = $_),  @options  );
+
+       print($OUT "Unknown option `$opt'\n"), next     unless $matches;
+       print($OUT "Ambiguous option `$opt'\n"), next   if $matches > 1;
+
+       if ($opt_needs_val{$option} && $val_defaulted) {
+           print $OUT "Option `$opt' is non-boolean.  Use `O $option=VAL' to set, `O $option?' to query\n";
+           next;
+       } 
+
+       $option{$option} = $val if defined $val;
+
+       eval qq{
+               local \$frame = 0; 
+               local \$doret = -2; 
+               require '$optionRequire{$option}';
+               1;
+        } || die  # XXX: shouldn't happen
+           if  defined $optionRequire{$option}     &&
+               defined $val;
+
+       ${$optionVars{$option}} = $val      
+           if  defined $optionVars{$option}        &&
+               defined $val;
+
+       &{$optionAction{$option}} ($val)    
+           if defined $optionAction{$option}       &&
+               defined &{$optionAction{$option}}    &&
+               defined $val;
+
+       # Not $rcfile
+       dump_option($option)    unless $OUT eq \*STDERR; 
     }
 }
 
 sub set_list {
   my ($stem,@list) = @_;
   my $val;
-  $ENV{"$ {stem}_n"} = @list;
+  $ENV{"${stem}_n"} = @list;
   for $i (0 .. $#list) {
     $val = $list[$i];
     $val =~ s/\\/\\\\/g;
     $val =~ s/([\0-\37\177\200-\377])/"\\0x" . unpack('H2',$1)/eg;
-    $ENV{"$ {stem}_$i"} = $val;
+    $ENV{"${stem}_$i"} = $val;
   }
 }
 
 sub get_list {
   my $stem = shift;
   my @list;
-  my $n = delete $ENV{"$ {stem}_n"};
+  my $n = delete $ENV{"${stem}_n"};
   my $val;
   for $i (0 .. $n - 1) {
-    $val = delete $ENV{"$ {stem}_$i"};
+    $val = delete $ENV{"${stem}_$i"};
     $val =~ s/\\((\\)|0x(..))/ $2 ? $2 : pack('H2', $3) /ge;
     push @list, $val;
   }
@@ -1636,6 +2270,22 @@ sub warn {
     print $OUT $msg;
 }
 
+sub reset_IN_OUT {
+    my $switch_li = $LINEINFO eq $OUT;
+    if ($term and $term->Features->{newTTY}) {
+      ($IN, $OUT) = (shift, shift);
+      $term->newTTY($IN, $OUT);
+    } elsif ($term) {
+       &warn("Too late to set IN/OUT filehandles, enabled on next `R'!\n");
+    } else {
+      ($IN, $OUT) = (shift, shift);
+    }
+    my $o = select $OUT;
+    $| = 1;
+    select $o;
+    $LINEINFO = $OUT if $switch_li;
+}
+
 sub TTY {
     if (@_ and $term and $term->Features->{newTTY}) {
       my ($in, $out) = shift;
@@ -1646,13 +2296,11 @@ sub TTY {
       }
       open IN, $in or die "cannot open `$in' for read: $!";
       open OUT, ">$out" or die "cannot open `$out' for write: $!";
-      $term->newTTY(\*IN, \*OUT);
-      $IN      = \*IN;
-      $OUT     = \*OUT;
+      reset_IN_OUT(\*IN,\*OUT);
       return $tty = $in;
-    } elsif ($term and @_) {
-       &warn("Too late to set TTY, enabled on next `R'!\n");
-    } 
+    }
+    &warn("Too late to set TTY, enabled on next `R'!\n") if $term and @_;
+    # Useful if done through PERLDB_OPTS:
     $tty = shift if @_;
     $tty or $console;
 }
@@ -1673,8 +2321,16 @@ sub ReadLine {
     $rl;
 }
 
+sub RemotePort {
+    if ($term) {
+        &warn("Too late to set RemotePort, enabled on next 'R'!\n") if @_;
+    }
+    $remoteport = shift if @_;
+    $remoteport;
+}
+
 sub tkRunning {
-    if ($ {$term->Features}{tkRunning}) {
+    if (${$term->Features}{tkRunning}) {
         return $term->tkRunning(@_);
     } else {
        print $OUT "tkRunning not supported by current ReadLine package.\n";
@@ -1706,7 +2362,6 @@ sub shellBang {
     $psh = $sh;
     $psh =~ s/\\b$//;
     $psh =~ s/\\(.)/$1/g;
-    &sethelp;
     $psh;
 }
 
@@ -1728,7 +2383,6 @@ sub recallCommand {
     $prc = $rc;
     $prc =~ s/\\b$//;
     $prc =~ s/\\(.)/$1/g;
-    &sethelp;
     $prc;
 }
 
@@ -1736,7 +2390,7 @@ sub LineInfo {
     return $lineinfo unless @_;
     $lineinfo = shift;
     my $stream = ($lineinfo =~ /^(\+?\>|\|)/) ? $lineinfo : ">$lineinfo";
-    $emacs = ($stream =~ /^\|/);
+    $slave_editor = ($stream =~ /^\|/);
     open(LINEINFO, "$stream") || &warn("Cannot open `$stream' for write");
     $LINEINFO = \*LINEINFO;
     my $save = select($LINEINFO);
@@ -1754,8 +2408,8 @@ sub list_versions {
     s,/,::,g ;
     s/^perl5db$/DB/;
     s/^Term::ReadLine::readline$/readline/;
-    if (defined $ { $_ . '::VERSION' }) {
-      $version{$file} = "$ { $_ . '::VERSION' } from ";
+    if (defined ${ $_ . '::VERSION' }) {
+      $version{$file} = "${ $_ . '::VERSION' } from ";
     } 
     $version{$file} .= $INC{$file};
   }
@@ -1763,6 +2417,10 @@ sub list_versions {
 }
 
 sub sethelp {
+    # XXX: make sure there are tabs between the command and explanation,
+    #      or print_help will screw up your formatting if you have
+    #      eeevil ornaments enabled.  This is an insane mess.
+
     $help = "
 B<T>           Stack trace.
 B<s> [I<expr>] Single step [in I<expr>].
@@ -1775,11 +2433,18 @@ B<l> I<min>B<+>I<incr>  List I<incr>+1 lines starting at I<min>.
 B<l> I<min>B<->I<max>  List lines I<min> through I<max>.
 B<l> I<line>           List single I<line>.
 B<l> I<subname>        List first window of lines from subroutine.
+B<l> I<\$var>          List first window of lines from subroutine referenced by I<\$var>.
 B<l>           List next window of lines.
 B<->           List previous window of lines.
 B<w> [I<line>] List window around I<line>.
 B<.>           Return to the executed line.
-B<f> I<filename>       Switch to viewing I<filename>. Must be loaded.
+B<f> I<filename>       Switch to viewing I<filename>. File must be already loaded.
+               I<filename> may be either the full name of the file, or a regular
+               expression matching the full file name:
+               B<f> I</home/me/foo.pl> and B<f> I<oo\\.> may access the same file.
+               Evals (with saved bodies) are considered to be filenames:
+               B<f> I<(eval 7)> and B<f> I<eval 7\\b> access the body of the 7th eval
+               (in the order of execution).
 B</>I<pattern>B</>     Search forwards for I<pattern>; final B</> is optional.
 B<?>I<pattern>B<?>     Search backwards for I<pattern>; final B<?> is optional.
 B<L>           List all breakpoints and actions.
@@ -1791,6 +2456,7 @@ B<b> [I<line>] [I<condition>]
                I<condition> breaks if it evaluates to true, defaults to '1'.
 B<b> I<subname> [I<condition>]
                Set breakpoint at first line of subroutine.
+B<b> I<\$var>          Set breakpoint at first line of subroutine referenced by I<\$var>.
 B<b> B<load> I<filename> Set breakpoint on `require'ing the given file.
 B<b> B<postpone> I<subname> [I<condition>]
                Set breakpoint at first line of subroutine after 
@@ -1800,51 +2466,31 @@ B<b> B<compile> I<subname>
 B<d> [I<line>] Delete the breakpoint for I<line>.
 B<D>           Delete all breakpoints.
 B<a> [I<line>] I<command>
-               Set an action to be done before the I<line> is executed.
+               Set an action to be done before the I<line> is executed;
+               I<line> defaults to the current execution line.
                Sequence is: check for breakpoint/watchpoint, print line
                if necessary, do action, prompt user if necessary,
-               execute expression.
+               execute line.
+B<a> [I<line>] Delete the action for I<line>.
 B<A>           Delete all actions.
 B<W> I<expr>           Add a global watch-expression.
 B<W>           Delete all watch-expressions.
 B<V> [I<pkg> [I<vars>]]        List some (default all) variables in package (default current).
                Use B<~>I<pattern> and B<!>I<pattern> for positive and negative regexps.
 B<X> [I<vars>] Same as \"B<V> I<currentpackage> [I<vars>]\".
-B<x> I<expr>           Evals expression in array context, dumps the result.
-B<m> I<expr>           Evals expression in array context, prints methods callable
+B<x> I<expr>           Evals expression in list context, dumps the result.
+B<m> I<expr>           Evals expression in list context, prints methods callable
                on the first element of the result.
 B<m> I<class>          Prints methods callable via the given class.
-B<O> [I<opt>[B<=>I<val>]] [I<opt>B<\">I<val>B<\">] [I<opt>B<?>]...
-               Set or query values of options.  I<val> defaults to 1.  I<opt> can
-               be abbreviated.  Several options can be listed.
-    I<recallCommand>, I<ShellBang>:    chars used to recall command or spawn shell;
-    I<pager>:                  program for output of \"|cmd\";
-    I<tkRunning>:                      run Tk while prompting (with ReadLine);
-    I<signalLevel> I<warnLevel> I<dieLevel>:   level of verbosity;
-    I<inhibit_exit>            Allows stepping off the end of the script.
-    I<ImmediateStop>           Debugger should stop as early as possible.
-  The following options affect what happens with B<V>, B<X>, and B<x> commands:
-    I<arrayDepth>, I<hashDepth>:       print only first N elements ('' for all);
-    I<compactDump>, I<veryCompact>:    change style of array and hash dump;
-    I<globPrint>:                      whether to print contents of globs;
-    I<DumpDBFiles>:            dump arrays holding debugged files;
-    I<DumpPackages>:           dump symbol tables of packages;
-    I<DumpReused>:             dump contents of \"reused\" addresses;
-    I<quote>, I<HighBit>, I<undefPrint>:       change style of string dump;
-    I<bareStringify>:          Do not print the overload-stringified value;
-  Option I<PrintRet> affects printing of return value after B<r> command,
-         I<frame>    affects printing messages on entry and exit from subroutines.
-         I<AutoTrace> affects printing messages on every possible breaking point.
-        I<maxTraceLen> gives maximal length of evals/args listed in stack trace.
-        I<ornaments> affects screen appearance of the command line.
-               During startup options are initialized from \$ENV{PERLDB_OPTS}.
-               You can put additional initialization options I<TTY>, I<noTTY>,
-               I<ReadLine>, and I<NonStop> there (or use `B<R>' after you set them).
+
+B<<> ?                 List Perl commands to run before each prompt.
 B<<> I<expr>           Define Perl command to run before each prompt.
 B<<<> I<expr>          Add to the list of Perl commands to run before each prompt.
+B<>> ?                 List Perl commands to run after each prompt.
 B<>> I<expr>           Define Perl command to run after each prompt.
-B<>>B<>> I<expr>       Add to the list of Perl commands to run after each prompt.
+B<>>B<>> I<expr>               Add to the list of Perl commands to run after each prompt.
 B<{> I<db_command>     Define debugger command to run before each prompt.
+B<{> ?                 List debugger commands to run before each prompt.
 B<{{> I<db_command>    Add to the list of debugger commands to run before each prompt.
 B<$prc> I<number>      Redo a previous command (default previous command).
 B<$prc> I<-number>     Redo number'th-to-last command.
@@ -1854,6 +2500,7 @@ B<$psh$psh> I<cmd>        Run cmd in a subprocess (reads from DB::IN, writes to DB::O
   . ( $rc eq $sh ? "" : "
 B<$psh> [I<cmd>]       Run I<cmd> in subshell (forces \"\$SHELL -c 'cmd'\")." ) . "
                See 'B<O> I<shellBang>' too.
+B<@>I<file>            Execute I<file> containing debugger commands (may nest).
 B<H> I<-number>        Display last number commands (default all).
 B<p> I<expr>           Same as \"I<print {DB::OUT} expr>\" in current package.
 B<|>I<dbcmd>           Run debugger command, piping DB::OUT to current pager.
@@ -1863,16 +2510,56 @@ I<command>              Execute as a perl statement in current package.
 B<v>           Show versions of loaded modules.
 B<R>           Pure-man-restart of debugger, some of debugger state
                and command-line options may be lost.
-               Currently the following setting are preserved: 
+               Currently the following settings are preserved:
                history, breakpoints and actions, debugger B<O>ptions 
                and the following command-line options: I<-w>, I<-I>, I<-e>.
+
+B<O> [I<opt>] ...      Set boolean option to true
+B<O> [I<opt>B<?>]      Query options
+B<O> [I<opt>B<=>I<val>] [I<opt>=B<\">I<val>B<\">] ... 
+               Set options.  Use quotes in spaces in value.
+    I<recallCommand>, I<ShellBang>     chars used to recall command or spawn shell;
+    I<pager>                   program for output of \"|cmd\";
+    I<tkRunning>                       run Tk while prompting (with ReadLine);
+    I<signalLevel> I<warnLevel> I<dieLevel>    level of verbosity;
+    I<inhibit_exit>            Allows stepping off the end of the script.
+    I<ImmediateStop>           Debugger should stop as early as possible.
+    I<RemotePort>                      Remote hostname:port for remote debugging
+  The following options affect what happens with B<V>, B<X>, and B<x> commands:
+    I<arrayDepth>, I<hashDepth>        print only first N elements ('' for all);
+    I<compactDump>, I<veryCompact>     change style of array and hash dump;
+    I<globPrint>                       whether to print contents of globs;
+    I<DumpDBFiles>             dump arrays holding debugged files;
+    I<DumpPackages>            dump symbol tables of packages;
+    I<DumpReused>                      dump contents of \"reused\" addresses;
+    I<quote>, I<HighBit>, I<undefPrint>        change style of string dump;
+    I<bareStringify>           Do not print the overload-stringified value;
+  Other options include:
+    I<PrintRet>                affects printing of return value after B<r> command,
+    I<frame>           affects printing messages on subroutine entry/exit.
+    I<AutoTrace>       affects printing messages on possible breaking points.
+    I<maxTraceLen>     gives max length of evals/args listed in stack trace.
+    I<ornaments>       affects screen appearance of the command line.
+    I<CreateTTY>       bits control attempts to create a new TTY on events:
+                       1: on fork()    2: debugger is started inside debugger
+                       4: on startup
+       During startup options are initialized from \$ENV{PERLDB_OPTS}.
+       You can put additional initialization options I<TTY>, I<noTTY>,
+       I<ReadLine>, I<NonStop>, and I<RemotePort> there (or use
+       `B<R>' after you set them).
+
+B<q> or B<^D>          Quit. Set B<\$DB::finished = 0> to debug global destruction.
 B<h> [I<db_command>]   Get help [on a specific debugger command], enter B<|h> to page.
-               Complete description of debugger is available in B<perldebug>
-               section of Perl documention
 B<h h>         Summary of debugger commands.
-B<q> or B<^D>          Quit. Set B<\$DB::finished = 0> to debug global destruction.
+B<$doccmd> I<manpage>  Runs the external doc viewer B<$doccmd> command on the 
+               named Perl I<manpage>, or on B<$doccmd> itself if omitted.
+               Set B<\$DB::doccmd> to change viewer.
+
+Type `|h' for a paged display if this was too hard to read.
+
+"; # Fix balance of vi % matching: }}}}
 
-";
+    #  note: tabs in the following section are not-so-helpful
     $summary = <<"END_SUM";
 I<List/search source lines:>               I<Control script execution:>
   B<l> [I<ln>|I<sub>]  List source code            B<T>           Stack trace
@@ -1880,7 +2567,7 @@ I<List/search source lines:>               I<Control script execution:>
   B<w> [I<line>]    List around line            B<n> [I<expr>]    Next, steps over subs
   B<f> I<filename>  View source in file         <B<CR>/B<Enter>>  Repeat last B<n> or B<s>
   B</>I<pattern>B</> B<?>I<patt>B<?>   Search forw/backw    B<r>           Return from subroutine
-  B<v>       Show versions of modules    B<c> [I<ln>|I<sub>]  Continue until position
+  B<v>           Show versions of modules    B<c> [I<ln>|I<sub>]  Continue until position
 I<Debugger controls:>                        B<L>           List break/watch/actions
   B<O> [...]     Set debugger options        B<t> [I<expr>]    Toggle trace [trace expr]
   B<<>[B<<>]|B<{>[B<{>]|B<>>[B<>>] [I<cmd>] Do pre/post-prompt B<b> [I<ln>|I<event>|I<sub>] [I<cnd>] Set breakpoint
@@ -1889,25 +2576,78 @@ I<Debugger controls:>                        B<L>           List break/watch/act
   B<=> [I<a> I<val>]   Define/list an alias        B<W> I<expr>      Add a watch expression
   B<h> [I<db_cmd>]  Get help on command         B<A> or B<W>      Delete all actions/watch
   B<|>[B<|>]I<db_cmd>  Send output to pager        B<$psh>\[B<$psh>\] I<syscmd> Run cmd in a subprocess
-  B<q> or B<^D>     Quit                         B<R>        Attempt a restart
-I<Data Examination:>         B<expr>     Execute perl code, also see: B<s>,B<n>,B<t> I<expr>
-  B<x>|B<m> I<expr>    Evals expr in array context, dumps the result or lists methods.
-  B<p> I<expr> Print expression (uses script's current package).
-  B<S> [[B<!>]I<pat>]  List subroutine names [not] matching pattern
-  B<V> [I<Pk> [I<Vars>]]       List Variables in Package.  Vars can be ~pattern or !pattern.
-  B<X> [I<Vars>]       Same as \"B<V> I<current_package> [I<Vars>]\".
-I<More help for> B<db_cmd>I<:>  Type B<h> I<cmd_letter>  Run B<perldoc perldebug> for more help.
+  B<q> or B<^D>     Quit                        B<R>           Attempt a restart
+I<Data Examination:>     B<expr>     Execute perl code, also see: B<s>,B<n>,B<t> I<expr>
+  B<x>|B<m> I<expr>       Evals expr in list context, dumps the result or lists methods.
+  B<p> I<expr>         Print expression (uses script's current package).
+  B<S> [[B<!>]I<pat>]     List subroutine names [not] matching pattern
+  B<V> [I<Pk> [I<Vars>]]  List Variables in Package.  Vars can be ~pattern or !pattern.
+  B<X> [I<Vars>]       Same as \"B<V> I<current_package> [I<Vars>]\".
+For more help, type B<h> I<cmd_letter>, or run B<$doccmd perldebug> for all docs.
 END_SUM
-                               # ')}}; # Fix balance of Emacs parsing
+                               # ')}}; # Fix balance of vi % matching
 }
 
 sub print_help {
-  my $message = shift;
-  if (@Term::ReadLine::TermCap::rl_term_set) {
-    $message =~ s/B<([^>]+|>)>/$Term::ReadLine::TermCap::rl_term_set[2]$1$Term::ReadLine::TermCap::rl_term_set[3]/g;
-    $message =~ s/I<([^>]+|>)>/$Term::ReadLine::TermCap::rl_term_set[0]$1$Term::ReadLine::TermCap::rl_term_set[1]/g;
-  }
-  print $OUT $message;
+    local $_ = shift;
+
+    # Restore proper alignment destroyed by eeevil I<> and B<>
+    # ornaments: A pox on both their houses!
+    #
+    # A help command will have everything up to and including
+    # the first tab sequence padded into a field 16 (or if indented 20)
+    # wide.  If it's wider than that, an extra space will be added.
+    s{
+       ^                       # only matters at start of line
+         ( \040{4} | \t )*     # some subcommands are indented
+         ( < ?                 # so <CR> works
+           [BI] < [^\t\n] + )  # find an eeevil ornament
+         ( \t+ )               # original separation, discarded
+         ( .* )                # this will now start (no earlier) than 
+                               # column 16
+    } {
+       my($leadwhite, $command, $midwhite, $text) = ($1, $2, $3, $4);
+       my $clean = $command;
+       $clean =~ s/[BI]<([^>]*)>/$1/g;  
+    # replace with this whole string:
+       ($leadwhite ? " " x 4 : "")
+      . $command
+      . ((" " x (16 + ($leadwhite ? 4 : 0) - length($clean))) || " ")
+      . $text;
+
+    }mgex;
+
+    s{                         # handle bold ornaments
+       B < ( [^>] + | > ) >
+    } {
+         $Term::ReadLine::TermCap::rl_term_set[2] 
+       . $1
+       . $Term::ReadLine::TermCap::rl_term_set[3]
+    }gex;
+
+    s{                         # handle italic ornaments
+       I < ( [^>] + | > ) >
+    } {
+         $Term::ReadLine::TermCap::rl_term_set[0] 
+       . $1
+       . $Term::ReadLine::TermCap::rl_term_set[1]
+    }gex;
+
+    print $OUT $_;
+}
+
+sub fix_less {
+    return if defined $ENV{LESS} && $ENV{LESS} =~ /r/;
+    my $is_less = $pager =~ /\bless\b/;
+    if ($pager =~ /\bmore\b/) { 
+       my @st_more = stat('/usr/bin/more');
+       my @st_less = stat('/usr/bin/less');
+       $is_less = @st_more    && @st_less 
+               && $st_more[0] == $st_less[0] 
+               && $st_more[1] == $st_less[1];
+    }
+    # changes environment!
+    $ENV{LESS} .= 'r'  if $is_less;
 }
 
 sub diesignal {
@@ -1933,7 +2673,7 @@ sub dbwarn {
   local $SIG{__DIE__} = '';
   eval { require Carp } if defined $^S;        # If error/warning during compilation,
                                         # require may be broken.
-  warn(@_, "\nCannot print stack trace, load with -MCarp option to see stack"),
+  CORE::warn(@_, "\nCannot print stack trace, load with -MCarp option to see stack"),
     return unless defined &Carp::longmess;
   my ($mysingle,$mytrace) = ($single,$trace);
   $single = 0; $trace = 0;
@@ -1956,15 +2696,24 @@ sub dbdie {
   if ($dieLevel < 2) {
     die @_ if $^S;             # in eval propagate
   }
-  eval { require Carp } if defined $^S;        # If error/warning during compilation,
+  # No need to check $^S, eval is much more robust nowadays
+  eval { require Carp }; #if defined $^S;# If error/warning during compilation,
                                        # require may be broken.
+
   die(@_, "\nCannot print stack trace, load with -MCarp option to see stack")
     unless defined &Carp::longmess;
+
   # We do not want to debug this chunk (automatic disabling works
   # inside DB::DB, but not in Carp).
   my ($mysingle,$mytrace) = ($single,$trace);
   $single = 0; $trace = 0;
-  my $mess = Carp::longmess(@_);
+  my $mess = "@_";
+  { 
+    package Carp;              # Do not include us in the list
+    eval {
+      $mess = Carp::longmess(@_);
+    };
+  }
   ($single,$trace) = ($mysingle,$mytrace);
   die $mess;
 }
@@ -1975,7 +2724,7 @@ sub warnLevel {
     $warnLevel = shift;
     if ($warnLevel) {
       $SIG{__WARN__} = \&DB::dbwarn;
-    } else {
+    } elsif ($prevwarn) {
       $SIG{__WARN__} = $prevwarn;
     }
   }
@@ -1993,7 +2742,7 @@ sub dieLevel {
         ( $dieLevel == 1 ? " outside of evals" : ""), ".\n"
          if $I_m_init;
       print $OUT "Dump printed too.\n" if $dieLevel > 2;
-    } else {
+    } elsif ($prevdie) {
       $SIG{__DIE__} = $prevdie;
       print $OUT "Default die handler restored.\n";
     }
@@ -2017,10 +2766,32 @@ sub signalLevel {
   $signalLevel;
 }
 
+sub CvGV_name {
+  my $in = shift;
+  my $name = CvGV_name_or_bust($in);
+  defined $name ? $name : $in;
+}
+
+sub CvGV_name_or_bust {
+  my $in = shift;
+  return if $skipCvGV;         # Backdoor to avoid problems if XS broken...
+  return unless ref $in;
+  $in = \&$in;                 # Hard reference...
+  eval {require Devel::Peek; 1} or return;
+  my $gv = Devel::Peek::CvGV($in) or return;
+  *$gv{PACKAGE} . '::' . *$gv{NAME};
+}
+
 sub find_sub {
   my $subr = shift;
-  return unless defined &$subr;
   $sub{$subr} or do {
+    return unless defined &$subr;
+    my $name = CvGV_name_or_bust($subr);
+    my $data;
+    $data = $sub{$name} if defined $name;
+    return $data if defined $data;
+
+    # Old stupid way...
     $subr = \&$subr;           # Hard reference
     my $s;
     for (keys %sub) {
@@ -2045,18 +2816,81 @@ sub methods_via {
   my $prefix = shift;
   my $prepend = $prefix ? "via $prefix: " : '';
   my $name;
-  for $name (grep {defined &{$ {"$ {class}::"}{$_}}} 
-            sort keys %{"$ {class}::"}) {
+  for $name (grep {defined &{${"${class}::"}{$_}}} 
+            sort keys %{"${class}::"}) {
     next if $seen{ $name }++;
     print $DB::OUT "$prepend$name\n";
   }
   return unless shift;         # Recurse?
-  for $name (@{"$ {class}::ISA"}) {
+  for $name (@{"${class}::ISA"}) {
     $prepend = $prefix ? $prefix . " -> $name" : $name;
     methods_via($name, $prepend, 1);
   }
 }
 
+sub setman { 
+    $doccmd = $^O !~ /^(?:MSWin32|VMS|os2|dos|amigaos|riscos|MacOS|NetWare)\z/s
+               ? "man"             # O Happy Day!
+               : "perldoc";        # Alas, poor unfortunates
+}
+
+sub runman {
+    my $page = shift;
+    unless ($page) {
+       &system("$doccmd $doccmd");
+       return;
+    } 
+    # this way user can override, like with $doccmd="man -Mwhatever"
+    # or even just "man " to disable the path check.
+    unless ($doccmd eq 'man') {
+       &system("$doccmd $page");
+       return;
+    } 
+
+    $page = 'perl' if lc($page) eq 'help';
+
+    require Config;
+    my $man1dir = $Config::Config{'man1dir'};
+    my $man3dir = $Config::Config{'man3dir'};
+    for ($man1dir, $man3dir) { s#/[^/]*\z## if /\S/ } 
+    my $manpath = '';
+    $manpath .= "$man1dir:" if $man1dir =~ /\S/;
+    $manpath .= "$man3dir:" if $man3dir =~ /\S/ && $man1dir ne $man3dir;
+    chop $manpath if $manpath;
+    # harmless if missing, I figure
+    my $oldpath = $ENV{MANPATH};
+    $ENV{MANPATH} = $manpath if $manpath;
+    my $nopathopt = $^O =~ /dunno what goes here/;
+    if (CORE::system($doccmd, 
+               # I just *know* there are men without -M
+               (($manpath && !$nopathopt) ? ("-M", $manpath) : ()),  
+           split ' ', $page) )
+    {
+       unless ($page =~ /^perl\w/) {
+           if (grep { $page eq $_ } qw{ 
+               5004delta 5005delta amiga api apio book boot bot call compile
+               cygwin data dbmfilter debug debguts delta diag doc dos dsc embed
+               faq faq1 faq2 faq3 faq4 faq5 faq6 faq7 faq8 faq9 filter fork
+               form func guts hack hist hpux intern ipc lexwarn locale lol mod
+               modinstall modlib number obj op opentut os2 os390 pod port 
+               ref reftut run sec style sub syn thrtut tie toc todo toot tootc
+               trap unicode var vms win32 xs xstut
+             }) 
+           {
+               $page =~ s/^/perl/;
+               CORE::system($doccmd, 
+                       (($manpath && !$nopathopt) ? ("-M", $manpath) : ()),  
+                       $page);
+           }
+       }
+    } 
+    if (defined $oldpath) {
+       $ENV{MANPATH} = $manpath;
+    } else {
+       delete $ENV{MANPATH};
+    } 
+} 
+
 # The following BEGIN is very handy if debugger goes havoc, debugging debugger?
 
 BEGIN {                        # This does not compile, alas.
@@ -2088,13 +2922,13 @@ BEGIN {                 # This does not compile, alas.
 
 BEGIN {$^W = $ini_warn;}       # Switch warnings back
 
-#use Carp;                     # This did break, left for debuggin
+#use Carp;                     # This did break, left for debugging
 
 sub db_complete {
   # Specific code for b c l V m f O, &blah, $blah, @blah, %blah
   my($text, $line, $start) = @_;
   my ($itext, $search, $prefix, $pack) =
-    ($text, "^\Q$ {'package'}::\E([^:]+)\$");
+    ($text, "^\Q${'package'}::\E([^:]+)\$");
   
   return sort grep /^\Q$text/, (keys %sub), qw(postpone load compile), # subroutines
                                (map { /$search/ ? ($1) : () } keys %sub)
@@ -2174,11 +3008,20 @@ sub end_report {
   print $OUT "Use `q' to quit or `R' to restart.  `h q' for details.\n"
 }
 
+sub clean_ENV {
+    if (defined($ini_pids)) {
+        $ENV{PERLDB_PIDS} = $ini_pids;
+    } else {
+        delete($ENV{PERLDB_PIDS});
+    }
+}
+
 END {
-  $finished = $inhibit_exit;   # So that some keys may be disabled.
+  $finished = 1 if $inhibit_exit;      # So that some keys may be disabled.
+  $fall_off_end = 1 unless $inhibit_exit;
   # Do not stop in at_exit() and destructors on exit:
-  $DB::single = !$exiting && !$runnonstop;
-  DB::fake::at_exit() unless $exiting or $runnonstop;
+  $DB::single = !$fall_off_end && !$runnonstop;
+  DB::fake::at_exit() unless $fall_off_end or $runnonstop;
 }
 
 package DB::fake;