xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/DB.pm (revision 0:68f95e015346)
1*0Sstevel@tonic-gate#
2*0Sstevel@tonic-gate# Documentation is at the __END__
3*0Sstevel@tonic-gate#
4*0Sstevel@tonic-gate
5*0Sstevel@tonic-gatepackage DB;
6*0Sstevel@tonic-gate
7*0Sstevel@tonic-gate# "private" globals
8*0Sstevel@tonic-gate
9*0Sstevel@tonic-gatemy ($running, $ready, $deep, $usrctxt, $evalarg,
10*0Sstevel@tonic-gate    @stack, @saved, @skippkg, @clients);
11*0Sstevel@tonic-gatemy $preeval = {};
12*0Sstevel@tonic-gatemy $posteval = {};
13*0Sstevel@tonic-gatemy $ineval = {};
14*0Sstevel@tonic-gate
15*0Sstevel@tonic-gate####
16*0Sstevel@tonic-gate#
17*0Sstevel@tonic-gate# Globals - must be defined at startup so that clients can refer to
18*0Sstevel@tonic-gate# them right after a C<require DB;>
19*0Sstevel@tonic-gate#
20*0Sstevel@tonic-gate####
21*0Sstevel@tonic-gate
22*0Sstevel@tonic-gateBEGIN {
23*0Sstevel@tonic-gate
24*0Sstevel@tonic-gate  # these are hardcoded in perl source (some are magical)
25*0Sstevel@tonic-gate
26*0Sstevel@tonic-gate  $DB::sub = '';        # name of current subroutine
27*0Sstevel@tonic-gate  %DB::sub = ();        # "filename:fromline-toline" for every known sub
28*0Sstevel@tonic-gate  $DB::single = 0;      # single-step flag (set it to 1 to enable stops in BEGIN/use)
29*0Sstevel@tonic-gate  $DB::signal = 0;      # signal flag (will cause a stop at the next line)
30*0Sstevel@tonic-gate  $DB::trace = 0;       # are we tracing through subroutine calls?
31*0Sstevel@tonic-gate  @DB::args = ();       # arguments of current subroutine or @ARGV array
32*0Sstevel@tonic-gate  @DB::dbline = ();     # list of lines in currently loaded file
33*0Sstevel@tonic-gate  %DB::dbline = ();     # actions in current file (keyed by line number)
34*0Sstevel@tonic-gate  @DB::ret = ();        # return value of last sub executed in list context
35*0Sstevel@tonic-gate  $DB::ret = '';        # return value of last sub executed in scalar context
36*0Sstevel@tonic-gate
37*0Sstevel@tonic-gate  # other "public" globals
38*0Sstevel@tonic-gate
39*0Sstevel@tonic-gate  $DB::package = '';    # current package space
40*0Sstevel@tonic-gate  $DB::filename = '';   # current filename
41*0Sstevel@tonic-gate  $DB::subname = '';    # currently executing sub (fullly qualified name)
42*0Sstevel@tonic-gate  $DB::lineno = '';     # current line number
43*0Sstevel@tonic-gate
44*0Sstevel@tonic-gate  $DB::VERSION = $DB::VERSION = '1.0';
45*0Sstevel@tonic-gate
46*0Sstevel@tonic-gate  # initialize private globals to avoid warnings
47*0Sstevel@tonic-gate
48*0Sstevel@tonic-gate  $running = 1;         # are we running, or are we stopped?
49*0Sstevel@tonic-gate  @stack = (0);
50*0Sstevel@tonic-gate  @clients = ();
51*0Sstevel@tonic-gate  $deep = 100;
52*0Sstevel@tonic-gate  $ready = 0;
53*0Sstevel@tonic-gate  @saved = ();
54*0Sstevel@tonic-gate  @skippkg = ();
55*0Sstevel@tonic-gate  $usrctxt = '';
56*0Sstevel@tonic-gate  $evalarg = '';
57*0Sstevel@tonic-gate}
58*0Sstevel@tonic-gate
59*0Sstevel@tonic-gate####
60*0Sstevel@tonic-gate# entry point for all subroutine calls
61*0Sstevel@tonic-gate#
62*0Sstevel@tonic-gatesub sub {
63*0Sstevel@tonic-gate  push(@stack, $DB::single);
64*0Sstevel@tonic-gate  $DB::single &= 1;
65*0Sstevel@tonic-gate  $DB::single |= 4 if $#stack == $deep;
66*0Sstevel@tonic-gate#  print $DB::sub, "\n";
67*0Sstevel@tonic-gate  if ($DB::sub =~ /(?:^|::)DESTROY$/ or not defined wantarray) {
68*0Sstevel@tonic-gate    &$DB::sub;
69*0Sstevel@tonic-gate    $DB::single |= pop(@stack);
70*0Sstevel@tonic-gate    $DB::ret = undef;
71*0Sstevel@tonic-gate  }
72*0Sstevel@tonic-gate  elsif (wantarray) {
73*0Sstevel@tonic-gate    @DB::ret = &$DB::sub;
74*0Sstevel@tonic-gate    $DB::single |= pop(@stack);
75*0Sstevel@tonic-gate    @DB::ret;
76*0Sstevel@tonic-gate  }
77*0Sstevel@tonic-gate  else {
78*0Sstevel@tonic-gate    $DB::ret = &$DB::sub;
79*0Sstevel@tonic-gate    $DB::single |= pop(@stack);
80*0Sstevel@tonic-gate    $DB::ret;
81*0Sstevel@tonic-gate  }
82*0Sstevel@tonic-gate}
83*0Sstevel@tonic-gate
84*0Sstevel@tonic-gate####
85*0Sstevel@tonic-gate# this is called by perl for every statement
86*0Sstevel@tonic-gate#
87*0Sstevel@tonic-gatesub DB {
88*0Sstevel@tonic-gate  return unless $ready;
89*0Sstevel@tonic-gate  &save;
90*0Sstevel@tonic-gate  ($DB::package, $DB::filename, $DB::lineno) = caller;
91*0Sstevel@tonic-gate
92*0Sstevel@tonic-gate  return if @skippkg and grep { $_ eq $DB::package } @skippkg;
93*0Sstevel@tonic-gate
94*0Sstevel@tonic-gate  $usrctxt = "package $DB::package;";		# this won't let them modify, alas
95*0Sstevel@tonic-gate  local(*DB::dbline) = "::_<$DB::filename";
96*0Sstevel@tonic-gate
97*0Sstevel@tonic-gate  # we need to check for pseudofiles on Mac OS (these are files
98*0Sstevel@tonic-gate  # not attached to a filename, but instead stored in Dev:Pseudo)
99*0Sstevel@tonic-gate  # since this is done late, $DB::filename will be "wrong" after
100*0Sstevel@tonic-gate  # skippkg
101*0Sstevel@tonic-gate  if ($^O eq 'MacOS' && $#DB::dbline < 0) {
102*0Sstevel@tonic-gate    $DB::filename = 'Dev:Pseudo';
103*0Sstevel@tonic-gate    *DB::dbline = "::_<$DB::filename";
104*0Sstevel@tonic-gate  }
105*0Sstevel@tonic-gate
106*0Sstevel@tonic-gate  my ($stop, $action);
107*0Sstevel@tonic-gate  if (($stop,$action) = split(/\0/,$DB::dbline{$DB::lineno})) {
108*0Sstevel@tonic-gate    if ($stop eq '1') {
109*0Sstevel@tonic-gate      $DB::signal |= 1;
110*0Sstevel@tonic-gate    }
111*0Sstevel@tonic-gate    else {
112*0Sstevel@tonic-gate      $stop = 0 unless $stop;			# avoid un_init warning
113*0Sstevel@tonic-gate      $evalarg = "\$DB::signal |= do { $stop; }"; &eval;
114*0Sstevel@tonic-gate      $DB::dbline{$DB::lineno} =~ s/;9($|\0)/$1/;    # clear any temp breakpt
115*0Sstevel@tonic-gate    }
116*0Sstevel@tonic-gate  }
117*0Sstevel@tonic-gate  if ($DB::single || $DB::trace || $DB::signal) {
118*0Sstevel@tonic-gate    $DB::subname = ($DB::sub =~ /\'|::/) ? $DB::sub : "${DB::package}::$DB::sub"; #';
119*0Sstevel@tonic-gate    DB->loadfile($DB::filename, $DB::lineno);
120*0Sstevel@tonic-gate  }
121*0Sstevel@tonic-gate  $evalarg = $action, &eval if $action;
122*0Sstevel@tonic-gate  if ($DB::single || $DB::signal) {
123*0Sstevel@tonic-gate    _outputall($#stack . " levels deep in subroutine calls.\n") if $DB::single & 4;
124*0Sstevel@tonic-gate    $DB::single = 0;
125*0Sstevel@tonic-gate    $DB::signal = 0;
126*0Sstevel@tonic-gate    $running = 0;
127*0Sstevel@tonic-gate
128*0Sstevel@tonic-gate    &eval if ($evalarg = DB->prestop);
129*0Sstevel@tonic-gate    my $c;
130*0Sstevel@tonic-gate    for $c (@clients) {
131*0Sstevel@tonic-gate      # perform any client-specific prestop actions
132*0Sstevel@tonic-gate      &eval if ($evalarg = $c->cprestop);
133*0Sstevel@tonic-gate
134*0Sstevel@tonic-gate      # Now sit in an event loop until something sets $running
135*0Sstevel@tonic-gate      do {
136*0Sstevel@tonic-gate	$c->idle;                     # call client event loop; must not block
137*0Sstevel@tonic-gate	if ($running == 2) {          # client wants something eval-ed
138*0Sstevel@tonic-gate	  &eval if ($evalarg = $c->evalcode);
139*0Sstevel@tonic-gate	  $running = 0;
140*0Sstevel@tonic-gate	}
141*0Sstevel@tonic-gate      } until $running;
142*0Sstevel@tonic-gate
143*0Sstevel@tonic-gate      # perform any client-specific poststop actions
144*0Sstevel@tonic-gate      &eval if ($evalarg = $c->cpoststop);
145*0Sstevel@tonic-gate    }
146*0Sstevel@tonic-gate    &eval if ($evalarg = DB->poststop);
147*0Sstevel@tonic-gate  }
148*0Sstevel@tonic-gate  ($@, $!, $,, $/, $\, $^W) = @saved;
149*0Sstevel@tonic-gate  ();
150*0Sstevel@tonic-gate}
151*0Sstevel@tonic-gate
152*0Sstevel@tonic-gate####
153*0Sstevel@tonic-gate# this takes its argument via $evalarg to preserve current @_
154*0Sstevel@tonic-gate#
155*0Sstevel@tonic-gatesub eval {
156*0Sstevel@tonic-gate  ($@, $!, $,, $/, $\, $^W) = @saved;
157*0Sstevel@tonic-gate  eval "$usrctxt $evalarg; &DB::save";
158*0Sstevel@tonic-gate  _outputall($@) if $@;
159*0Sstevel@tonic-gate}
160*0Sstevel@tonic-gate
161*0Sstevel@tonic-gate###############################################################################
162*0Sstevel@tonic-gate#         no compile-time subroutine call allowed before this point           #
163*0Sstevel@tonic-gate###############################################################################
164*0Sstevel@tonic-gate
165*0Sstevel@tonic-gateuse strict;                # this can run only after DB() and sub() are defined
166*0Sstevel@tonic-gate
167*0Sstevel@tonic-gatesub save {
168*0Sstevel@tonic-gate  @saved = ($@, $!, $,, $/, $\, $^W);
169*0Sstevel@tonic-gate  $, = ""; $/ = "\n"; $\ = ""; $^W = 0;
170*0Sstevel@tonic-gate}
171*0Sstevel@tonic-gate
172*0Sstevel@tonic-gatesub catch {
173*0Sstevel@tonic-gate  for (@clients) { $_->awaken; }
174*0Sstevel@tonic-gate  $DB::signal = 1;
175*0Sstevel@tonic-gate  $ready = 1;
176*0Sstevel@tonic-gate}
177*0Sstevel@tonic-gate
178*0Sstevel@tonic-gate####
179*0Sstevel@tonic-gate#
180*0Sstevel@tonic-gate# Client callable (read inheritable) methods defined after this point
181*0Sstevel@tonic-gate#
182*0Sstevel@tonic-gate####
183*0Sstevel@tonic-gate
184*0Sstevel@tonic-gatesub register {
185*0Sstevel@tonic-gate  my $s = shift;
186*0Sstevel@tonic-gate  $s = _clientname($s) if ref($s);
187*0Sstevel@tonic-gate  push @clients, $s;
188*0Sstevel@tonic-gate}
189*0Sstevel@tonic-gate
190*0Sstevel@tonic-gatesub done {
191*0Sstevel@tonic-gate  my $s = shift;
192*0Sstevel@tonic-gate  $s = _clientname($s) if ref($s);
193*0Sstevel@tonic-gate  @clients = grep {$_ ne $s} @clients;
194*0Sstevel@tonic-gate  $s->cleanup;
195*0Sstevel@tonic-gate#  $running = 3 unless @clients;
196*0Sstevel@tonic-gate  exit(0) unless @clients;
197*0Sstevel@tonic-gate}
198*0Sstevel@tonic-gate
199*0Sstevel@tonic-gatesub _clientname {
200*0Sstevel@tonic-gate  my $name = shift;
201*0Sstevel@tonic-gate  "$name" =~ /^(.+)=[A-Z]+\(.+\)$/;
202*0Sstevel@tonic-gate  return $1;
203*0Sstevel@tonic-gate}
204*0Sstevel@tonic-gate
205*0Sstevel@tonic-gatesub next {
206*0Sstevel@tonic-gate  my $s = shift;
207*0Sstevel@tonic-gate  $DB::single = 2;
208*0Sstevel@tonic-gate  $running = 1;
209*0Sstevel@tonic-gate}
210*0Sstevel@tonic-gate
211*0Sstevel@tonic-gatesub step {
212*0Sstevel@tonic-gate  my $s = shift;
213*0Sstevel@tonic-gate  $DB::single = 1;
214*0Sstevel@tonic-gate  $running = 1;
215*0Sstevel@tonic-gate}
216*0Sstevel@tonic-gate
217*0Sstevel@tonic-gatesub cont {
218*0Sstevel@tonic-gate  my $s = shift;
219*0Sstevel@tonic-gate  my $i = shift;
220*0Sstevel@tonic-gate  $s->set_tbreak($i) if $i;
221*0Sstevel@tonic-gate  for ($i = 0; $i <= $#stack;) {
222*0Sstevel@tonic-gate	$stack[$i++] &= ~1;
223*0Sstevel@tonic-gate  }
224*0Sstevel@tonic-gate  $DB::single = 0;
225*0Sstevel@tonic-gate  $running = 1;
226*0Sstevel@tonic-gate}
227*0Sstevel@tonic-gate
228*0Sstevel@tonic-gate####
229*0Sstevel@tonic-gate# XXX caller must experimentally determine $i (since it depends
230*0Sstevel@tonic-gate# on how many client call frames are between this call and the DB call).
231*0Sstevel@tonic-gate# Such is life.
232*0Sstevel@tonic-gate#
233*0Sstevel@tonic-gatesub ret {
234*0Sstevel@tonic-gate  my $s = shift;
235*0Sstevel@tonic-gate  my $i = shift;      # how many levels to get to DB sub
236*0Sstevel@tonic-gate  $i = 0 unless defined $i;
237*0Sstevel@tonic-gate  $stack[$#stack-$i] |= 1;
238*0Sstevel@tonic-gate  $DB::single = 0;
239*0Sstevel@tonic-gate  $running = 1;
240*0Sstevel@tonic-gate}
241*0Sstevel@tonic-gate
242*0Sstevel@tonic-gate####
243*0Sstevel@tonic-gate# XXX caller must experimentally determine $start (since it depends
244*0Sstevel@tonic-gate# on how many client call frames are between this call and the DB call).
245*0Sstevel@tonic-gate# Such is life.
246*0Sstevel@tonic-gate#
247*0Sstevel@tonic-gatesub backtrace {
248*0Sstevel@tonic-gate  my $self = shift;
249*0Sstevel@tonic-gate  my $start = shift;
250*0Sstevel@tonic-gate  my($p,$f,$l,$s,$h,$w,$e,$r,$a, @a, @ret,$i);
251*0Sstevel@tonic-gate  $start = 1 unless $start;
252*0Sstevel@tonic-gate  for ($i = $start; ($p,$f,$l,$s,$h,$w,$e,$r) = caller($i); $i++) {
253*0Sstevel@tonic-gate    @a = @DB::args;
254*0Sstevel@tonic-gate    for (@a) {
255*0Sstevel@tonic-gate      s/'/\\'/g;
256*0Sstevel@tonic-gate      s/([^\0]*)/'$1'/ unless /^-?[\d.]+$/;
257*0Sstevel@tonic-gate      s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg;
258*0Sstevel@tonic-gate      s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg;
259*0Sstevel@tonic-gate    }
260*0Sstevel@tonic-gate    $w = $w ? '@ = ' : '$ = ';
261*0Sstevel@tonic-gate    $a = $h ? '(' . join(', ', @a) . ')' : '';
262*0Sstevel@tonic-gate    $e =~ s/\n\s*\;\s*\Z// if $e;
263*0Sstevel@tonic-gate    $e =~ s/[\\\']/\\$1/g if $e;
264*0Sstevel@tonic-gate    if ($r) {
265*0Sstevel@tonic-gate      $s = "require '$e'";
266*0Sstevel@tonic-gate    } elsif (defined $r) {
267*0Sstevel@tonic-gate      $s = "eval '$e'";
268*0Sstevel@tonic-gate    } elsif ($s eq '(eval)') {
269*0Sstevel@tonic-gate      $s = "eval {...}";
270*0Sstevel@tonic-gate    }
271*0Sstevel@tonic-gate    $f = "file `$f'" unless $f eq '-e';
272*0Sstevel@tonic-gate    push @ret, "$w&$s$a from $f line $l";
273*0Sstevel@tonic-gate    last if $DB::signal;
274*0Sstevel@tonic-gate  }
275*0Sstevel@tonic-gate  return @ret;
276*0Sstevel@tonic-gate}
277*0Sstevel@tonic-gate
278*0Sstevel@tonic-gatesub _outputall {
279*0Sstevel@tonic-gate  my $c;
280*0Sstevel@tonic-gate  for $c (@clients) {
281*0Sstevel@tonic-gate    $c->output(@_);
282*0Sstevel@tonic-gate  }
283*0Sstevel@tonic-gate}
284*0Sstevel@tonic-gate
285*0Sstevel@tonic-gatesub trace_toggle {
286*0Sstevel@tonic-gate  my $s = shift;
287*0Sstevel@tonic-gate  $DB::trace = !$DB::trace;
288*0Sstevel@tonic-gate}
289*0Sstevel@tonic-gate
290*0Sstevel@tonic-gate
291*0Sstevel@tonic-gate####
292*0Sstevel@tonic-gate# without args: returns all defined subroutine names
293*0Sstevel@tonic-gate# with subname args: returns a listref [file, start, end]
294*0Sstevel@tonic-gate#
295*0Sstevel@tonic-gatesub subs {
296*0Sstevel@tonic-gate  my $s = shift;
297*0Sstevel@tonic-gate  if (@_) {
298*0Sstevel@tonic-gate    my(@ret) = ();
299*0Sstevel@tonic-gate    while (@_) {
300*0Sstevel@tonic-gate      my $name = shift;
301*0Sstevel@tonic-gate      push @ret, [$DB::sub{$name} =~ /^(.*)\:(\d+)-(\d+)$/]
302*0Sstevel@tonic-gate	if exists $DB::sub{$name};
303*0Sstevel@tonic-gate    }
304*0Sstevel@tonic-gate    return @ret;
305*0Sstevel@tonic-gate  }
306*0Sstevel@tonic-gate  return keys %DB::sub;
307*0Sstevel@tonic-gate}
308*0Sstevel@tonic-gate
309*0Sstevel@tonic-gate####
310*0Sstevel@tonic-gate# first argument is a filename whose subs will be returned
311*0Sstevel@tonic-gate# if a filename is not supplied, all subs in the current
312*0Sstevel@tonic-gate# filename are returned.
313*0Sstevel@tonic-gate#
314*0Sstevel@tonic-gatesub filesubs {
315*0Sstevel@tonic-gate  my $s = shift;
316*0Sstevel@tonic-gate  my $fname = shift;
317*0Sstevel@tonic-gate  $fname = $DB::filename unless $fname;
318*0Sstevel@tonic-gate  return grep { $DB::sub{$_} =~ /^$fname/ } keys %DB::sub;
319*0Sstevel@tonic-gate}
320*0Sstevel@tonic-gate
321*0Sstevel@tonic-gate####
322*0Sstevel@tonic-gate# returns a list of all filenames that DB knows about
323*0Sstevel@tonic-gate#
324*0Sstevel@tonic-gatesub files {
325*0Sstevel@tonic-gate  my $s = shift;
326*0Sstevel@tonic-gate  my(@f) = grep(m|^_<|, keys %main::);
327*0Sstevel@tonic-gate  return map { substr($_,2) } @f;
328*0Sstevel@tonic-gate}
329*0Sstevel@tonic-gate
330*0Sstevel@tonic-gate####
331*0Sstevel@tonic-gate# returns reference to an array holding the lines in currently
332*0Sstevel@tonic-gate# loaded file
333*0Sstevel@tonic-gate#
334*0Sstevel@tonic-gatesub lines {
335*0Sstevel@tonic-gate  my $s = shift;
336*0Sstevel@tonic-gate  return \@DB::dbline;
337*0Sstevel@tonic-gate}
338*0Sstevel@tonic-gate
339*0Sstevel@tonic-gate####
340*0Sstevel@tonic-gate# loadfile($file, $line)
341*0Sstevel@tonic-gate#
342*0Sstevel@tonic-gatesub loadfile {
343*0Sstevel@tonic-gate  my $s = shift;
344*0Sstevel@tonic-gate  my($file, $line) = @_;
345*0Sstevel@tonic-gate  if (!defined $main::{'_<' . $file}) {
346*0Sstevel@tonic-gate    my $try;
347*0Sstevel@tonic-gate    if (($try) = grep(m|^_<.*$file|, keys %main::)) {
348*0Sstevel@tonic-gate      $file = substr($try,2);
349*0Sstevel@tonic-gate    }
350*0Sstevel@tonic-gate  }
351*0Sstevel@tonic-gate  if (defined($main::{'_<' . $file})) {
352*0Sstevel@tonic-gate    my $c;
353*0Sstevel@tonic-gate#    _outputall("Loading file $file..");
354*0Sstevel@tonic-gate    *DB::dbline = "::_<$file";
355*0Sstevel@tonic-gate    $DB::filename = $file;
356*0Sstevel@tonic-gate    for $c (@clients) {
357*0Sstevel@tonic-gate#      print "2 ", $file, '|', $line, "\n";
358*0Sstevel@tonic-gate      $c->showfile($file, $line);
359*0Sstevel@tonic-gate    }
360*0Sstevel@tonic-gate    return $file;
361*0Sstevel@tonic-gate  }
362*0Sstevel@tonic-gate  return undef;
363*0Sstevel@tonic-gate}
364*0Sstevel@tonic-gate
365*0Sstevel@tonic-gatesub lineevents {
366*0Sstevel@tonic-gate  my $s = shift;
367*0Sstevel@tonic-gate  my $fname = shift;
368*0Sstevel@tonic-gate  my(%ret) = ();
369*0Sstevel@tonic-gate  my $i;
370*0Sstevel@tonic-gate  $fname = $DB::filename unless $fname;
371*0Sstevel@tonic-gate  local(*DB::dbline) = "::_<$fname";
372*0Sstevel@tonic-gate  for ($i = 1; $i <= $#DB::dbline; $i++) {
373*0Sstevel@tonic-gate    $ret{$i} = [$DB::dbline[$i], split(/\0/, $DB::dbline{$i})]
374*0Sstevel@tonic-gate      if defined $DB::dbline{$i};
375*0Sstevel@tonic-gate  }
376*0Sstevel@tonic-gate  return %ret;
377*0Sstevel@tonic-gate}
378*0Sstevel@tonic-gate
379*0Sstevel@tonic-gatesub set_break {
380*0Sstevel@tonic-gate  my $s = shift;
381*0Sstevel@tonic-gate  my $i = shift;
382*0Sstevel@tonic-gate  my $cond = shift;
383*0Sstevel@tonic-gate  $i ||= $DB::lineno;
384*0Sstevel@tonic-gate  $cond ||= '1';
385*0Sstevel@tonic-gate  $i = _find_subline($i) if ($i =~ /\D/);
386*0Sstevel@tonic-gate  $s->output("Subroutine not found.\n") unless $i;
387*0Sstevel@tonic-gate  if ($i) {
388*0Sstevel@tonic-gate    if ($DB::dbline[$i] == 0) {
389*0Sstevel@tonic-gate      $s->output("Line $i not breakable.\n");
390*0Sstevel@tonic-gate    }
391*0Sstevel@tonic-gate    else {
392*0Sstevel@tonic-gate      $DB::dbline{$i} =~ s/^[^\0]*/$cond/;
393*0Sstevel@tonic-gate    }
394*0Sstevel@tonic-gate  }
395*0Sstevel@tonic-gate}
396*0Sstevel@tonic-gate
397*0Sstevel@tonic-gatesub set_tbreak {
398*0Sstevel@tonic-gate  my $s = shift;
399*0Sstevel@tonic-gate  my $i = shift;
400*0Sstevel@tonic-gate  $i = _find_subline($i) if ($i =~ /\D/);
401*0Sstevel@tonic-gate  $s->output("Subroutine not found.\n") unless $i;
402*0Sstevel@tonic-gate  if ($i) {
403*0Sstevel@tonic-gate    if ($DB::dbline[$i] == 0) {
404*0Sstevel@tonic-gate      $s->output("Line $i not breakable.\n");
405*0Sstevel@tonic-gate    }
406*0Sstevel@tonic-gate    else {
407*0Sstevel@tonic-gate      $DB::dbline{$i} =~ s/($|\0)/;9$1/; # add one-time-only b.p.
408*0Sstevel@tonic-gate    }
409*0Sstevel@tonic-gate  }
410*0Sstevel@tonic-gate}
411*0Sstevel@tonic-gate
412*0Sstevel@tonic-gatesub _find_subline {
413*0Sstevel@tonic-gate  my $name = shift;
414*0Sstevel@tonic-gate  $name =~ s/\'/::/;
415*0Sstevel@tonic-gate  $name = "${DB::package}\:\:" . $name if $name !~ /::/;
416*0Sstevel@tonic-gate  $name = "main" . $name if substr($name,0,2) eq "::";
417*0Sstevel@tonic-gate  my($fname, $from, $to) = ($DB::sub{$name} =~ /^(.*):(\d+)-(\d+)$/);
418*0Sstevel@tonic-gate  if ($from) {
419*0Sstevel@tonic-gate    local *DB::dbline = "::_<$fname";
420*0Sstevel@tonic-gate    ++$from while $DB::dbline[$from] == 0 && $from < $to;
421*0Sstevel@tonic-gate    return $from;
422*0Sstevel@tonic-gate  }
423*0Sstevel@tonic-gate  return undef;
424*0Sstevel@tonic-gate}
425*0Sstevel@tonic-gate
426*0Sstevel@tonic-gatesub clr_breaks {
427*0Sstevel@tonic-gate  my $s = shift;
428*0Sstevel@tonic-gate  my $i;
429*0Sstevel@tonic-gate  if (@_) {
430*0Sstevel@tonic-gate    while (@_) {
431*0Sstevel@tonic-gate      $i = shift;
432*0Sstevel@tonic-gate      $i = _find_subline($i) if ($i =~ /\D/);
433*0Sstevel@tonic-gate      $s->output("Subroutine not found.\n") unless $i;
434*0Sstevel@tonic-gate      if (defined $DB::dbline{$i}) {
435*0Sstevel@tonic-gate        $DB::dbline{$i} =~ s/^[^\0]+//;
436*0Sstevel@tonic-gate        if ($DB::dbline{$i} =~ s/^\0?$//) {
437*0Sstevel@tonic-gate          delete $DB::dbline{$i};
438*0Sstevel@tonic-gate        }
439*0Sstevel@tonic-gate      }
440*0Sstevel@tonic-gate    }
441*0Sstevel@tonic-gate  }
442*0Sstevel@tonic-gate  else {
443*0Sstevel@tonic-gate    for ($i = 1; $i <= $#DB::dbline ; $i++) {
444*0Sstevel@tonic-gate      if (defined $DB::dbline{$i}) {
445*0Sstevel@tonic-gate        $DB::dbline{$i} =~ s/^[^\0]+//;
446*0Sstevel@tonic-gate        if ($DB::dbline{$i} =~ s/^\0?$//) {
447*0Sstevel@tonic-gate          delete $DB::dbline{$i};
448*0Sstevel@tonic-gate        }
449*0Sstevel@tonic-gate      }
450*0Sstevel@tonic-gate    }
451*0Sstevel@tonic-gate  }
452*0Sstevel@tonic-gate}
453*0Sstevel@tonic-gate
454*0Sstevel@tonic-gatesub set_action {
455*0Sstevel@tonic-gate  my $s = shift;
456*0Sstevel@tonic-gate  my $i = shift;
457*0Sstevel@tonic-gate  my $act = shift;
458*0Sstevel@tonic-gate  $i = _find_subline($i) if ($i =~ /\D/);
459*0Sstevel@tonic-gate  $s->output("Subroutine not found.\n") unless $i;
460*0Sstevel@tonic-gate  if ($i) {
461*0Sstevel@tonic-gate    if ($DB::dbline[$i] == 0) {
462*0Sstevel@tonic-gate      $s->output("Line $i not actionable.\n");
463*0Sstevel@tonic-gate    }
464*0Sstevel@tonic-gate    else {
465*0Sstevel@tonic-gate      $DB::dbline{$i} =~ s/\0[^\0]*//;
466*0Sstevel@tonic-gate      $DB::dbline{$i} .= "\0" . $act;
467*0Sstevel@tonic-gate    }
468*0Sstevel@tonic-gate  }
469*0Sstevel@tonic-gate}
470*0Sstevel@tonic-gate
471*0Sstevel@tonic-gatesub clr_actions {
472*0Sstevel@tonic-gate  my $s = shift;
473*0Sstevel@tonic-gate  my $i;
474*0Sstevel@tonic-gate  if (@_) {
475*0Sstevel@tonic-gate    while (@_) {
476*0Sstevel@tonic-gate      my $i = shift;
477*0Sstevel@tonic-gate      $i = _find_subline($i) if ($i =~ /\D/);
478*0Sstevel@tonic-gate      $s->output("Subroutine not found.\n") unless $i;
479*0Sstevel@tonic-gate      if ($i && $DB::dbline[$i] != 0) {
480*0Sstevel@tonic-gate	$DB::dbline{$i} =~ s/\0[^\0]*//;
481*0Sstevel@tonic-gate	delete $DB::dbline{$i} if $DB::dbline{$i} =~ s/^\0?$//;
482*0Sstevel@tonic-gate      }
483*0Sstevel@tonic-gate    }
484*0Sstevel@tonic-gate  }
485*0Sstevel@tonic-gate  else {
486*0Sstevel@tonic-gate    for ($i = 1; $i <= $#DB::dbline ; $i++) {
487*0Sstevel@tonic-gate      if (defined $DB::dbline{$i}) {
488*0Sstevel@tonic-gate	$DB::dbline{$i} =~ s/\0[^\0]*//;
489*0Sstevel@tonic-gate	delete $DB::dbline{$i} if $DB::dbline{$i} =~ s/^\0?$//;
490*0Sstevel@tonic-gate      }
491*0Sstevel@tonic-gate    }
492*0Sstevel@tonic-gate  }
493*0Sstevel@tonic-gate}
494*0Sstevel@tonic-gate
495*0Sstevel@tonic-gatesub prestop {
496*0Sstevel@tonic-gate  my ($client, $val) = @_;
497*0Sstevel@tonic-gate  return defined($val) ? $preeval->{$client} = $val : $preeval->{$client};
498*0Sstevel@tonic-gate}
499*0Sstevel@tonic-gate
500*0Sstevel@tonic-gatesub poststop {
501*0Sstevel@tonic-gate  my ($client, $val) = @_;
502*0Sstevel@tonic-gate  return defined($val) ? $posteval->{$client} = $val : $posteval->{$client};
503*0Sstevel@tonic-gate}
504*0Sstevel@tonic-gate
505*0Sstevel@tonic-gate#
506*0Sstevel@tonic-gate# "pure virtual" methods
507*0Sstevel@tonic-gate#
508*0Sstevel@tonic-gate
509*0Sstevel@tonic-gate# client-specific pre/post-stop actions.
510*0Sstevel@tonic-gatesub cprestop {}
511*0Sstevel@tonic-gatesub cpoststop {}
512*0Sstevel@tonic-gate
513*0Sstevel@tonic-gate# client complete startup
514*0Sstevel@tonic-gatesub awaken {}
515*0Sstevel@tonic-gate
516*0Sstevel@tonic-gatesub skippkg {
517*0Sstevel@tonic-gate  my $s = shift;
518*0Sstevel@tonic-gate  push @skippkg, @_ if @_;
519*0Sstevel@tonic-gate}
520*0Sstevel@tonic-gate
521*0Sstevel@tonic-gatesub evalcode {
522*0Sstevel@tonic-gate  my ($client, $val) = @_;
523*0Sstevel@tonic-gate  if (defined $val) {
524*0Sstevel@tonic-gate    $running = 2;    # hand over to DB() to evaluate in its context
525*0Sstevel@tonic-gate    $ineval->{$client} = $val;
526*0Sstevel@tonic-gate  }
527*0Sstevel@tonic-gate  return $ineval->{$client};
528*0Sstevel@tonic-gate}
529*0Sstevel@tonic-gate
530*0Sstevel@tonic-gatesub ready {
531*0Sstevel@tonic-gate  my $s = shift;
532*0Sstevel@tonic-gate  return $ready = 1;
533*0Sstevel@tonic-gate}
534*0Sstevel@tonic-gate
535*0Sstevel@tonic-gate# stubs
536*0Sstevel@tonic-gate
537*0Sstevel@tonic-gatesub init {}
538*0Sstevel@tonic-gatesub stop {}
539*0Sstevel@tonic-gatesub idle {}
540*0Sstevel@tonic-gatesub cleanup {}
541*0Sstevel@tonic-gatesub output {}
542*0Sstevel@tonic-gate
543*0Sstevel@tonic-gate#
544*0Sstevel@tonic-gate# client init
545*0Sstevel@tonic-gate#
546*0Sstevel@tonic-gatefor (@clients) { $_->init }
547*0Sstevel@tonic-gate
548*0Sstevel@tonic-gate$SIG{'INT'} = \&DB::catch;
549*0Sstevel@tonic-gate
550*0Sstevel@tonic-gate# disable this if stepping through END blocks is desired
551*0Sstevel@tonic-gate# (looks scary and deconstructivist with Swat)
552*0Sstevel@tonic-gateEND { $ready = 0 }
553*0Sstevel@tonic-gate
554*0Sstevel@tonic-gate1;
555*0Sstevel@tonic-gate__END__
556*0Sstevel@tonic-gate
557*0Sstevel@tonic-gate=head1 NAME
558*0Sstevel@tonic-gate
559*0Sstevel@tonic-gateDB - programmatic interface to the Perl debugging API (draft, subject to
560*0Sstevel@tonic-gatechange)
561*0Sstevel@tonic-gate
562*0Sstevel@tonic-gate=head1 SYNOPSIS
563*0Sstevel@tonic-gate
564*0Sstevel@tonic-gate    package CLIENT;
565*0Sstevel@tonic-gate    use DB;
566*0Sstevel@tonic-gate    @ISA = qw(DB);
567*0Sstevel@tonic-gate
568*0Sstevel@tonic-gate    # these (inherited) methods can be called by the client
569*0Sstevel@tonic-gate
570*0Sstevel@tonic-gate    CLIENT->register()      # register a client package name
571*0Sstevel@tonic-gate    CLIENT->done()          # de-register from the debugging API
572*0Sstevel@tonic-gate    CLIENT->skippkg('hide::hide')  # ask DB not to stop in this package
573*0Sstevel@tonic-gate    CLIENT->cont([WHERE])       # run some more (until BREAK or another breakpt)
574*0Sstevel@tonic-gate    CLIENT->step()              # single step
575*0Sstevel@tonic-gate    CLIENT->next()              # step over
576*0Sstevel@tonic-gate    CLIENT->ret()               # return from current subroutine
577*0Sstevel@tonic-gate    CLIENT->backtrace()         # return the call stack description
578*0Sstevel@tonic-gate    CLIENT->ready()             # call when client setup is done
579*0Sstevel@tonic-gate    CLIENT->trace_toggle()      # toggle subroutine call trace mode
580*0Sstevel@tonic-gate    CLIENT->subs([SUBS])        # return subroutine information
581*0Sstevel@tonic-gate    CLIENT->files()             # return list of all files known to DB
582*0Sstevel@tonic-gate    CLIENT->lines()             # return lines in currently loaded file
583*0Sstevel@tonic-gate    CLIENT->loadfile(FILE,LINE) # load a file and let other clients know
584*0Sstevel@tonic-gate    CLIENT->lineevents()        # return info on lines with actions
585*0Sstevel@tonic-gate    CLIENT->set_break([WHERE],[COND])
586*0Sstevel@tonic-gate    CLIENT->set_tbreak([WHERE])
587*0Sstevel@tonic-gate    CLIENT->clr_breaks([LIST])
588*0Sstevel@tonic-gate    CLIENT->set_action(WHERE,ACTION)
589*0Sstevel@tonic-gate    CLIENT->clr_actions([LIST])
590*0Sstevel@tonic-gate    CLIENT->evalcode(STRING)  # eval STRING in executing code's context
591*0Sstevel@tonic-gate    CLIENT->prestop([STRING]) # execute in code context before stopping
592*0Sstevel@tonic-gate    CLIENT->poststop([STRING])# execute in code context before resuming
593*0Sstevel@tonic-gate
594*0Sstevel@tonic-gate    # These methods will be called at the appropriate times.
595*0Sstevel@tonic-gate    # Stub versions provided do nothing.
596*0Sstevel@tonic-gate    # None of these can block.
597*0Sstevel@tonic-gate
598*0Sstevel@tonic-gate    CLIENT->init()          # called when debug API inits itself
599*0Sstevel@tonic-gate    CLIENT->stop(FILE,LINE) # when execution stops
600*0Sstevel@tonic-gate    CLIENT->idle()          # while stopped (can be a client event loop)
601*0Sstevel@tonic-gate    CLIENT->cleanup()       # just before exit
602*0Sstevel@tonic-gate    CLIENT->output(LIST)    # called to print any output that API must show
603*0Sstevel@tonic-gate
604*0Sstevel@tonic-gate=head1 DESCRIPTION
605*0Sstevel@tonic-gate
606*0Sstevel@tonic-gatePerl debug information is frequently required not just by debuggers,
607*0Sstevel@tonic-gatebut also by modules that need some "special" information to do their
608*0Sstevel@tonic-gatejob properly, like profilers.
609*0Sstevel@tonic-gate
610*0Sstevel@tonic-gateThis module abstracts and provides all of the hooks into Perl internal
611*0Sstevel@tonic-gatedebugging functionality, so that various implementations of Perl debuggers
612*0Sstevel@tonic-gate(or packages that want to simply get at the "privileged" debugging data)
613*0Sstevel@tonic-gatecan all benefit from the development of this common code.  Currently used
614*0Sstevel@tonic-gateby Swat, the perl/Tk GUI debugger.
615*0Sstevel@tonic-gate
616*0Sstevel@tonic-gateNote that multiple "front-ends" can latch into this debugging API
617*0Sstevel@tonic-gatesimultaneously.  This is intended to facilitate things like
618*0Sstevel@tonic-gatedebugging with a command line and GUI at the same time, debugging
619*0Sstevel@tonic-gatedebuggers etc.  [Sounds nice, but this needs some serious support -- GSAR]
620*0Sstevel@tonic-gate
621*0Sstevel@tonic-gateIn particular, this API does B<not> provide the following functions:
622*0Sstevel@tonic-gate
623*0Sstevel@tonic-gate=over 4
624*0Sstevel@tonic-gate
625*0Sstevel@tonic-gate=item *
626*0Sstevel@tonic-gate
627*0Sstevel@tonic-gatedata display
628*0Sstevel@tonic-gate
629*0Sstevel@tonic-gate=item *
630*0Sstevel@tonic-gate
631*0Sstevel@tonic-gatecommand processing
632*0Sstevel@tonic-gate
633*0Sstevel@tonic-gate=item *
634*0Sstevel@tonic-gate
635*0Sstevel@tonic-gatecommand alias management
636*0Sstevel@tonic-gate
637*0Sstevel@tonic-gate=item *
638*0Sstevel@tonic-gate
639*0Sstevel@tonic-gateuser interface (tty or graphical)
640*0Sstevel@tonic-gate
641*0Sstevel@tonic-gate=back
642*0Sstevel@tonic-gate
643*0Sstevel@tonic-gateThese are intended to be services performed by the clients of this API.
644*0Sstevel@tonic-gate
645*0Sstevel@tonic-gateThis module attempts to be squeaky clean w.r.t C<use strict;> and when
646*0Sstevel@tonic-gatewarnings are enabled.
647*0Sstevel@tonic-gate
648*0Sstevel@tonic-gate
649*0Sstevel@tonic-gate=head2 Global Variables
650*0Sstevel@tonic-gate
651*0Sstevel@tonic-gateThe following "public" global names can be read by clients of this API.
652*0Sstevel@tonic-gateBeware that these should be considered "readonly".
653*0Sstevel@tonic-gate
654*0Sstevel@tonic-gate=over 8
655*0Sstevel@tonic-gate
656*0Sstevel@tonic-gate=item  $DB::sub
657*0Sstevel@tonic-gate
658*0Sstevel@tonic-gateName of current executing subroutine.
659*0Sstevel@tonic-gate
660*0Sstevel@tonic-gate=item  %DB::sub
661*0Sstevel@tonic-gate
662*0Sstevel@tonic-gateThe keys of this hash are the names of all the known subroutines.  Each value
663*0Sstevel@tonic-gateis an encoded string that has the sprintf(3) format
664*0Sstevel@tonic-gateC<("%s:%d-%d", filename, fromline, toline)>.
665*0Sstevel@tonic-gate
666*0Sstevel@tonic-gate=item  $DB::single
667*0Sstevel@tonic-gate
668*0Sstevel@tonic-gateSingle-step flag.  Will be true if the API will stop at the next statement.
669*0Sstevel@tonic-gate
670*0Sstevel@tonic-gate=item  $DB::signal
671*0Sstevel@tonic-gate
672*0Sstevel@tonic-gateSignal flag. Will be set to a true value if a signal was caught.  Clients may
673*0Sstevel@tonic-gatecheck for this flag to abort time-consuming operations.
674*0Sstevel@tonic-gate
675*0Sstevel@tonic-gate=item  $DB::trace
676*0Sstevel@tonic-gate
677*0Sstevel@tonic-gateThis flag is set to true if the API is tracing through subroutine calls.
678*0Sstevel@tonic-gate
679*0Sstevel@tonic-gate=item  @DB::args
680*0Sstevel@tonic-gate
681*0Sstevel@tonic-gateContains the arguments of current subroutine, or the C<@ARGV> array if in the
682*0Sstevel@tonic-gatetoplevel context.
683*0Sstevel@tonic-gate
684*0Sstevel@tonic-gate=item  @DB::dbline
685*0Sstevel@tonic-gate
686*0Sstevel@tonic-gateList of lines in currently loaded file.
687*0Sstevel@tonic-gate
688*0Sstevel@tonic-gate=item  %DB::dbline
689*0Sstevel@tonic-gate
690*0Sstevel@tonic-gateActions in current file (keys are line numbers).  The values are strings that
691*0Sstevel@tonic-gatehave the sprintf(3) format C<("%s\000%s", breakcondition, actioncode)>.
692*0Sstevel@tonic-gate
693*0Sstevel@tonic-gate=item  $DB::package
694*0Sstevel@tonic-gate
695*0Sstevel@tonic-gatePackage namespace of currently executing code.
696*0Sstevel@tonic-gate
697*0Sstevel@tonic-gate=item  $DB::filename
698*0Sstevel@tonic-gate
699*0Sstevel@tonic-gateCurrently loaded filename.
700*0Sstevel@tonic-gate
701*0Sstevel@tonic-gate=item  $DB::subname
702*0Sstevel@tonic-gate
703*0Sstevel@tonic-gateFully qualified name of currently executing subroutine.
704*0Sstevel@tonic-gate
705*0Sstevel@tonic-gate=item  $DB::lineno
706*0Sstevel@tonic-gate
707*0Sstevel@tonic-gateLine number that will be executed next.
708*0Sstevel@tonic-gate
709*0Sstevel@tonic-gate=back
710*0Sstevel@tonic-gate
711*0Sstevel@tonic-gate=head2 API Methods
712*0Sstevel@tonic-gate
713*0Sstevel@tonic-gateThe following are methods in the DB base class.  A client must
714*0Sstevel@tonic-gateaccess these methods by inheritance (*not* by calling them directly),
715*0Sstevel@tonic-gatesince the API keeps track of clients through the inheritance
716*0Sstevel@tonic-gatemechanism.
717*0Sstevel@tonic-gate
718*0Sstevel@tonic-gate=over 8
719*0Sstevel@tonic-gate
720*0Sstevel@tonic-gate=item CLIENT->register()
721*0Sstevel@tonic-gate
722*0Sstevel@tonic-gateregister a client object/package
723*0Sstevel@tonic-gate
724*0Sstevel@tonic-gate=item CLIENT->evalcode(STRING)
725*0Sstevel@tonic-gate
726*0Sstevel@tonic-gateeval STRING in executing code context
727*0Sstevel@tonic-gate
728*0Sstevel@tonic-gate=item CLIENT->skippkg('D::hide')
729*0Sstevel@tonic-gate
730*0Sstevel@tonic-gateask DB not to stop in these packages
731*0Sstevel@tonic-gate
732*0Sstevel@tonic-gate=item CLIENT->run()
733*0Sstevel@tonic-gate
734*0Sstevel@tonic-gaterun some more (until a breakpt is reached)
735*0Sstevel@tonic-gate
736*0Sstevel@tonic-gate=item CLIENT->step()
737*0Sstevel@tonic-gate
738*0Sstevel@tonic-gatesingle step
739*0Sstevel@tonic-gate
740*0Sstevel@tonic-gate=item CLIENT->next()
741*0Sstevel@tonic-gate
742*0Sstevel@tonic-gatestep over
743*0Sstevel@tonic-gate
744*0Sstevel@tonic-gate=item CLIENT->done()
745*0Sstevel@tonic-gate
746*0Sstevel@tonic-gatede-register from the debugging API
747*0Sstevel@tonic-gate
748*0Sstevel@tonic-gate=back
749*0Sstevel@tonic-gate
750*0Sstevel@tonic-gate=head2 Client Callback Methods
751*0Sstevel@tonic-gate
752*0Sstevel@tonic-gateThe following "virtual" methods can be defined by the client.  They will
753*0Sstevel@tonic-gatebe called by the API at appropriate points.  Note that unless specified
754*0Sstevel@tonic-gateotherwise, the debug API only defines empty, non-functional default versions
755*0Sstevel@tonic-gateof these methods.
756*0Sstevel@tonic-gate
757*0Sstevel@tonic-gate=over 8
758*0Sstevel@tonic-gate
759*0Sstevel@tonic-gate=item CLIENT->init()
760*0Sstevel@tonic-gate
761*0Sstevel@tonic-gateCalled after debug API inits itself.
762*0Sstevel@tonic-gate
763*0Sstevel@tonic-gate=item CLIENT->prestop([STRING])
764*0Sstevel@tonic-gate
765*0Sstevel@tonic-gateUsually inherited from DB package.  If no arguments are passed,
766*0Sstevel@tonic-gatereturns the prestop action string.
767*0Sstevel@tonic-gate
768*0Sstevel@tonic-gate=item CLIENT->stop()
769*0Sstevel@tonic-gate
770*0Sstevel@tonic-gateCalled when execution stops (w/ args file, line).
771*0Sstevel@tonic-gate
772*0Sstevel@tonic-gate=item CLIENT->idle()
773*0Sstevel@tonic-gate
774*0Sstevel@tonic-gateCalled while stopped (can be a client event loop).
775*0Sstevel@tonic-gate
776*0Sstevel@tonic-gate=item CLIENT->poststop([STRING])
777*0Sstevel@tonic-gate
778*0Sstevel@tonic-gateUsually inherited from DB package.  If no arguments are passed,
779*0Sstevel@tonic-gatereturns the poststop action string.
780*0Sstevel@tonic-gate
781*0Sstevel@tonic-gate=item CLIENT->evalcode(STRING)
782*0Sstevel@tonic-gate
783*0Sstevel@tonic-gateUsually inherited from DB package.  Ask for a STRING to be C<eval>-ed
784*0Sstevel@tonic-gatein executing code context.
785*0Sstevel@tonic-gate
786*0Sstevel@tonic-gate=item CLIENT->cleanup()
787*0Sstevel@tonic-gate
788*0Sstevel@tonic-gateCalled just before exit.
789*0Sstevel@tonic-gate
790*0Sstevel@tonic-gate=item CLIENT->output(LIST)
791*0Sstevel@tonic-gate
792*0Sstevel@tonic-gateCalled when API must show a message (warnings, errors etc.).
793*0Sstevel@tonic-gate
794*0Sstevel@tonic-gate
795*0Sstevel@tonic-gate=back
796*0Sstevel@tonic-gate
797*0Sstevel@tonic-gate
798*0Sstevel@tonic-gate=head1 BUGS
799*0Sstevel@tonic-gate
800*0Sstevel@tonic-gateThe interface defined by this module is missing some of the later additions
801*0Sstevel@tonic-gateto perl's debugging functionality.  As such, this interface should be considered
802*0Sstevel@tonic-gatehighly experimental and subject to change.
803*0Sstevel@tonic-gate
804*0Sstevel@tonic-gate=head1 AUTHOR
805*0Sstevel@tonic-gate
806*0Sstevel@tonic-gateGurusamy Sarathy	gsar@activestate.com
807*0Sstevel@tonic-gate
808*0Sstevel@tonic-gateThis code heavily adapted from an early version of perl5db.pl attributable
809*0Sstevel@tonic-gateto Larry Wall and the Perl Porters.
810*0Sstevel@tonic-gate
811*0Sstevel@tonic-gate=cut
812