This file is indexed.

/usr/share/perl5/IO/Handle/Prototype/Fallback.pm is in libio-handle-util-perl 0.01-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
package IO::Handle::Prototype::Fallback;

use strict;
use warnings;

use Carp ();

use parent qw(IO::Handle::Prototype);

sub new {
    my ( $class, @args ) = @_;

    $class->SUPER::new(
        $class->_process_callbacks(@args),
    );
}

sub __write { shift->_cb(__write => @_) }
sub __read  { shift->_cb(__read => @_)  }

sub _process_callbacks {
    my ( $class, %user_cb ) = @_;

    if ( keys %user_cb == 1 ) {
        # these callbacks require wrapping of the user's callback to add
        # buffering, so we short circuit the entire process
        foreach my $fallback (qw(__read read getline)) {
            if ( my $cb = $user_cb{$fallback} ) {
                my $method = "_default_${fallback}_callbacks";

                return $class->_process_callbacks(
                    $class->$method($cb),
                );
            }
        }
    }

    my @fallbacks = $class->_base_callbacks;

    # additional fallbacks based on explicitly provided callbacks

    foreach my $fallback (qw(__write print write syswrite)) {
        if ( exists $user_cb{$fallback} ) {
            push @fallbacks, $class->_default_write_callbacks($fallback);
            last;
        }
    }

    if ( exists $user_cb{getline} ) {
        push @fallbacks, $class->_simple_getline_callbacks;
    }

    if ( exists $user_cb{read} ) {
        push @fallbacks, $class->_simple_read_callbacks;
    }

    # merge everything
    my %cb = (
        @fallbacks,
        %user_cb,
    );

    return \%cb;
}

sub _base_callbacks {
    my $class = shift;

    return (
        fileno => sub { undef },
        stat => sub { undef },
        opened => sub { 1 },
        blocking => sub {
            my ( $self, @args ) = @_;

            Carp::croak("Can't set blocking mode on iterator") if @args;

            return 1;
        },
    );
}

sub _make_read_callbacks {
    my ( $class, $read ) = @_;

    no warnings 'uninitialized';

    return (
        # these fallbacks must wrap the underlying reading mechanism
        __read => sub {
            my $self = shift;
            if ( exists $self->{buf} ) {
                return delete $self->{buf};
            } else {
                my $ret = $self->$read;

                unless ( defined $ret ) {
                    $self->{eof}++;
                }

                return $ret;
            }
        },
        getline => sub {
            my $self = shift;

            return undef if $self->{eof};

            if ( ref $/ ) {
                $self->read(my $ret, ${$/});
                return $ret;
            } elsif ( defined $/ ) {
                getline: {
                    if ( defined $self->{buf} and (my $off = index($self->{buf}, $/)) > -1 ) {
                        return substr($self->{buf}, 0, $off + length($/), '');
                    } else {
                        if ( defined( my $chunk = $self->$read ) ) {
                            $self->{buf} .= $chunk;
                            redo getline;
                        } else {
                            $self->{eof}++;

                            if ( length( my $buf = delete $self->{buf} ) ) {
                                return $buf;
                            } else {
                                return undef;
                            }
                        }
                    }
                }
            } else {
                my $ret = delete $self->{buf};

                while ( defined( my $chunk = $self->$read ) ) {
                    $ret .= $chunk;
                }

                $self->{eof}++;

                return $ret;
            }
        },
        read => sub {
            my ( $self, undef, $length, $offset ) = @_;

            return 0 if $self->{eof};

            if ( $offset and length($_[1]) < $offset ) {
                $_[1] .= "\0" x ( $offset - length($_[1]) );
            }

            while (length($self->{buf}) < $length) {
                if ( defined(my $next = $self->$read) ) {
                    $self->{buf} .= $next;
                } else {
                    # data ended but still under $length, return all that remains and
                    # empty the buffer
                    my $ret = length($self->{buf});

                    if ( $offset ) {
                        substr($_[1], $offset) = delete $self->{buf};
                    } else {
                        $_[1] = delete $self->{buf};
                    }

                    $self->{eof}++;
                    return $ret;
                }
            }

            my $read;
            if ( $length > length($self->{buf}) ) {
                $read = delete $self->{buf};
            } else {
                $read = substr($self->{buf}, 0, $length, '');
            }

            if ( $offset ) {
                substr($_[1], $offset) = $read;
            } else {
                $_[1] = $read;
            }

            return length($read);
        },
        eof => sub {
            my $self = shift;
            $self->{eof};
        },
        ungetc => sub {
            my ( $self, $ord ) = @_;

            substr( $self->{buf}, 0, 0, chr($ord) );

            return;
        },
    );
}

sub _default___read_callbacks {
    my ( $class, $read ) = @_;

    $class->_make_read_callbacks($read);
}

sub _default_read_callbacks {
    my ( $class, $read ) = @_;

    $class->_make_read_callbacks(sub {
        my $self = shift;

        if ( $self->$read(my $buf, ref $/ ? ${ $/ } : 4096) ) {
            return $buf;
        } else {
            return undef;
        }
    });
}

sub _default_getline_callbacks {
    my ( $class, $getline ) = @_;

    $class->_make_read_callbacks(sub {
        local $/ = ref $/ ? $/ : \4096;
        $_[0]->$getline;
    });
}

sub _simple_read_callbacks {
    my $class = shift;

    return (
        # these are generic fallbacks defined in terms of the wrapping ones
        sysread => sub {
            shift->read(@_);
        },
        getc => sub {
            my $self = shift;

            if ( $self->read(my $str, 1) ) {
                return $str;
            } else {
                return undef;
            }
        },
    );
}

sub _simple_getline_callbacks {
    my $class = shift;

    return (
        getlines => sub {
            my $self = shift;

            my @accum;

            while ( defined(my $next = $self->getline) ) {
                push @accum, $next;
            }

            return @accum;
        }
    );
}

sub _default_write_callbacks {
    my ( $class, $canonical ) = @_;

    return (
        autoflush => sub { 1 },
        sync      => sub { },
        flush     => sub { },

        # these are defined in terms of a canonical print method, either write,
        # syswrite or print
        __write => sub {
            my ( $self, $str ) = @_;
            local $\;
            local $,;
            $self->$canonical($str);
        },
        print => sub {
            my $self = shift;
            my $ofs = defined $, ? $, : '';
            my $ors = defined $\ ? $\ : '';
            $self->__write( join($ofs, @_) . $ors );
        },

        (map { $_ => sub {
            my ( $self, $str, $len, $offset ) = @_;
            $len = length($str) unless defined $len;
            $offset ||= 0;
            $self->__write(substr($str, $offset, $len));
        } } qw(write syswrite)),

        # wrappers for print
        printf => sub {
            my ( $self, $f, @args ) = @_;
            $self->print(sprintf $f, @args);
        },
        say => sub {
            local $\ = "\n";
            shift->print(@_);
        },
        printflush => sub {
            my $self = shift;
            my $autoflush = $self->autoflush;
            my $ret = $self->print(@_);
            $self->autoflush($autoflush);
            return $ret;
        }
    );
}

__PACKAGE__

# ex: set sw=4 et:

__END__

=pod

=head1 NAME

IO::Handle::Prototype::Fallback - Create L<IO::Handle> like objects using a set
of callbacks.

=head1 SYNOPSIS

    my $fh = IO::Handle::Prototype::Fallback->new(
        getline => sub {
            my $fh = shift;

            ...
        },
    );

=head1 DESCRIPTION

This class provides a way to define a filehandle based on callbacks.

Fallback implementations are provided to the extent possible based on the
provided callbacks, for both writing and reading.

=head1 SPECIAL CALLBACKS

This class provides two additional methods on top of L<IO::Handle>, designed to
let you implement things with a minimal amount of baggage.

The fallback methods are all best implemented using these, though these can be
implemented in terms of Perl's standard methods too.

However, to provide the most consistent semantics, it's better to do this:

    IO::Handle::Prototype::Fallback->new(
        __read => sub {
            shift @array;
        },
    );

Than this:

    IO::Handle::Prototype::Fallback->new(
        getline => sub {
            shift @array;
        },
    );

Because the fallback implementation of C<getline> implements all of the extra
crap you'd need to handle to have a fully featured implementation.

=over 4

=item __read

Return a chunk of data of any size (could use C<$/> or not, it depends on you,
unlike C<getline> which probably I<should> respect the value of C<$/>).

This avoids the annoying C<substr> stuff you need to do with C<read>.

=item __write $string

Write out a string.

This is like a simplified C<print>, which can disregard C<$,> and C<$\> as well
as multiple argument forms, and does not have the extra C<substr> annoyance of
C<write> or C<syswrite>.

=back

=head1 WRAPPING

If you provide a B<single> reading related callback (C<__read>, C<getline> or
C<read>) then your callback will be used to implement all of the other reading
primitives using a string buffer.

These implementations handle C<$/> in all forms (C<undef>, ref to number and
string), all the funny calling conventions for C<read>, etc.

=head1 FALLBACKS

Any callback that can be defined purely in terms of other callbacks in a way
will be added. For instance C<getc> can be implemented in terms of C<read>,
C<say> can be implemented in terms of C<print>, C<print> can be implemented in
terms of C<write>, C<write> can be implemented in terms of C<print>, etc.

None of these require special wrapping and will always be added if their
dependencies are present.

=head1 GLOB OVERLOADING

When overloaded as a glob a tied handle will be returned. This allows you to
use the handle in Perl's IO builtins. For instance:

    my $line = <$fh>

will not call the C<getline> method natively, but the tied interface arranges
for that to happen.

=cut