File Coverage

lib/Devel/Trepan/Interface/Server.pm
Criterion Covered Total %
statement 43 130 33.0
branch 1 34 2.9
condition n/a
subroutine 15 33 45.4
pod 0 18 0.0
total 59 215 27.4


line stmt bran cond sub pod time code
1             # -*- coding: utf-8 -*-
2             # Copyright (C) 2011-2014 Rocky Bernstein <rocky@cpan.org>
3              
4 12     12   78 use warnings; no warnings 'redefine'; use utf8;
  12     12   29  
  12     12   365  
  12         64  
  12         26  
  12         363  
  12         68  
  12         29  
  12         92  
5              
6             # Interface for debugging a program but having user control
7             # reside outside of the debugged process, possibly on another
8             # computer
9             package Devel::Trepan::Interface::Server;
10 12     12   531 use English qw( -no_match_vars );
  12         35  
  12         78  
11             our (@ISA);
12              
13             # Our local modules
14 12     12   4447 use rlib '../../..';
  12         40  
  12         67  
15 12     12   4091 use rlib '.';
  12         28  
  12         51  
16 12     12   4112 use if !@ISA, Devel::Trepan::Interface::ComCodes;
  12         33  
  12         94  
17 12     12   1134 use if !@ISA, Devel::Trepan::IO::Input;
  12         30  
  12         59  
18 12     12   1392 use Devel::Trepan::Util qw(hash_merge YES NO);
  12         28  
  12         733  
19 12     12   71 use if !@ISA, Devel::Trepan::IO::TCPServer;
  12         30  
  12         54  
20 12     12   741 use if !@ISA, Devel::Trepan::IO::FIFOServer;
  12         30  
  12         95  
21              
22 12 50   12   609 use constant HAVE_TTY => eval q(use Devel::Trepan::IO::TTYServer; 1) ? 1 : 0;
  12     12   35  
  12         705  
  12         4794  
  0         0  
  0         0  
23              
24 12     12   86 use strict;
  12         32  
  12         562  
25              
26             @ISA = qw(Devel::Trepan::Interface Exporter);
27              
28 12         15216 use constant DEFAULT_INIT_CONNECTION_OPTS => {
29             io => 'tcp',
30             logger => undef # An Interface. Complaints go here.
31 12     12   77 };
  12         28  
32              
33             sub new
34             {
35 0     0 0   my($class, $input, $out, $connection_opts) = @_;
36 0           $connection_opts = hash_merge($connection_opts,
37             DEFAULT_INIT_CONNECTION_OPTS);
38              
39 0           my $server_type = $connection_opts->{io};
40             my $self = {
41             interactive => 1, # Or at least so we think initially
42             logger => $connection_opts->{logger}
43 0           };
44 0 0         unless (defined($input)) {
45 0           my $server;
46 0 0         if ('tty' eq $server_type) {
    0          
    0          
47 0           if (HAVE_TTY) {
48             $server = Devel::Trepan::IO::TTYServer->new($connection_opts);
49             } else {
50 0           die "You don't have Devel::Trepan::TTY installed";
51             }
52             } elsif ('fifo' eq $server_type) {
53 0           $server = Devel::Trepan::IO::FIFOServer->new($connection_opts);
54             } elsif ('tcp' eq $server_type) {
55 0           $server = Devel::Trepan::IO::TCPServer->new($connection_opts);
56             } else {
57 0           die "Unknown communication protocol: $server_type";
58             }
59             # For Compatability
60 0           $self->{output} = $self->{input} = $self->{inout} = $server;
61             }
62              
63 0           bless $self, $class;
64 0           return $self;
65             }
66              
67             # Closes both input and output
68             sub close($)
69             {
70 0     0 0   my ($self) = @_;
71 0           $self->{output}->write(QUIT . 'bye');
72             # FIXME: remove sleep and figure out to find when above worked.
73 0           sleep 1;
74 0 0         if ($self->{output} == $self->{input}) {
75 0           $self->{output}->close;
76             } else {
77 0           $self->{input}->close;
78 0           $self->{output}->close;
79             }
80             }
81              
82             sub is_closed($)
83             {
84 0     0 0   my ($self) = @_;
85             $self->{input}->is_closed && $self->{output}->is_closed
86 0 0         }
87              
88             sub is_interactive($)
89             {
90 0     0 0   my $self = shift;
91 0           $self->{input}->is_interactive;
92             }
93              
94             sub has_completion($)
95             {
96 0     0 0   0
97             }
98              
99             # Called when a dangerous action is about to be done to make sure
100             # it's okay. `prompt' is printed; user response is returned.
101             # FIXME: make common routine for this and user.rb
102             sub confirm($;$$)
103             {
104 0     0 0   my ($self, $prompt, $default) = @_;
105              
106 0           my $reply;
107 0           while (1) {
108             # begin
109 0           $self->write_confirm($prompt, $default);
110 0           $reply = $self->readline;
111 0           chomp($reply);
112 0 0         if (defined($reply)) {
113 0           ($reply = lc(unpack("A*", $reply))) =~ s/^\s+//;
114             } else {
115 0           return $default;
116             }
117 0 0         if (grep(/^${reply}$/, YES)) {
    0          
118 0           return 1;
119             } elsif (grep(/^${reply}$/, NO)) {
120 0           return 0;
121             } else {
122 0           $self->msg("Please answer 'yes' or 'no'. Try again.");
123             }
124             }
125 0           return $default;
126             }
127              
128             # Return 1 if we are connected
129             sub is_connected($)
130             {
131 0     0 0   my ($self) = @_;
132 0           'connected' eq $self->{inout}->{state};
133             }
134              
135             sub is_input_eof($)
136             {
137 0     0 0   my ($self) = @_;
138 0           0;
139             }
140              
141             # used to write to a debugger that is connected to this
142             # server; `str' written will have a newline added to it
143             sub msg($;$)
144             {
145 0     0 0   my ($self, $msg) = @_;
146 0           my @msg = split(/\n/, $msg);
147 0           foreach my $line (@msg) {
148 0           $self->{inout}->writeline(PRINT . $line);
149             }
150             }
151              
152             # used to write to a debugger that is connected to this
153             # server; `str' written will have a newline added to it
154             sub errmsg($;$)
155             {
156 0     0 0   my ($self, $msg) = @_;
157 0           my @msg = split(/\n/, $msg);
158 0           foreach my $line (@msg) {
159 0           $self->{inout}->writeline(SERVERERR . $line);
160             }
161             }
162              
163             # used to write to a debugger that is connected to this
164             # server; `str' written will not have a newline added to it
165             sub msg_nocr($$)
166             {
167 0     0 0   my ($self, $msg) = @_;
168 0           $self->{inout}->write(PRINT . $msg);
169             }
170              
171             # read a debugger command
172             sub read_command($$)
173             {
174 0     0 0   my ($self, $prompt) = @_;
175 0           $self->readline($prompt);
176             }
177              
178             sub read_data($)
179             {
180 0     0 0   my ($self, $prompt) = @_;
181 0           $self->{inout}->read_data;
182             }
183              
184             sub readline($;$)
185             {
186 0     0 0   my ($self, $prompt, $add_to_history) = @_;
187             # my ($self, $prompt, $add_to_history) = @_;
188             # $add_to_history = 1;
189 0 0         if ($prompt) {
190 0           $self->write_prompt($prompt);
191             }
192 0           my $coded_line;
193 0           eval {
194 0           $coded_line = $self->{inout}->read_msg();
195             };
196 0 0         if ($EVAL_ERROR) {
197 0 0         print {$self->{logger}} "$EVAL_ERROR\n" if $self->{logger};
  0            
198 0           $self->errmsg("Server communication protocol error, resyncing...");
199 0           return ('#');
200             } else {
201 0 0         if ($coded_line) {
202 0           my $read_ctrl = substr($coded_line,0,1);
203 0           return substr($coded_line, 1);
204             } else {
205 0           return "";
206             }
207             }
208             }
209              
210             sub remove_history($;$)
211             {
212 0     0 0   my ($self, $which) = @_;
213 0 0         return unless ($self->{input}{readline});
214 0 0         $which = $self->{input}{readline}->where_history() unless defined $which;
215 0           $self->{input}{readline}->remove_history($which);
216             }
217              
218             # Return connected
219             sub state($)
220             {
221 0     0 0   my ($self) = @_;
222 0           $self->{inout}->{state};
223             }
224              
225             sub write_prompt($$)
226             {
227 0     0 0   my ($self, $prompt) = @_;
228 0           $self->{inout}->write(PROMPT . $prompt);
229             }
230              
231             sub write_confirm($$$)
232             {
233 0     0 0   my ($self, $prompt, $default) = @_;
234 0 0         my $code = $default ? CONFIRM_TRUE : CONFIRM_FALSE;
235 0           $self->{inout}->write($code . $prompt)
236             }
237              
238             # Demo
239             unless (caller) {
240             my $intf = __PACKAGE__->new(undef, undef, {open => 0, io => 'tcp'});
241             # $intf->close();
242             $intf = __PACKAGE__->new(undef, undef,
243             {open => 1, io => 'tty', logger=>\*STDOUT});
244             $intf->close();
245             }
246              
247             1;