1b39c5158Smillertpackage TAP::Parser::Source; 2b39c5158Smillert 3b39c5158Smillertuse strict; 46fb12b70Safresh1use warnings; 5b39c5158Smillert 6898184e3Ssthenuse File::Basename qw( fileparse ); 76fb12b70Safresh1use base 'TAP::Object'; 8b39c5158Smillert 991f110e0Safresh1use constant BLK_SIZE => 512; 1091f110e0Safresh1 11b39c5158Smillert=head1 NAME 12b39c5158Smillert 13898184e3SsthenTAP::Parser::Source - a TAP source & meta data about it 14b39c5158Smillert 15b39c5158Smillert=head1 VERSION 16b39c5158Smillert 17*3d61058aSafresh1Version 3.48 18b39c5158Smillert 19b39c5158Smillert=cut 20b39c5158Smillert 21*3d61058aSafresh1our $VERSION = '3.48'; 22b39c5158Smillert 23b39c5158Smillert=head1 SYNOPSIS 24b39c5158Smillert 25b39c5158Smillert use TAP::Parser::Source; 26b39c5158Smillert my $source = TAP::Parser::Source->new; 27898184e3Ssthen $source->raw( \'reference to raw TAP source' ) 28898184e3Ssthen ->config( \%config ) 29898184e3Ssthen ->merge( $boolean ) 30898184e3Ssthen ->switches( \@switches ) 31898184e3Ssthen ->test_args( \@args ) 32898184e3Ssthen ->assemble_meta; 33898184e3Ssthen 34898184e3Ssthen do { ... } if $source->meta->{is_file}; 35898184e3Ssthen # see assemble_meta for a full list of data available 36b39c5158Smillert 37b39c5158Smillert=head1 DESCRIPTION 38b39c5158Smillert 39898184e3SsthenA TAP I<source> is something that produces a stream of TAP for the parser to 40898184e3Ssthenconsume, such as an executable file, a text file, an archive, an IO handle, a 41898184e3Ssthendatabase, etc. C<TAP::Parser::Source>s encapsulate these I<raw> sources, and 42898184e3Ssthenprovide some useful meta data about them. They are used by 43898184e3SsthenL<TAP::Parser::SourceHandler>s, which do whatever is required to produce & 44898184e3Ssthencapture a stream of TAP from the I<raw> source, and package it up in a 45898184e3SsthenL<TAP::Parser::Iterator> for the parser to consume. 46898184e3Ssthen 47898184e3SsthenUnless you're writing a new L<TAP::Parser::SourceHandler>, a plugin or 48898184e3Ssthensubclassing L<TAP::Parser>, you probably won't need to use this module directly. 49b39c5158Smillert 50b39c5158Smillert=head1 METHODS 51b39c5158Smillert 52b39c5158Smillert=head2 Class Methods 53b39c5158Smillert 54b39c5158Smillert=head3 C<new> 55b39c5158Smillert 56b39c5158Smillert my $source = TAP::Parser::Source->new; 57b39c5158Smillert 58b39c5158SmillertReturns a new C<TAP::Parser::Source> object. 59b39c5158Smillert 60b39c5158Smillert=cut 61b39c5158Smillert 62b39c5158Smillert# new() implementation supplied by TAP::Object 63b39c5158Smillert 64b39c5158Smillertsub _initialize { 65898184e3Ssthen my ($self) = @_; 66898184e3Ssthen $self->meta( {} ); 67898184e3Ssthen $self->config( {} ); 68b39c5158Smillert return $self; 69b39c5158Smillert} 70b39c5158Smillert 71b39c5158Smillert############################################################################## 72b39c5158Smillert 73b39c5158Smillert=head2 Instance Methods 74b39c5158Smillert 75898184e3Ssthen=head3 C<raw> 76b39c5158Smillert 77898184e3Ssthen my $raw = $source->raw; 78898184e3Ssthen $source->raw( $some_value ); 79b39c5158Smillert 80898184e3SsthenChaining getter/setter for the raw TAP source. This is a reference, as it may 81898184e3Ssthencontain large amounts of data (eg: raw TAP). 82b39c5158Smillert 83898184e3Ssthen=head3 C<meta> 84b39c5158Smillert 85898184e3Ssthen my $meta = $source->meta; 86898184e3Ssthen $source->meta({ %some_value }); 87b39c5158Smillert 88898184e3SsthenChaining getter/setter for meta data about the source. This defaults to an 89898184e3Ssthenempty hashref. See L</assemble_meta> for more info. 90b39c5158Smillert 91898184e3Ssthen=head3 C<has_meta> 92b39c5158Smillert 93898184e3SsthenTrue if the source has meta data. 94b39c5158Smillert 95898184e3Ssthen=head3 C<config> 96b39c5158Smillert 97898184e3Ssthen my $config = $source->config; 98898184e3Ssthen $source->config({ %some_value }); 99b39c5158Smillert 100898184e3SsthenChaining getter/setter for the source's configuration, if any has been provided 101898184e3Ssthenby the user. How it's used is up to you. This defaults to an empty hashref. 102898184e3SsthenSee L</config_for> for more info. 103b39c5158Smillert 104b39c5158Smillert=head3 C<merge> 105b39c5158Smillert 106b39c5158Smillert my $merge = $source->merge; 107898184e3Ssthen $source->config( $bool ); 108b39c5158Smillert 109898184e3SsthenChaining getter/setter for the flag that dictates whether STDOUT and STDERR 110898184e3Ssthenshould be merged (where appropriate). Defaults to undef. 111898184e3Ssthen 112898184e3Ssthen=head3 C<switches> 113898184e3Ssthen 114898184e3Ssthen my $switches = $source->switches; 115898184e3Ssthen $source->config([ @switches ]); 116898184e3Ssthen 117898184e3SsthenChaining getter/setter for the list of command-line switches that should be 118898184e3Ssthenpassed to the source (where appropriate). Defaults to undef. 119898184e3Ssthen 120898184e3Ssthen=head3 C<test_args> 121898184e3Ssthen 122898184e3Ssthen my $test_args = $source->test_args; 123898184e3Ssthen $source->config([ @test_args ]); 124898184e3Ssthen 125898184e3SsthenChaining getter/setter for the list of command-line arguments that should be 126898184e3Ssthenpassed to the source (where appropriate). Defaults to undef. 127b39c5158Smillert 128b39c5158Smillert=cut 129b39c5158Smillert 130898184e3Ssthensub raw { 131898184e3Ssthen my $self = shift; 132898184e3Ssthen return $self->{raw} unless @_; 133898184e3Ssthen $self->{raw} = shift; 134898184e3Ssthen return $self; 135898184e3Ssthen} 136898184e3Ssthen 137898184e3Ssthensub meta { 138898184e3Ssthen my $self = shift; 139898184e3Ssthen return $self->{meta} unless @_; 140898184e3Ssthen $self->{meta} = shift; 141898184e3Ssthen return $self; 142898184e3Ssthen} 143898184e3Ssthen 144898184e3Ssthensub has_meta { 145898184e3Ssthen return scalar %{ shift->meta } ? 1 : 0; 146898184e3Ssthen} 147898184e3Ssthen 148898184e3Ssthensub config { 149898184e3Ssthen my $self = shift; 150898184e3Ssthen return $self->{config} unless @_; 151898184e3Ssthen $self->{config} = shift; 152898184e3Ssthen return $self; 153898184e3Ssthen} 154898184e3Ssthen 155b39c5158Smillertsub merge { 156b39c5158Smillert my $self = shift; 157b39c5158Smillert return $self->{merge} unless @_; 158b39c5158Smillert $self->{merge} = shift; 159b39c5158Smillert return $self; 160b39c5158Smillert} 161b39c5158Smillert 162898184e3Ssthensub switches { 163898184e3Ssthen my $self = shift; 164898184e3Ssthen return $self->{switches} unless @_; 165898184e3Ssthen $self->{switches} = shift; 166898184e3Ssthen return $self; 167898184e3Ssthen} 168898184e3Ssthen 169898184e3Ssthensub test_args { 170898184e3Ssthen my $self = shift; 171898184e3Ssthen return $self->{test_args} unless @_; 172898184e3Ssthen $self->{test_args} = shift; 173898184e3Ssthen return $self; 174898184e3Ssthen} 175898184e3Ssthen 176898184e3Ssthen=head3 C<assemble_meta> 177898184e3Ssthen 178898184e3Ssthen my $meta = $source->assemble_meta; 179898184e3Ssthen 180898184e3SsthenGathers meta data about the L</raw> source, stashes it in L</meta> and returns 181898184e3Ssthenit as a hashref. This is done so that the L<TAP::Parser::SourceHandler>s don't 182898184e3Ssthenhave to repeat common checks. Currently this includes: 183898184e3Ssthen 184898184e3Ssthen is_scalar => $bool, 185898184e3Ssthen is_hash => $bool, 186898184e3Ssthen is_array => $bool, 187898184e3Ssthen 188898184e3Ssthen # for scalars: 189898184e3Ssthen length => $n 190898184e3Ssthen has_newlines => $bool 191898184e3Ssthen 192898184e3Ssthen # only done if the scalar looks like a filename 193898184e3Ssthen is_file => $bool, 194898184e3Ssthen is_dir => $bool, 195898184e3Ssthen is_symlink => $bool, 196898184e3Ssthen file => { 197898184e3Ssthen # only done if the scalar looks like a filename 198898184e3Ssthen basename => $string, # including ext 199898184e3Ssthen dir => $string, 200898184e3Ssthen ext => $string, 201898184e3Ssthen lc_ext => $string, 202898184e3Ssthen # system checks 203898184e3Ssthen exists => $bool, 204898184e3Ssthen stat => [ ... ], # perldoc -f stat 205898184e3Ssthen empty => $bool, 206898184e3Ssthen size => $n, 207898184e3Ssthen text => $bool, 208898184e3Ssthen binary => $bool, 209898184e3Ssthen read => $bool, 210898184e3Ssthen write => $bool, 211898184e3Ssthen execute => $bool, 212898184e3Ssthen setuid => $bool, 213898184e3Ssthen setgid => $bool, 214898184e3Ssthen sticky => $bool, 215898184e3Ssthen is_file => $bool, 216898184e3Ssthen is_dir => $bool, 217898184e3Ssthen is_symlink => $bool, 218898184e3Ssthen # only done if the file's a symlink 219898184e3Ssthen lstat => [ ... ], # perldoc -f lstat 220898184e3Ssthen # only done if the file's a readable text file 221898184e3Ssthen shebang => $first_line, 222898184e3Ssthen } 223898184e3Ssthen 224898184e3Ssthen # for arrays: 225898184e3Ssthen size => $n, 226898184e3Ssthen 227898184e3Ssthen=cut 228898184e3Ssthen 229898184e3Ssthensub assemble_meta { 230898184e3Ssthen my ($self) = @_; 231898184e3Ssthen 232898184e3Ssthen return $self->meta if $self->has_meta; 233898184e3Ssthen 234898184e3Ssthen my $meta = $self->meta; 235898184e3Ssthen my $raw = $self->raw; 236898184e3Ssthen 237898184e3Ssthen # rudimentary is object test - if it's blessed it'll 238898184e3Ssthen # inherit from UNIVERSAL 239898184e3Ssthen $meta->{is_object} = UNIVERSAL::isa( $raw, 'UNIVERSAL' ) ? 1 : 0; 240898184e3Ssthen 241898184e3Ssthen if ( $meta->{is_object} ) { 242898184e3Ssthen $meta->{class} = ref($raw); 243898184e3Ssthen } 244898184e3Ssthen else { 245898184e3Ssthen my $ref = lc( ref($raw) ); 246898184e3Ssthen $meta->{"is_$ref"} = 1; 247898184e3Ssthen } 248898184e3Ssthen 249898184e3Ssthen if ( $meta->{is_scalar} ) { 250898184e3Ssthen my $source = $$raw; 251898184e3Ssthen $meta->{length} = length($$raw); 252898184e3Ssthen $meta->{has_newlines} = $$raw =~ /\n/ ? 1 : 0; 253898184e3Ssthen 254898184e3Ssthen # only do file checks if it looks like a filename 255898184e3Ssthen if ( !$meta->{has_newlines} and $meta->{length} < 1024 ) { 256898184e3Ssthen my $file = {}; 257898184e3Ssthen $file->{exists} = -e $source ? 1 : 0; 258898184e3Ssthen if ( $file->{exists} ) { 259898184e3Ssthen $meta->{file} = $file; 260898184e3Ssthen 261898184e3Ssthen # avoid extra system calls (see `perldoc -f -X`) 262898184e3Ssthen $file->{stat} = [ stat(_) ]; 263898184e3Ssthen $file->{empty} = -z _ ? 1 : 0; 264898184e3Ssthen $file->{size} = -s _; 265898184e3Ssthen $file->{text} = -T _ ? 1 : 0; 266898184e3Ssthen $file->{binary} = -B _ ? 1 : 0; 267898184e3Ssthen $file->{read} = -r _ ? 1 : 0; 268898184e3Ssthen $file->{write} = -w _ ? 1 : 0; 269898184e3Ssthen $file->{execute} = -x _ ? 1 : 0; 270898184e3Ssthen $file->{setuid} = -u _ ? 1 : 0; 271898184e3Ssthen $file->{setgid} = -g _ ? 1 : 0; 272898184e3Ssthen $file->{sticky} = -k _ ? 1 : 0; 273898184e3Ssthen 274898184e3Ssthen $meta->{is_file} = $file->{is_file} = -f _ ? 1 : 0; 275898184e3Ssthen $meta->{is_dir} = $file->{is_dir} = -d _ ? 1 : 0; 276898184e3Ssthen 277898184e3Ssthen # symlink check requires another system call 278898184e3Ssthen $meta->{is_symlink} = $file->{is_symlink} 279898184e3Ssthen = -l $source ? 1 : 0; 280898184e3Ssthen if ( $file->{is_symlink} ) { 281898184e3Ssthen $file->{lstat} = [ lstat(_) ]; 282898184e3Ssthen } 283898184e3Ssthen 284898184e3Ssthen # put together some common info about the file 285898184e3Ssthen ( $file->{basename}, $file->{dir}, $file->{ext} ) 286898184e3Ssthen = map { defined $_ ? $_ : '' } 287898184e3Ssthen fileparse( $source, qr/\.[^.]*/ ); 288898184e3Ssthen $file->{lc_ext} = lc( $file->{ext} ); 289898184e3Ssthen $file->{basename} .= $file->{ext} if $file->{ext}; 290898184e3Ssthen 29191f110e0Safresh1 if ( !$file->{is_dir} && $file->{read} ) { 29291f110e0Safresh1 eval { $file->{shebang} = $self->shebang($$raw); }; 293898184e3Ssthen if ( my $e = $@ ) { 294898184e3Ssthen warn $e; 295898184e3Ssthen } 296898184e3Ssthen } 297898184e3Ssthen } 298898184e3Ssthen } 299898184e3Ssthen } 300898184e3Ssthen elsif ( $meta->{is_array} ) { 301898184e3Ssthen $meta->{size} = $#$raw + 1; 302898184e3Ssthen } 303898184e3Ssthen elsif ( $meta->{is_hash} ) { 304898184e3Ssthen ; # do nothing 305898184e3Ssthen } 306898184e3Ssthen 307898184e3Ssthen return $meta; 308898184e3Ssthen} 309898184e3Ssthen 310898184e3Ssthen=head3 C<shebang> 311898184e3Ssthen 312898184e3SsthenGet the shebang line for a script file. 313898184e3Ssthen 314898184e3Ssthen my $shebang = TAP::Parser::Source->shebang( $some_script ); 315898184e3Ssthen 316898184e3SsthenMay be called as a class method 317898184e3Ssthen 318898184e3Ssthen=cut 319898184e3Ssthen 320898184e3Ssthen{ 321898184e3Ssthen 322898184e3Ssthen # Global shebang cache. 323898184e3Ssthen my %shebang_for; 324898184e3Ssthen 325898184e3Ssthen sub _read_shebang { 32691f110e0Safresh1 my ( $class, $file ) = @_; 32791f110e0Safresh1 open my $fh, '<', $file or die "Can't read $file: $!\n"; 32891f110e0Safresh1 32991f110e0Safresh1 # Might be a binary file - so read a fixed number of bytes. 3306fb12b70Safresh1 my $got = read $fh, my ($buf), BLK_SIZE; 33191f110e0Safresh1 defined $got or die "I/O error: $!\n"; 33291f110e0Safresh1 return $1 if $buf =~ /(.*)/; 33391f110e0Safresh1 return; 334898184e3Ssthen } 335898184e3Ssthen 336898184e3Ssthen sub shebang { 337898184e3Ssthen my ( $class, $file ) = @_; 338898184e3Ssthen $shebang_for{$file} = $class->_read_shebang($file) 339898184e3Ssthen unless exists $shebang_for{$file}; 340898184e3Ssthen return $shebang_for{$file}; 341898184e3Ssthen } 342898184e3Ssthen} 343898184e3Ssthen 344898184e3Ssthen=head3 C<config_for> 345898184e3Ssthen 346898184e3Ssthen my $config = $source->config_for( $class ); 347898184e3Ssthen 348898184e3SsthenReturns L</config> for the $class given. Class names may be fully qualified 349898184e3Ssthenor abbreviated, eg: 350898184e3Ssthen 351898184e3Ssthen # these are equivalent 352898184e3Ssthen $source->config_for( 'Perl' ); 353898184e3Ssthen $source->config_for( 'TAP::Parser::SourceHandler::Perl' ); 354898184e3Ssthen 355898184e3SsthenIf a fully qualified $class is given, its abbreviated version is checked first. 356898184e3Ssthen 357898184e3Ssthen=cut 358898184e3Ssthen 359898184e3Ssthensub config_for { 360898184e3Ssthen my ( $self, $class ) = @_; 361898184e3Ssthen my ($abbrv_class) = ( $class =~ /(?:\:\:)?(\w+)$/ ); 362898184e3Ssthen my $config = $self->config->{$abbrv_class} || $self->config->{$class}; 363898184e3Ssthen return $config; 364b39c5158Smillert} 365b39c5158Smillert 366b39c5158Smillert1; 367b39c5158Smillert 368898184e3Ssthen__END__ 369b39c5158Smillert 370898184e3Ssthen=head1 AUTHORS 371b39c5158Smillert 372898184e3SsthenSteve Purkis. 373b39c5158Smillert 374b39c5158Smillert=head1 SEE ALSO 375b39c5158Smillert 376b39c5158SmillertL<TAP::Object>, 377b39c5158SmillertL<TAP::Parser>, 378898184e3SsthenL<TAP::Parser::IteratorFactory>, 379898184e3SsthenL<TAP::Parser::SourceHandler> 380b39c5158Smillert 381b39c5158Smillert=cut 382