File Coverage

blib/lib/IO/Mux/Handle.pm
Criterion Covered Total %
statement 176 181 97.2
branch 58 64 90.6
condition 7 7 100.0
subroutine 32 33 96.9
pod 3 3 100.0
total 276 288 95.8


line stmt bran cond sub pod time code
1             package IO::Mux::Handle ;
2             @ISA = qw(IO::Handle) ;
3              
4 3     3   16 use strict ;
  3         9  
  3         101  
5 3     3   1067 use IO::Handle ;
  3         7667  
  3         697  
6              
7              
8             our $VERSION = '0.08' ;
9              
10              
11             sub new {
12 12     12 1 1765 my $class = shift ;
13 12         18 my $mux = shift ;
14              
15 12         58 my $this = $class->SUPER::new() ;
16 12         219 tie(*{$this}, 'IO::Mux::Tie::Handle', $mux) ;
  12         96  
17              
18 12         39 return $this ;
19             }
20              
21              
22             sub open {
23 13     13 1 2140 my $this = shift ;
24 13         15 my $id = shift ;
25              
26 13         36 return open($this, $id) ;
27             }
28              
29              
30             sub get_error {
31 2     2 1 411 my $this = shift ;
32              
33 2         7 return $this->_get_tie()->_get_error() ;
34             }
35              
36              
37             sub _get_tie {
38 39     39   62 my $this = shift ;
39              
40 39         79 return tied(*{$this}) ;
  39         139  
41             }
42              
43              
44              
45             #################################################
46             package IO::Mux::Tie::Handle ;
47             @IO::Mux::Tie::Handle::ISA = qw(Tie::Handle) ;
48              
49 3     3   3047 use Tie::Handle ;
  3         7126  
  3         63  
50 3     3   1638 use IO::Mux::Packet ;
  3         10  
  3         93  
51 3     3   2837 use Errno ;
  3         4554  
  3         8665  
52              
53              
54             sub TIEHANDLE {
55 12     12   19 my $class = shift ;
56 12         17 my $mux = shift ;
57              
58 12         53 return $class->new($mux) ;
59             }
60              
61              
62             sub new {
63 12     12   14 my $class = shift ;
64 12         13 my $mux = shift ;
65              
66 12         20 my $this = {} ;
67 12         21 $this->{mux} = $mux ;
68 12         20 $this->{id} = undef ;
69 12         18 $this->{closed} = 1 ;
70 12         18 $this->{'eof'} = 0 ;
71 12         20 $this->{error} = undef ;
72              
73 12         45 bless($this, $class) ;
74             }
75              
76              
77             sub OPEN {
78 19     19   758 my $this = shift ;
79 19         21 my $id = shift ;
80              
81 19         39 $this->CLOSE() ;
82              
83 19         33 $id =~ s/\t/ /g ; # no \t's allowed in the id
84 19 100       35 if ($this->_get_mux()->_buffer_exists($id)){
85 1         6 $this->_set_error("Id '$id' is already in use by another handle") ;
86 1         4 return undef ;
87             }
88              
89 18         36 $this->{id} = $id ;
90             # Allocate the buffer
91 18         31 $this->_get_mux()->_get_buffer($id) ;
92 18         29 $this->{closed} = 0 ;
93 18         27 $this->{'eof'} = 0 ;
94 18         20 $this->{error} = undef ;
95              
96 18         53 return 1 ;
97             }
98              
99              
100             sub _get_mux {
101 277     277   338 my $this = shift ;
102              
103 277         750 return $this->{mux} ;
104             }
105              
106              
107             sub _get_id {
108 215     215   274 my $this = shift ;
109              
110 215         900 return $this->{id} ;
111             }
112              
113              
114             sub _get_eof {
115 51     51   60 my $this = shift ;
116              
117 51         298 return $this->{'eof'} ;
118             }
119              
120              
121             sub _set_eof {
122 7     7   13 my $this = shift ;
123              
124 7         13 $this->{'eof'} = 1 ;
125             }
126              
127              
128             sub _get_error {
129 2     2   2 my $this = shift ;
130              
131 2         17 return $this->{error} ;
132             }
133              
134              
135             sub _set_error {
136 9     9   12 my $this = shift ;
137 9         11 my $msg = shift ;
138              
139 9         12 $this->{error} = $msg ;
140 9 50       42 if (exists($!{EIO})){
141 9         56 $! = Errno::EIO() ;
142             }
143             else {
144 0         0 $! = 99999 ;
145             }
146             }
147              
148              
149             sub _get_buffer {
150 130     130   144 my $this = shift ;
151              
152 130         251 return $this->_get_mux()->_get_buffer($this->_get_id()) ;
153             }
154              
155              
156             sub _kill_buffer {
157 18     18   24 my $this = shift ;
158              
159 18         36 return $this->_get_mux()->_kill_buffer($this->_get_id()) ;
160             }
161              
162              
163              
164             sub WRITE {
165 28     28   6293 my $this = shift ;
166 28         48 my ($buf, $len, $offset) = @_ ;
167              
168 28 100       110 if ($this->{closed}){
169 3         8 $this->_set_error("WRITE on closed filehandle") ;
170 3         9 return undef ;
171             }
172              
173 25   100     53 my $p = new IO::Mux::Packet($this->_get_id(), substr($buf, $offset || 0, $len)) ;
174 25         68 my $rc = $this->_get_mux()->_write($p) ;
175              
176 25         169 return $rc ;
177             }
178              
179              
180             sub READ {
181 15     15   8125 my $this = shift ;
182 15         29 my ($buf, $len, $offset) = @_ ;
183              
184 15 100       47 if ($this->{closed}){
185 1         4 $this->_set_error("READ on closed filehandle") ;
186 1         4 return undef ;
187             }
188 14 100       33 return 0 if $this->_get_eof() ;
189              
190             # Load the buffer until there is enough data or EOF.
191             #while ($this->_get_buffer()->get_length() < $len){
192              
193             # We must block if the buffer is empty, otherwise we just check
194             # if there is something pending.
195 13         21 my $probe = 1 ;
196 13 100       36 if (! $this->_get_buffer()->get_length()){
197 7         19 my $rc = $this->_read_more_data(1) ;
198 7 50       31 if (! defined($rc)){
    100          
199 0         0 return undef ; # error already set by read_more_data
200             }
201             elsif (! $rc){
202             # EOF
203 3         7 $probe = 0 ;
204             }
205             }
206              
207 13 100       36 if ($probe){
208 10         11 my $rc = 1 ;
209 10         47 while ($rc > 0){
210 12         24 $rc = $this->_read_more_data(0) ;
211             }
212              
213 10 50       26 if (! defined($rc)){
214 0         0 return undef ; # error already set by read_more_data
215             }
216             }
217              
218             # Shorten the length if we hit EOF...
219 13 100       24 if ($this->_get_buffer()->get_length() < $len){
220 4         10 $len = $this->_get_buffer()->get_length() ;
221             }
222              
223 13 100       34 if ($len > 0){
224             # Extract $len bytes from the beginning of the buffer and
225 10         21 my $data = $this->_get_buffer()->shift_data($len) ;
226 10   100     41 substr($buf, $offset || 0, $len) = $data ;
227 10         15 $_[0] = $buf ;
228             }
229              
230 13         35 return $len ;
231             }
232              
233              
234             sub READLINE {
235 17     17   4710 my $this = shift ;
236              
237 17 100       51 if ($this->{closed}){
238 1         3 $this->_set_error("READLINE on closed filehandle") ;
239 1         5 return undef ;
240             }
241 16 100       36 return (wantarray ? () : undef) if $this->_get_eof() ;
    100          
242              
243 13         21 my @ret = () ;
244 13         43 while (1){
245 15         21 my $idx = -1 ;
246 15         16 my $buf = undef ;
247 15   100     66 while ((! length($/))||(($idx = index($this->_get_buffer()->get_data(), $/)) == -1)){
248 16         36 my $rc = $this->_read_more_data(1) ;
249 16 100       213 if (! defined($rc)){
    100          
250             # Return what we got or return undef/() ?
251 1         3 last ; # error already set by read_more_data
252             }
253             elsif (! $rc){
254             # EOF
255 3         3 last ;
256             }
257             }
258              
259 15 100       38 if ($idx != -1){
260 11         24 $buf = $this->_get_buffer()->shift_data($idx + length($/)) ;
261             }
262             else {
263             # Empty the buffer
264 4         21 my $len = $this->_get_buffer()->get_length() ;
265 4 100       12 if ($len){
266 1         2 $buf = $this->_get_buffer()->shift_data($len) ;
267             }
268             }
269              
270 15 100       39 if (defined($buf)){
271 12         16 push @ret, $buf ;
272 12 100       38 last unless wantarray ;
273             }
274             else {
275 3         5 last ;
276             }
277             }
278              
279 13 100       76 return (wantarray ? @ret : $ret[0]) ;
280             }
281              
282              
283             sub _read_more_data {
284 35     35   40 my $this = shift ;
285 35         38 my $blocking = shift ;
286              
287 35 100       55 if ($this->_get_buffer()->is_closed()){
288             # The handle is closed.
289 1         4 $this->_set_eof() ;
290 1         2 return 0 ;
291             }
292              
293 34         53 my $rc = undef ;
294 34         49 eval {
295 34         61 $rc = $this->_get_mux()->_read($this->_get_id(), $blocking) ;
296             } ;
297 34 100       205 if ($@){
    50          
    100          
    100          
298 1         4 $this->_set_error($@) ;
299 1         2 return undef ;
300             }
301             elsif (! defined($rc)){
302 0         0 return $rc ;
303             }
304             elsif ($rc == -1){
305             # No data available in non-blocking mode.
306 10         37 return -1 ;
307             }
308             elsif (! $rc){
309             # We have reached EOF.
310 5         12 $this->_set_eof() ;
311             }
312            
313 23         35 return $rc ;
314             }
315              
316              
317             sub EOF {
318 22     22   2277 my $this = shift ;
319              
320 22 100       84 return 1 if $this->{closed} ;
321 21         57 return $this->_get_eof() ;
322             }
323              
324              
325             sub CLOSE {
326 43     43   4606 my $this = shift ;
327              
328 43         64 my $ret = 0 ;
329 43 100       133 if (! $this->{closed}){
330 18         79 my $p = new IO::Mux::Packet($this->{id}, 0) ;
331 18         84 $p->make_eof() ;
332             # Here the real filehandle is possibly closed, so we must silence
333             # the warning. We may also get a SIGPIPE, which we will solve
334             # by closing the real handle.
335             local $SIG{__WARN__} = sub {
336 0 0   0   0 warn $_[0] unless ($_[0] =~ /closed filehandle/i) ;
337 18         147 } ;
338             local $SIG{PIPE} = sub {
339 1     1   5 close($this->_get_mux()->_get_handle()) ;
340 18         326 } ;
341              
342 18         47 $ret = $this->_get_mux()->_write($p) ;
343 18         41 $this->{closed} = 1 ;
344 18         67 $this->_kill_buffer() ;
345 18         606 return 1 ;
346             }
347              
348 25         160 return $ret ;
349             }
350              
351              
352             sub SEEK {
353 2     2   951 my $this = shift ;
354 2         3 my $pos = shift ;
355 2         4 my $whence = shift ;
356              
357 2         5 return 0 ;
358             }
359              
360              
361             sub BINMODE {
362 2     2   924 my $this = shift ;
363              
364 2 100       7 if ($this->{closed}){
365 1         3 $this->_set_error("BINMODE on closed filehandle") ;
366 1         3 return undef ;
367             }
368              
369 1         3 return 1 ;
370             }
371              
372              
373             sub FILENO {
374 2     2   768 my $this = shift ;
375              
376 2         6 return $this->_get_id() ;
377             }
378              
379              
380             sub TELL {
381 2     2   7 my $this = shift ;
382              
383 2 100       7 if ($this->{closed}){
384 1         4 $this->_set_error("TELL on closed filehandle") ;
385 1         4 return -1 ;
386             }
387              
388 1         3 return 0 ;
389             }
390              
391              
392             sub DESTROY {
393 12     12   1705 my $this = shift ;
394              
395 12         37 $this->CLOSE() ;
396             }
397              
398              
399              
400             1 ;
401             __END__