This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Math::Big* test tweaks to work better with core:
[perl5.git] / lib / perl5db.pl
index dbc5531..aab1a68 100644 (file)
@@ -2,16 +2,8 @@ package DB;
 
 # Debugger for Perl 5.00x; perl5db.pl patch level:
 
-$VERSION = 0.9906;
-$header = "perl5db.pl patch level $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 ;-)
+$VERSION = 1.14;
+$header = "perl5db.pl version $VERSION";
 
 #
 # This file is automatically included if you do perl -d.
@@ -33,7 +25,7 @@ $header = "perl5db.pl patch level $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 patch level $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 patch level $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 patch level $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 patch level $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 patch level $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 patch level $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():
@@ -173,26 +284,34 @@ $trace = $signal = $single = 0;   # Uninitialized warning suppression
                                 # (local $^W cannot help - other packages!).
 $inhibit_exit = $option{PrintRet} = 1;
 
-@options     = qw(hashDepth arrayDepth DumpDBFiles DumpPackages 
+@options     = qw(hashDepth arrayDepth DumpDBFiles DumpPackages DumpReused
                  compactDump veryCompact quote HighBit undefPrint
                  globPrint PrintRet UsageOnly frame AutoTrace
                  TTY noTTY ReadLine NonStop LineInfo maxTraceLen
-                 recallCommand ShellBang pager tkRunning
-                 signalLevel warnLevel dieLevel inhibit_exit);
+                 recallCommand ShellBang pager tkRunning ornaments
+                 signalLevel warnLevel dieLevel inhibit_exit
+                 ImmediateStop bareStringify CreateTTY
+                 RemotePort windowSize);
 
 %optionVars    = (
                 hashDepth      => \$dumpvar::hashDepth,
                 arrayDepth     => \$dumpvar::arrayDepth,
                 DumpDBFiles    => \$dumpvar::dumpDBFiles,
                 DumpPackages   => \$dumpvar::dumpPackages,
+                DumpReused     => \$dumpvar::dumpReused,
                 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  = (
@@ -211,6 +330,8 @@ $inhibit_exit = $option{PrintRet} = 1;
                  warnLevel     => \&warnLevel,
                  dieLevel      => \&dieLevel,
                  tkRunning     => \&tkRunning,
+                 ornaments     => \&ornaments,
+                 RemotePort    => \&RemotePort,
                 );
 
 %optionRequire = (
@@ -220,39 +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}) ? $ENV{PAGER} : "|more") unless defined $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;
@@ -261,7 +457,8 @@ if (exists $ENV{PERLDB_RESTART}) {
   %postponed = get_list("PERLDB_POSTPONE");
   my @had_breakpoints= get_list("PERLDB_VISITED");
   for (0 .. $#had_breakpoints) {
-    %{$postponed_file{$had_breakpoints[$_]}} = get_list("PERLDB_FILE_$_");
+    my %pf = get_list("PERLDB_FILE_$_");
+    $postponed_file{$had_breakpoints[$_]} = \%pf if %pf;
   }
   my %opt = get_list("PERLDB_OPT");
   my ($opt,$val);
@@ -271,61 +468,102 @@ if (exists $ENV{PERLDB_RESTART}) {
   }
   @INC = get_list("PERLDB_INC");
   @ini_INC = @INC;
+  $pretype = [get_list("PERLDB_PRETYPE")];
+  $pre = [get_list("PERLDB_PRE")];
+  $post = [get_list("PERLDB_POST")];
+  @typeahead = get_list("PERLDB_TYPEAHEAD", @typeahead);
 }
 
 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 (-e "/dev/tty") {
+  if ($^O eq 'cygwin') {
+    # /dev/tty is binary. use stdin for textmode
+    undef $console;
+  } elsif (-e "/dev/tty") {
     $console = "/dev/tty";
-  } elsif (-e "con") {
+  } 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 ($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;
+  }
+
+  if ($^O eq 'epoc') {
     $console = undef;
   }
 
   $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.\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";
+    }
   }
 }
 
@@ -339,42 +577,78 @@ if (defined &afterinit) { # May be defined in $rcfile
   &afterinit();
 }
 
+$I_m_init = 1;
+
 ############################################################ Subroutines
 
 sub DB {
     # _After_ the perl program is compiled, $single is set to 1:
     if ($single and not $second_time++) {
       if ($runnonstop) {       # Disable until signal
-       for ($i=0; $i <= $#stack; ) {
+       for ($i=0; $i <= $stack_depth; ) {
            $stack[$i++] &= ~1;
        }
        $single = 0;
        # return;                       # Would not print trace!
+      } elsif ($ImmediateStop) {
+       $ImmediateStop = 0;
+       $signal = 1;
       }
     }
     $runnonstop = 0 if $single or $signal; # Disable it if interactive.
     &save;
     ($package, $filename, $line) = caller;
     $filename_ini = $filename;
-    $usercontext = '($@, $!, $,, $/, $\, $^W) = @saved;' .
+    $usercontext = '($@, $!, $^E, $,, $/, $\, $^W) = @saved;' .
       "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) {
-           $evalarg = "\$DB::signal |= do {$stop;}"; &eval;
+           $evalarg = "\$DB::signal |= 1 if do {$stop}"; &eval;
            $dbline{$line} =~ s/;9($|\0)/$1/;
        }
     }
     my $was_signal = $signal;
+    if ($trace & 2) {
+      for (my $n = 0; $n <= $#to_watch; $n++) {
+       $evalarg = $to_watch[$n];
+       local $onetimeDump;     # Do not output results
+       my ($val) = &eval;      # Fix context (&eval is doing array)?
+       $val = ( (defined $val) ? "'$val'" : 'undef' );
+       if ($val ne $old_watch[$n]) {
+         $signal = 1;
+         print $OUT <<EOP;
+Watchpoint $n:\t$to_watch[$n] changed:
+    old value:\t$old_watch[$n]
+    new value:\t$val
+EOP
+         $old_watch[$n] = $val;
+       }
+      }
+    }
+    if ($trace & 4) {          # User-installed watch
+      return if watchfunction($package, $filename, $line) 
+       and not $single and not $was_signal and not ($trace & ~4);
+    }
+    $was_signal = $signal;
     $signal = 0;
-    if ($single || $trace || $was_signal) {
-       $term || &setterm;
-       if ($emacs) {
+    if ($single || ($trace & 1) || $was_signal) {
+       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);
+Debugged program terminated.  Use B<q> to quit or B<R> to restart,
+  use B<O> I<inhibit_exit> to avoid stopping after program termination,
+  B<h q>, B<h R> or B<h O> to get additional info.  
+EOP
+         $package = 'main';
+         $usercontext = '($@, $!, $^E, $,, $/, $\, $^W) = @saved;' .
+           "package $package;";        # this won't let them modify, alas
        } else {
            $sub =~ s/\'/::/;
            $prefix = $sub =~ /::/ ? "" : "${'package'}::";
@@ -389,9 +663,9 @@ sub DB {
                $position = "$prefix$line$infix$dbline[$line]$after";
            }
            if ($frame) {
-               print $LINEINFO ' ' x $#stack, "$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]/;
@@ -400,9 +674,9 @@ sub DB {
                $incr_pos = "$prefix$i$infix$dbline[$i]$after";
                $position .= $incr_pos;
                if ($frame) {
-                   print $LINEINFO ' ' x $#stack, "$i:\t$dbline[$i]$after";
+                   print_lineinfo(' ' x $stack_depth, "$i:\t$dbline[$i]$after");
                } else {
-                   print $LINEINFO $incr_pos;
+                   print_lineinfo($incr_pos);
                }
            }
        }
@@ -410,17 +684,21 @@ sub DB {
     $evalarg = $action, &eval if $action;
     if ($single || $was_signal) {
        local $level = $level + 1;
-       map {$evalarg = $_, &eval} @$pre;
-       print $OUT $#stack . " levels deep in subroutine calls!\n"
+       foreach $evalarg (@$pre) {
+         &eval;
+       }
+       print $OUT $stack_depth . " levels deep in subroutine calls!\n"
          if $single & 4;
        $start = $line;
        $incr = -1;             # for backward motion.
-       @typeahead = @$pretype, @typeahead;
+       @typeahead = (@$pretype, @typeahead);
       CMD:
        while (($term || &setterm),
-              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 {
@@ -430,28 +708,44 @@ sub DB {
                $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 $OUT $help;
+                       print_help($help);
                        next CMD; };
                    $cmd =~ /^h\s+h$/ && do {
-                       print $OUT $summary;
+                       print_help($summary);
                        next CMD; };
-                   $cmd =~ /^h\s+(\S)$/ && do {
-                       my $asked = "\Q$1";
-                       if ($help =~ /^$asked/m) {
-                         while ($help =~ /^($asked([\s\S]*?)\n)(\Z|[^\s$asked])/mg) {
-                           print $OUT $1;
+                   # 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 {
-                           print $OUT "`$asked' is not a debugger command.\n";
+                           print_help("B<$asked> is not a debugger command.\n");
                        }
                        next CMD; };
                    $cmd =~ /^t$/ && do {
-                       $trace = !$trace;
-                       print $OUT "Trace = ".($trace?"on":"off")."\n";
+                       $trace ^= 1;
+                       print $OUT "Trace = " .
+                           (($trace & 1) ? "on" : "off" ) . "\n";
                        next CMD; };
                    $cmd =~ /^S(\s+(!)?(.+))?$/ && do {
                        $Srev = defined $2; $Spatt = $3; $Snocheck = ! defined $1;
@@ -474,7 +768,11 @@ sub DB {
                        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";
                        }
@@ -516,16 +814,29 @@ sub DB {
                          }
                      };
                    $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;
@@ -545,7 +856,7 @@ sub DB {
                        $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;
@@ -573,12 +884,14 @@ sub DB {
                        $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) 
                                  ?  '==>' 
@@ -586,8 +899,9 @@ sub DB {
                                $arrow .= 'b' if $stop;
                                $arrow .= 'a' if $action;
                                print $OUT "$i$arrow\t", $dbline[$i];
-                               last if $signal;
+                               $i++, last if $signal;
                            }
+                           print $OUT "\n" unless $dbline[$i-1] =~ /\n$/;
                        }
                        $start = $i; # remember in case they want more
                        $start = $max if $start > $max;
@@ -608,11 +922,14 @@ sub DB {
                                }
                            }
                        }
+                       
+                       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;
@@ -623,7 +940,7 @@ sub DB {
                        
                        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"
@@ -649,12 +966,11 @@ sub DB {
                        print $OUT "Postponed breakpoints in files:\n";
                        my ($file, $line);
                        for $file (keys %postponed_file) {
-                         my %db = %{$postponed_file{$file}};
-                         next unless keys %db;
+                         my $db = $postponed_file{$file};
                          print $OUT " $file:\n";
-                         for $line (sort {$a <=> $b} keys %db) {
+                         for $line (sort {$a <=> $b} keys %$db) {
                                print $OUT "  $line:\n";
-                               my ($stop,$action) = split(/\0/, $db{$line});
+                               my ($stop,$action) = split(/\0/, $$db{$line});
                                print $OUT "    break if (", $stop, ")\n"
                                  if $stop;
                                print $OUT "    action:  ", $action, "\n"
@@ -672,64 +988,44 @@ sub DB {
                          last if $signal;
                        }
                      }
+                     if ($trace & 2) {
+                       print $OUT "Watch-expressions:\n";
+                       my $expr;
+                       for $expr (@to_watch) {
+                         print $OUT " $expr\n";
+                         last if $signal;
+                       }
+                     }
                      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};
@@ -742,6 +1038,10 @@ sub DB {
                                delete $dbline{$i} if $dbline{$i} eq '';
                            }
                        }
+                       
+                       unless ($had_breakpoints{$file} &= ~2) {
+                           delete $had_breakpoints{$file};
+                       }
                      }
                      next CMD; };
                    $cmd =~ /^O\s*$/ && do {
@@ -759,27 +1059,90 @@ sub DB {
                        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 {
@@ -794,14 +1157,20 @@ sub DB {
                        last CMD; };
                    $cmd =~ /^c\b\s*([\w:]*)\s*$/ && do {
                        end_report(), next CMD if $finished and $level <= 1;
-                       $i = $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
-                           ($file,$i) = (find_sub($i) =~ /^(.*):(.*)$/);
+                           $subname = $package."::".$subname 
+                               unless $subname =~ /::/;
+                           ($file,$i) = (find_sub($subname) =~ /^(.*):(.*)$/);
                            $i += 0;
                            if ($i) {
                                $filename = $file;
                                *dbline = $main::{'_<' . $filename};
-                               $had_breakpoints{$filename}++;
+                               $had_breakpoints{$filename} |= 1;
                                $max = $#dbline;
                                ++$i while $dbline[$i] == 0 && $i < $max;
                            } else {
@@ -816,14 +1185,14 @@ sub DB {
                            }
                            $dbline{$i} =~ s/($|\0)/;9$1/; # add one-time-only b.p.
                        }
-                       for ($i=0; $i <= $#stack; ) {
+                       for ($i=0; $i <= $stack_depth; ) {
                            $stack[$i++] &= ~1;
                        }
                        last CMD; };
                    $cmd =~ /^r$/ && do {
                        end_report(), next CMD if $finished and $level <= 1;
-                       $stack[$#stack] |= 1;
-                       $doret = $option{PrintRet} ? $#stack - 1 : -2;
+                       $stack[$stack_depth] |= 1;
+                       $doret = $option{PrintRet} ? $stack_depth - 1 : -2;
                        last CMD; };
                    $cmd =~ /^R$/ && do {
                        print $OUT "Warning: some settings and command-line options may be lost!\n";
@@ -838,7 +1207,7 @@ sub DB {
                        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 {
@@ -855,12 +1224,12 @@ sub DB {
                        for (0 .. $#had_breakpoints) {
                          my $file = $had_breakpoints[$_];
                          *dbline = $main::{'_<' . $file};
-                         next unless %dbline or %{$postponed_file{$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}};
+                           if $postponed_file{$file};
                          set_list("PERLDB_FILE_$_", %dbline, @add);
                        }
                        for (@hard) { # Yes, really-really...
@@ -897,18 +1266,39 @@ sub DB {
                          }
                        }
                        set_list("PERLDB_POSTPONE", %postponed);
+                       set_list("PERLDB_PRETYPE", @$pretype);
+                       set_list("PERLDB_PRE", @$pre);
+                       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 {
                        print_trace($OUT, 1); # skip DB
                        next CMD; };
+                   $cmd =~ /^W\s*$/ && do {
+                       $trace &= ~2;
+                       @to_watch = @old_watch = ();
+                       next CMD; };
+                   $cmd =~ /^W\b\s*(.*)/s && do {
+                       push @to_watch, $1;
+                       $evalarg = $1;
+                       my ($val) = &eval;
+                       $val = (defined $val) ? "'$val'" : 'undef' ;
+                       push @old_watch, $val;
+                       $trace |= 2;
+                       next CMD; };
                    $cmd =~ /^\/(.*)$/ && do {
                        $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 "$@";
@@ -924,7 +1314,7 @@ sub DB {
                                $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";
@@ -938,9 +1328,12 @@ sub DB {
                        $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;
@@ -953,7 +1346,7 @@ sub DB {
                                $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";
@@ -965,9 +1358,9 @@ sub DB {
                        next CMD; };
                    $cmd =~ /^$rc+\s*(-)?(\d+)?$/ && do {
                        pop(@hist) if length($cmd) > 1;
-                       $i = $1 ? ($#hist-($2?$2:1)) : ($2?$2:$#hist);
-                       $cmd = $hist[$i] . "\n";
-                       print $OUT $cmd;
+                       $i = $1 ? ($#hist-($2||1)) : ($2||$#hist);
+                       $cmd = $hist[$i];
+                       print $OUT $cmd, "\n";
                        redo CMD; };
                    $cmd =~ /^$sh$sh\s*([\x00-\xff]*)/ && do {
                        &system($1);
@@ -982,39 +1375,72 @@ sub DB {
                            print $OUT "No such command!\n\n";
                            next CMD;
                        }
-                       $cmd = $hist[$i] . "\n";
-                       print $OUT $cmd;
+                       $cmd = $hist[$i];
+                       print $OUT $cmd, "\n";
                        redo CMD; };
                    $cmd =~ /^$sh$/ && do {
                        &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");
@@ -1022,47 +1448,64 @@ sub DB {
                        } 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/^t\s/\$DB::trace |= 1;\n/;
                    $cmd =~ s/^s\s/\$DB::single = 1;\n/ && do {$laststep = 's'};
                    $cmd =~ s/^n\s/\$DB::single = 2;\n/ && do {$laststep = 'n'};
                }               # PIPE:
            $evalarg = "\$^D = \$^D | \$DB::db_stop;\n$cmd"; &eval;
            if ($onetimeDump) {
                $onetimeDump = undef;
-           } else {
+           } elsif ($term_pid == $$) {
                print $OUT "\n";
            }
        } 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 {
@@ -1073,10 +1516,12 @@ sub DB {
                $piped= "";
            }
        }                       # CMD:
-       $exiting = 1 unless defined $cmd;
-        map {$evalarg = $_; &eval} @$post;
+       $fall_off_end = 1 unless defined $cmd; # Emulate `q' on EOF
+       foreach $evalarg (@$post) {
+         &eval;
+       }
     }                          # if ($single || $signal)
-    ($@, $!, $,, $/, $\, $^W) = @saved;
+    ($@, $!, $^E, $,, $/, $\, $^W) = @saved;
     ();
 }
 
@@ -1088,53 +1533,208 @@ sub sub {
     if (length($sub) > 10 && substr($sub, -10, 10) eq '::AUTOLOAD') {
        $al = " for $$sub";
     }
-    push(@stack, $single);
+    local $stack_depth = $stack_depth + 1; # Protect from non-local exits
+    $#stack = $stack_depth;
+    $stack[-1] = $single;
     $single &= 1;
-    $single |= 4 if $#stack == $deep;
+    $single |= 4 if $stack_depth == $deep;
     ($frame & 4 
-     ? ( (print $LINEINFO ' ' x ($#stack - 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 - 1), "entering $sub$al\n") if $frame;
+     : print_lineinfo(' ' x ($stack_depth - 1), "entering $sub$al\n")) if $frame;
     if (wantarray) {
        @ret = &$sub;
-       $single |= pop(@stack);
+       $single |= $stack[$stack_depth--];
        ($frame & 4 
-        ? ( (print $LINEINFO ' ' x $#stack, "out "), 
+        ? ( print_lineinfo(' ' x $stack_depth, "out "), 
             print_trace($LINEINFO, -1, 1, 1, "$sub$al") )
-        : print $LINEINFO ' ' x $#stack, "exited $sub$al\n") if $frame & 2;
-       print ($OUT ($frame & 16 ? ' ' x $#stack : ""),
-                   "list context return from $sub:\n"), dumpit( \@ret ),
-         $doret = -2 if $doret eq $#stack or $frame & 16;
+        : 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;
+           print $fh "list context return from $sub:\n"; 
+           dumpit($fh, \@ret );
+           $doret = -2;
+       }
        @ret;
     } else {
-       $ret = &$sub;
-       $single |= pop(@stack);
+        if (defined wantarray) {
+           $ret = &$sub;
+        } else {
+            &$sub; undef $ret;
+        };
+       $single |= $stack[$stack_depth--];
        ($frame & 4 
-        ? ( (print $LINEINFO ' ' x $#stack, "out "), 
+        ? (  print_lineinfo(' ' x $stack_depth, "out "),
              print_trace($LINEINFO, -1, 1, 1, "$sub$al") )
-        : print $LINEINFO ' ' x $#stack, "exited $sub$al\n") if $frame & 2;
-       print ($OUT ($frame & 16 ? ' ' x $#stack : ""),
-                   "scalar context return from $sub: "), dumpit( $ret ),
-         $doret = -2 if $doret eq $#stack or $frame & 16;
+        : 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;
+           print $fh (defined wantarray 
+                        ? "scalar context return from $sub: " 
+                        : "void context return from $sub\n");
+           dumpit( $fh, $ret ) if defined wantarray;
+           $doret = -2;
+       }
        $ret;
     }
 }
 
+### 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 = ($@, $!, $,, $/, $\, $^W);
+    @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;
     {
-       local (@stack) = @stack; # guard against recursive debugging
-       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;
@@ -1142,14 +1742,14 @@ sub eval {
     }
     my $at = $@;
     local $saved[0];           # Preserve the old value of $@
-    eval "&DB::save";
+    eval { &DB::save };
     if ($at) {
        print $OUT $at;
-    } elsif ($onetimeDump eq 'dump') {
-       dumpit(\@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;
 }
 
 sub postponed_sub {
@@ -1158,11 +1758,11 @@ sub postponed_sub {
     my $offset = $1 || 0;
     # Filename below can contain ':'
     my ($file,$i) = (find_sub($subname) =~ /^(.*):(\d+)-.*$/);
-    $i += $offset;
     if ($i) {
+      $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};
@@ -1176,6 +1776,10 @@ sub postponed_sub {
 }
 
 sub postponed {
+  if ($ImmediateStop) {
+    $ImmediateStop = 0;
+    $signal = 1;
+  }
   return &postponed_sub
     unless ref \$_[0] eq 'GLOB'; # A subroutine is compiled.
   # Cannot be done before the file is compiled
@@ -1184,19 +1788,19 @@ sub postponed {
   $filename =~ s/^_<//;
   $signal = 1, print $OUT "'$filename' loaded...\n"
     if $break_on_load{$filename};
-  print $LINEINFO ' ' x $#stack, "Package $filename.\n" if $frame;
-  return unless %{$postponed_file{$filename}};
-  $had_breakpoints{$filename}++;
+  print_lineinfo(' ' x $stack_depth, "Package $filename.\n") if $frame;
+  return unless $postponed_file{$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};
   }
-  undef %{$postponed_file{$filename}};
+  delete $postponed_file{$filename};
 }
 
 sub dumpit {
-    local ($savout) = select($OUT);
+    local ($savout) = select(shift);
     my $osingle = $single;
     my $otrace = $trace;
     $single = $trace = 0;
@@ -1219,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;
@@ -1277,7 +1882,7 @@ sub dump_trace {
        push(@a, $_);
       }
     }
-    $context = $context ? '@' : "\$";
+    $context = $context ? '@' : (defined $context ? "\$" : '.');
     $args = $h ? [@a] : undef;
     $e =~ s/\n\s*\;\s*\Z// if $e;
     $e =~ s/([\\\'])/\\$1/g if $e;
@@ -1306,51 +1911,78 @@ 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,">&OUT") || &warn("Can't save STDOUT");
+    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 {
     local $frame = 0;
     local $doret = -2;
-    local @stack = @stack;             # Prevent growth by failing `use'.
     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 {
@@ -1370,9 +2002,107 @@ sub setterm {
     if ($term->Features->{setHistory} and "@hist" ne "?") {
       $term->SetHistory(@hist);
     }
+    ornaments($ornaments) if defined $ornaments;
+    $term_pid = $$;
+}
+
+# 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' |];
+  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 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;
@@ -1383,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 {
@@ -1397,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) {
@@ -1424,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;
   }
@@ -1494,35 +2270,67 @@ sub warn {
     print $OUT $msg;
 }
 
-sub TTY {
-    if ($term) {
-       &warn("Too late to set TTY!\n") if @_;
+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 {
-       $tty = shift if @_;
+      ($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;
+      if ($in =~ /,/) {
+       ($in, $out) = split /,/, $in, 2;
+      } else {
+       $out = $in;
+      }
+      open IN, $in or die "cannot open `$in' for read: $!";
+      open OUT, ">$out" or die "cannot open `$out' for write: $!";
+      reset_IN_OUT(\*IN,\*OUT);
+      return $tty = $in;
     }
+    &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;
 }
 
 sub noTTY {
     if ($term) {
-       &warn("Too late to set noTTY!\n") if @_;
-    } else {
-       $notty = shift if @_;
+       &warn("Too late to set noTTY, enabled on next `R'!\n") if @_;
     }
+    $notty = shift if @_;
     $notty;
 }
 
 sub ReadLine {
     if ($term) {
-       &warn("Too late to set ReadLine!\n") if @_;
-    } else {
-       $rl = shift if @_;
+       &warn("Too late to set ReadLine, enabled on next `R'!\n") if @_;
     }
+    $rl = shift if @_;
     $rl;
 }
 
+sub RemotePort {
+    if ($term) {
+        &warn("Too late to set RemotePort, enabled on next 'R'!\n") if @_;
+    }
+    $remoteport = shift if @_;
+    $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";
@@ -1532,10 +2340,9 @@ sub tkRunning {
 
 sub NonStop {
     if ($term) {
-       &warn("Too late to set up NonStop mode!\n") if @_;
-    } else {
-       $runnonstop = shift if @_;
+       &warn("Too late to set up NonStop mode, enabled on next `R'!\n") if @_;
     }
+    $runnonstop = shift if @_;
     $runnonstop;
 }
 
@@ -1555,10 +2362,19 @@ sub shellBang {
     $psh = $sh;
     $psh =~ s/\\b$//;
     $psh =~ s/\\(.)/$1/g;
-    &sethelp;
     $psh;
 }
 
+sub ornaments {
+  if (defined $term) {
+    local ($warnLevel,$dieLevel) = (0, 1);
+    return '' unless $term->Features->{ornaments};
+    eval { $term->ornaments(@_) } || '';
+  } else {
+    $ornaments = shift;
+  }
+}
+
 sub recallCommand {
     if (@_) {
        $rc = quotemeta shift;
@@ -1567,7 +2383,6 @@ sub recallCommand {
     $prc = $rc;
     $prc =~ s/\\b$//;
     $prc =~ s/\\(.)/$1/g;
-    &sethelp;
     $prc;
 }
 
@@ -1575,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);
@@ -1593,148 +2408,246 @@ 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};
   }
-  do 'dumpvar.pl' unless defined &main::dumpValue;
-  if (defined &main::dumpValue) {
-    local $frame = 0;
-    &main::dumpValue(\%version);
-  } else {
-    print $OUT "dumpvar.pl not available.\n";
-  }
+  dumpit($OUT,\%version);
 }
 
 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 = "
-T              Stack trace.
-s [expr]       Single step [in expr].
-n [expr]       Next, steps over subroutine calls [in expr].
-<CR>           Repeat last n or s command.
-r              Return from current subroutine.
-c [line|sub]   Continue; optionally inserts a one-time-only breakpoint
+B<T>           Stack trace.
+B<s> [I<expr>] Single step [in I<expr>].
+B<n> [I<expr>] Next, steps over subroutine calls [in I<expr>].
+<B<CR>>                Repeat last B<n> or B<s> command.
+B<r>           Return from current subroutine.
+B<c> [I<line>|I<sub>]  Continue; optionally inserts a one-time-only breakpoint
                at the specified position.
-l min+incr     List incr+1 lines starting at min.
-l min-max      List lines min through max.
-l line         List single line.
-l subname      List first window of lines from subroutine.
-l              List next window of lines.
--              List previous window of lines.
-w [line]       List window around line.
-.              Return to the executed line.
-f filename     Switch to viewing filename. Must be loaded.
-/pattern/      Search forwards for pattern; final / is optional.
-?pattern?      Search backwards for pattern; final ? is optional.
-L              List all breakpoints and actions.
-S [[!]pattern] List subroutine names [not] matching pattern.
-t              Toggle trace mode.
-t expr         Trace through execution of expr.
-b [line] [condition]
-               Set breakpoint; line defaults to the current execution line;
-               condition breaks if it evaluates to true, defaults to '1'.
-b subname [condition]
+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>. 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.
+B<S> [[B<!>]I<pattern>]        List subroutine names [not] matching I<pattern>.
+B<t>           Toggle trace mode.
+B<t> I<expr>           Trace through execution of I<expr>.
+B<b> [I<line>] [I<condition>]
+               Set breakpoint; I<line> defaults to the current execution line;
+               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 load filename Set breakpoint on `require'ing the given file.
-b postpone subname [condition]
+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 
                it is compiled.
-b compile subname
+B<b> B<compile> I<subname>
                Stop after the subroutine is compiled.
-d [line]       Delete the breakpoint for line.
-D              Delete all breakpoints.
-a [line] command
-               Set an action to be done before the line is executed.
-               Sequence is: check for breakpoint, print line if necessary,
-               do action, prompt user if breakpoint or step, evaluate line.
-A              Delete all actions.
-V [pkg [vars]] List some (default all) variables in package (default current).
-               Use ~pattern and !pattern for positive and negative regexps.
-X [vars]       Same as \"V currentpackage [vars]\".
-x expr         Evals expression in array context, dumps the result.
-m expr         Evals expression in array context, prints methods callable
+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;
+               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 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 list context, dumps the result.
+B<m> I<expr>           Evals expression in list context, prints methods callable
                on the first element of the result.
-m class                Prints methods callable via the given class.
-O [opt[=val]] [opt\"val\"] [opt?]...
-               Set or query values of options.  val defaults to 1.  opt can
-               be abbreviated.  Several options can be listed.
-    recallCommand, ShellBang:  chars used to recall command or spawn shell;
-    pager:                     program for output of \"|cmd\";
-    tkRunning:                 run Tk while prompting (with ReadLine);
-    signalLevel warnLevel dieLevel:    level of verbosity;
-    inhibit_exit               Allows stepping off the end of the script.
-  The following options affect what happens with V, X, and x commands:
-    arrayDepth, hashDepth:     print only first N elements ('' for all);
-    compactDump, veryCompact:  change style of array and hash dump;
-    globPrint:                 whether to print contents of globs;
-    DumpDBFiles:               dump arrays holding debugged files;
-    DumpPackages:              dump symbol tables of packages;
-    quote, HighBit, undefPrint:        change style of string dump;
-  Option PrintRet affects printing of return value after r command,
-         frame    affects printing messages on entry and exit from subroutines.
-         AutoTrace affects printing messages on every possible breaking point.
-        maxTraceLen gives maximal length of evals/args listed in stack trace.
-               During startup options are initialized from \$ENV{PERLDB_OPTS}.
-               You can put additional initialization options TTY, noTTY,
-               ReadLine, and NonStop there.
-< command      Define Perl command to run before each prompt.
-<< command     Add to the list of Perl commands to run before each prompt.
-> command      Define Perl command to run after each prompt.
->> command     Add to the list of Perl commands to run after each prompt.
-\{ commandline Define debugger command to run before each prompt.
-\{{ commandline        Add to the list of debugger commands to run before each prompt.
-$prc number    Redo a previous command (default previous command).
-$prc -number   Redo number'th-to-last command.
-$prc pattern   Redo last command that started with pattern.
-               See 'O recallCommand' too.
-$psh$psh cmd   Run cmd in a subprocess (reads from DB::IN, writes to DB::OUT)"
+B<m> I<class>          Prints methods callable via the given class.
+
+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<{> 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.
+B<$prc> I<pattern>     Redo last command that started with I<pattern>.
+               See 'B<O> I<recallCommand>' too.
+B<$psh$psh> I<cmd>     Run cmd in a subprocess (reads from DB::IN, writes to DB::OUT)"
   . ( $rc eq $sh ? "" : "
-$psh [cmd]     Run cmd in subshell (forces \"\$SHELL -c 'cmd'\")." ) . "
-               See 'O shellBang' too.
-H -number      Display last number commands (default all).
-p expr         Same as \"print {DB::OUT} expr\" in current package.
-|dbcmd         Run debugger command, piping DB::OUT to current pager.
-||dbcmd                Same as |dbcmd but DB::OUT is temporarilly select()ed as well.
-\= [alias value]       Define a command alias, or list current aliases.
-command                Execute as a perl statement in current package.
-v              Show versions of loaded modules.
-R              Pure-man-restart of debugger, some of debugger state
+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.
+B<||>I<dbcmd>          Same as B<|>I<dbcmd> but DB::OUT is temporarilly select()ed as well.
+B<\=> [I<alias> I<value>]      Define a command alias, or list current aliases.
+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: 
-               history, breakpoints and actions, debugger Options 
-               and the following command-line options: -w, -I, -e.
-h [db_command] Get help [on a specific debugger command], enter |h to page.
-h h            Summary of debugger commands.
-q or ^D                Quit. Set \$DB::finished to 0 to debug global destruction.
-
-";
+               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.
+B<h h>         Summary of debugger commands.
+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";
-List/search source lines:               Control script execution:
-  l [ln|sub]  List source code            T           Stack trace
-  - or .      List previous/current line  s [expr]    Single step [in expr]
-  w [line]    List around line            n [expr]    Next, steps over subs
-  f filename  View source in file         <CR>        Repeat last n or s
-  /pattern/ ?patt?   Search forw/backw    r           Return from subroutine
-  v          Show versions of modules    c [ln|sub]  Continue until position
-Debugger controls:                        L           List break pts & actions
-  O [...]     Set debugger options        t [expr]    Toggle trace [trace expr]
-  <[<] or {[{] [cmd]   Do before prompt   b [ln/event] [c]     Set breakpoint
-  >[>] [cmd]  Do after prompt             b sub [c]   Set breakpoint for sub
-  $prc [N|pat]   Redo a previous command     d [line]    Delete a breakpoint
-  H [-num]    Display last num commands   D           Delete all breakpoints
-  = [a val]   Define/list an alias        a [ln] cmd  Do cmd before line
-  h [db_cmd]  Get help on command         A           Delete all actions
-  |[|]dbcmd   Send output to pager        $psh\[$psh\] syscmd Run cmd in a subprocess
-  q or ^D     Quit                       R           Attempt a restart
-Data Examination:            expr     Execute perl code, also see: s,n,t expr
-  x|m expr     Evals expr in array context, dumps the result or lists methods.
-  p expr       Print expression (uses script's current package).
-  S [[!]pat]   List subroutine names [not] matching pattern
-  V [Pk [Vars]]        List Variables in Package.  Vars can be ~pattern or !pattern.
-  X [Vars]     Same as \"V current_package [Vars]\".
+I<List/search source lines:>               I<Control script execution:>
+  B<l> [I<ln>|I<sub>]  List source code            B<T>           Stack trace
+  B<-> or B<.>      List previous/current line  B<s> [I<expr>]    Single step [in expr]
+  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
+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
+  B<$prc> [I<N>|I<pat>]   Redo a previous command     B<d> [I<ln>] or B<D> Delete a/all breakpoints
+  B<H> [I<-num>]    Display last num commands   B<a> [I<ln>] I<cmd>  Do cmd before line
+  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 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 {
+    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 {
@@ -1758,18 +2671,15 @@ sub dbwarn {
   local $doret = -2;
   local $SIG{__WARN__} = '';
   local $SIG{__DIE__} = '';
-  eval { require Carp };       # If error/warning during compilation,
-                                # require may be broken.
-  warn(@_, "\nPossible unrecoverable error"), warn("\nTry to decrease warnLevel `O'ption!\n"), return
-    unless defined &Carp::longmess;
-  #&warn("Entering dbwarn\n");
+  eval { require Carp } if defined $^S;        # If error/warning during compilation,
+                                        # require may be broken.
+  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;
   my $mess = Carp::longmess(@_);
   ($single,$trace) = ($mysingle,$mytrace);
-  #&warn("Warning in dbwarn\n");
   &warn($mess); 
-  #&warn("Exiting dbwarn\n");
 }
 
 sub dbdie {
@@ -1778,28 +2688,33 @@ sub dbdie {
   local $SIG{__DIE__} = '';
   local $SIG{__WARN__} = '';
   my $i = 0; my $ineval = 0; my $sub;
-  #&warn("Entering dbdie\n");
-  if ($dieLevel != 2) {
-    while ((undef,undef,undef,$sub) = caller(++$i)) {
-      $ineval = 1, last if $sub eq '(eval)';
-    }
-    {
+  if ($dieLevel > 2) {
       local $SIG{__WARN__} = \&dbwarn;
-      &warn(@_) if $dieLevel > 2; # Ineval is false during destruction?
-    }
-    #&warn("dieing quietly in dbdie\n") if $ineval and $dieLevel < 2;
-    die @_ if $ineval and $dieLevel < 2;
+      &warn(@_);               # Yell no matter what
+      return;
+  }
+  if ($dieLevel < 2) {
+    die @_ if $^S;             # in eval propagate
   }
-  eval { require Carp };       # If error/warning during compilation,
-                                # require may be broken.
-  die(@_, "\nUnrecoverable error") unless defined &Carp::longmess;
+  # 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);
-  #&warn("dieing loudly in dbdie\n");
   die $mess;
 }
 
@@ -1809,7 +2724,7 @@ sub warnLevel {
     $warnLevel = shift;
     if ($warnLevel) {
       $SIG{__WARN__} = \&DB::dbwarn;
-    } else {
+    } elsif ($prevwarn) {
       $SIG{__WARN__} = $prevwarn;
     }
   }
@@ -1824,9 +2739,10 @@ sub dieLevel {
       $SIG{__DIE__} = \&DB::dbdie; # if $dieLevel < 2;
       #$SIG{__DIE__} = \&DB::diehard if $dieLevel >= 2;
       print $OUT "Stack dump during die enabled", 
-        ( $dieLevel == 1 ? " outside of evals" : ""), ".\n";
+        ( $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";
     }
@@ -1850,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) {
@@ -1878,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.
@@ -1914,19 +2915,20 @@ BEGIN {                 # This does not compile, alas.
   # @stack and $doret are needed in sub sub, which is called for DB::postponed.
   # Triggers bug (?) in perl is we postpone this until runtime:
   @postponed = @stack = (0);
+  $stack_depth = 0;            # Localized $#stack
   $doret = -2;
   $frame = 0;
 }
 
 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)
@@ -2002,19 +3004,30 @@ sub db_complete {
   return $term->filename_list($text); # filenames
 }
 
-sub end_report { print $OUT "Use `q' to quit and `R' to restart. `h q' for details.\n" }
+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;
 
 sub at_exit {
-  "Debuggee terminated. Use `q' to quit and `R' to restart.";
+  "Debugged program terminated.  Use `q' to quit or `R' to restart.";
 }
 
 package DB;                    # Do not trace this 1; below!