package App::Info::Lib::Expat; # $Id: Expat.pm 3180 2006-09-25 17:56:07Z theory $ =head1 NAME App::Info::Lib::Expat - Information about the Expat XML parser =head1 SYNOPSIS use App::Info::Lib::Expat; my $expat = App::Info::Lib::Expat->new; if ($expat->installed) { print "App name: ", $expat->name, "\n"; print "Version: ", $expat->version, "\n"; print "Bin dir: ", $expat->bin_dir, "\n"; } else { print "Expat is not installed. :-(\n"; } =head1 DESCRIPTION App::Info::Lib::Expat supplies information about the Expat XML parser installed on the local system. It implements all of the methods defined by App::Info::Lib. Methods that trigger events will trigger them only the first time they're called (See L for documentation on handling events). To start over (after, say, someone has installed Expat) construct a new App::Info::Lib::Expat object to aggregate new metadata. Some of the methods trigger the same events. This is due to cross-calling of shared subroutines. However, any one event should be triggered no more than once. For example, although the info event "Searching for 'expat.h'" is documented for the methods C, C, C, and C, rest assured that it will only be triggered once, by whichever of those four methods is called first. =cut use strict; use App::Info::Util; use App::Info::Lib; use Config; use vars qw(@ISA $VERSION); @ISA = qw(App::Info::Lib); $VERSION = '0.52'; my $u = App::Info::Util->new; ############################################################################## =head1 INTERFACE =head2 Constructor =head3 new my $expat = App::Info::Lib::Expat->new(@params); Returns an App::Info::Lib::Expat object. See L for a complete description of argument parameters. When called, C searches all of the paths returned by the C method for one of the files returned by the C method. If any of is found, then Expat is assumed to be installed. Otherwise, most of the object methods will return C. B =over 4 =item info Searching for Expat libraries =item confirm Path to Expat library directory? =item unknown Path to Expat library directory? =back =cut sub new { # Construct the object. my $self = shift->SUPER::new(@_); # Find libexpat. $self->info("Searching for Expat libraries"); my @libs = $self->search_lib_names; my $cb = sub { $u->first_cat_dir(\@libs, $_) }; if (my $lexpat = $u->first_cat_dir(\@libs, $self->search_lib_dirs)) { # We found libexpat. Confirm. $self->{libexpat} = $self->confirm( key => 'expat lib dir', prompt => 'Path to Expat library directory?', value => $lexpat, callback => $cb, error => 'No Expat libraries found in directory'); } else { # Handle an unknown value. $self->{libexpat} = $self->unknown( key => 'expat lib dir', prompt => 'Path to Expat library directory?', callback => $cb, error => 'No Expat libraries found in directory'); } return $self; } ############################################################################## =head2 Class Method =head3 key_name my $key_name = App::Info::Lib::Expat->key_name; Returns the unique key name that describes this class. The value returned is the string "Expat". =cut sub key_name { 'Expat' } ############################################################################## =head2 Object Methods =head3 installed print "Expat is ", ($expat->installed ? '' : 'not '), "installed.\n"; Returns true if Expat is installed, and false if it is not. App::Info::Lib::Expat determines whether Expat is installed based on the presence or absence on the file system of one of the files searched for when C constructed the object. If Expat does not appear to be installed, then most of the other object methods will return empty values. =cut sub installed { $_[0]->{libexpat} ? 1 : undef } ############################################################################## =head3 name my $name = $expat->name; Returns the name of the application. In this case, C simply returns the string "Expat". =cut sub name { 'Expat' } ############################################################################## =head3 version Returns the full version number for Expat. App::Info::Lib::Expat attempts to parse the version number from the F file, if it exists. B =over 4 =item info Searching for 'expat.h' Searching for include directory =item error Cannot find include directory Cannot find 'expat.h' Failed to parse version from 'expat.h' =item unknown Enter a valid Expat include directory Enter a valid Expat version number =back =cut my $get_version = sub { my $self = shift; $self->{version} = undef; $self->info("Searching for 'expat.h'"); my $inc = $self->inc_dir or ($self->error("Cannot find 'expat.h'")) && return; my $header = $u->catfile($inc, 'expat.h'); my @regexen = ( qr/XML_MAJOR_VERSION\s+(\d+)$/, qr/XML_MINOR_VERSION\s+(\d+)$/, qr/XML_MICRO_VERSION\s+(\d+)$/ ); my ($x, $y, $z) = $u->multi_search_file($header, @regexen); if (defined $x and defined $y and defined $z) { # Assemble the version number and store it. my $v = "$x.$y.$z"; @{$self}{qw(version major minor patch)} = ($v, $x, $y, $z); } else { # Warn them if we couldn't get them all. $self->error("Failed to parse version from '$header'"); } }; sub version { my $self = shift; return unless $self->{libexpat}; # Get data. $get_version->($self) unless exists $self->{version}; # Handle an unknown value. unless ($self->{version}) { # Create a validation code reference. my $chk_version = sub { # Try to get the version number parts. my ($x, $y, $z) = /^(\d+)\.(\d+).(\d+)$/; # Return false if we didn't get all three. return unless $x and defined $y and defined $z; # Save all three parts. @{$self}{qw(major minor patch)} = ($x, $y, $z); # Return true. return 1; }; $self->{version} = $self->unknown( key => 'expat version number', callback => $chk_version); } return $self->{version}; } ############################################################################## =head3 major_version my $major_version = $expat->major_version; Returns the Expat major version number. App::Info::Lib::Expat attempts to parse the version number from the F file, if it exists. For example, if C returns "1.95.2", then this method returns "1". B =over 4 =item info Searching for 'expat.h' Searching for include directory =item error Cannot find include directory Cannot find 'expat.h' Failed to parse version from 'expat.h' =item unknown Enter a valid Expat include directory Enter a valid Expat major version number =back =cut # This code reference is used by major_version(), minor_version(), and # patch_version() to validate a version number entered by a user. my $is_int = sub { /^\d+$/ }; sub major_version { my $self = shift; return unless $self->{libexpat}; # Get data. $get_version->($self) unless exists $self->{version}; # Handle an unknown value. $self->{major} = $self->unknown( key => 'expat major version number', callback => $is_int) unless $self->{major}; return $self->{major}; } ############################################################################## =head3 minor_version my $minor_version = $expat->minor_version; Returns the Expat minor version number. App::Info::Lib::Expat attempst to parse the version number from the F file, if it exists. For example, if C returns "1.95.2", then this method returns "95". B =over 4 =item info Searching for 'expat.h' Searching for include directory =item error Cannot find include directory Cannot find 'expat.h' Failed to parse version from 'expat.h' =item unknown Enter a valid Expat include directory Enter a valid Expat minor version number =back =cut sub minor_version { my $self = shift; return unless $self->{libexpat}; # Get data. $get_version->($self) unless exists $self->{version}; # Handle an unknown value. $self->{minor} = $self->unknown( key =>'expat minor version number', callback => $is_int) unless $self->{minor}; return $self->{minor}; } ############################################################################## =head3 patch_version my $patch_version = $expat->patch_version; Returns the Expat patch version number. App::Info::Lib::Expat attempst to parse the version number from the F file, if it exists. For example, C returns "1.95.2", then this method returns "2". B =over 4 =item info Searching for 'expat.h' Searching for include directory =item error Cannot find include directory Cannot find 'expat.h' Failed to parse version from 'expat.h' =item unknown Enter a valid Expat include directory Enter a valid Expat patch version number =back =cut sub patch_version { my $self = shift; return unless $self->{libexpat}; # Get data. $get_version->($self) unless exists $self->{version}; # Handle an unknown value. $self->{patch} = $self->unknown( key => 'expat patch version number', callback => $is_int) unless $self->{patch}; return $self->{patch}; } ############################################################################## =head3 bin_dir my $bin_dir = $expat->bin_dir; Since Expat includes no binaries, this method always returns false. =cut sub bin_dir { return } ############################################################################## =head3 executable my $executable = $expat->executable; Since Expat includes no executable program, this method always returns false. =cut sub executable { return } ############################################################################## =head3 inc_dir my $inc_dir = $expat->inc_dir; Returns the directory path in which the file F was found. App::Info::Lib::Expat searches for F in the following directories: =over 4 =item /usr/local/include =item /usr/include =item /sw/include =back B =over 4 =item info Searching for include directory =item error Cannot find include directory =item unknown Enter a valid Expat include directory =back =cut # This code reference is used by inc_dir() and so_lib_dir() to validate a # directory entered by the user. my $is_dir = sub { -d }; sub inc_dir { my $self = shift; return unless $self->{libexpat}; unless (exists $self->{inc_dir}) { $self->info("Searching for include directory"); my @incs = $self->search_inc_names; if (my $dir = $u->first_cat_dir(\@incs, $self->search_inc_dirs)) { $self->{inc_dir} = $dir; } else { $self->error("Cannot find include directory"); my $cb = sub { $u->first_cat_dir(\@incs, $_) }; $self->{inc_dir} = $self->unknown( key => 'explat inc dir', callback => $cb, error => "No expat include file found in " . "directory"); } } return $self->{inc_dir}; } ############################################################################## =head3 lib_dir my $lib_dir = $expat->lib_dir; Returns the directory path in which a Expat library was found. The files and paths searched are as described for the L<"new"|new> constructor, as are the events. =cut sub lib_dir { $_[0]->{libexpat} } ############################################################################## =head3 so_lib_dir my $so_lib_dir = $expat->so_lib_dir; Returns the directory path in which a Expat shared object library was found. It searches all of the paths in the C and C attributes defined by the Perl L module -- plus F (for all you Fink fans) -- for one of the following files: =over =item libexpat.so =item libexpat.so.0 =item libexpat.so.0.0.1 =item libexpat.dylib =item libexpat.0.dylib =item libexpat.0.0.1.dylib =back B =over 4 =item info Searching for shared object library directory =item error Cannot find shared object library direcory =item unknown Enter a valid Expat shared object library directory =back =cut sub so_lib_dir { my $self = shift; return unless $self->{libexpat}; unless (exists $self->{so_lib_dir}) { $self->info("Searching for shared object library directory"); my @libs = $self->search_so_lib_names; my $cb = sub { $u->first_cat_dir(\@libs, $_) }; if (my $dir = $u->first_cat_dir(\@libs, $self->search_lib_dirs)) { $self->{so_lib_dir} = $dir; } else { $self->error("Cannot find shared object library direcory"); $self->{so_lib_dir} = $self->unknown( key => 'expat so dir', callback => $cb, error => "Shared object libraries not " . "found in directory"); } } return $self->{so_lib_dir}; } =head3 home_url my $home_url = $expat->home_url; Returns the libexpat home page URL. =cut sub home_url { 'http://expat.sourceforge.net/' } =head3 download_url my $download_url = $expat->download_url; Returns the libexpat download URL. =cut sub download_url { 'http://sourceforge.net/projects/expat/' } ############################################################################## =head3 search_lib_names my @seach_lib_names = $self->search_lib_nams Returns a list of possible names for library files. Used by C to search for library files. By default, the list is: =over =item libexpat.a =item libexpat.la =item libexpat.so =item libexpat.so.0 =item libexpat.so.0.0.1 =item libexpat.dylib =item libexpat.0.dylib =item libexpat.0.0.1.dylib =back =cut sub search_lib_names { my $self = shift; return $self->SUPER::search_lib_names, map { "libexpat.$_"} qw(a la so so.0 so.0.0.1 dylib 0.dylib 0.0.1.dylib); } ############################################################################## =head3 search_so_lib_names my @seach_so_lib_names = $self->search_so_lib_nams Returns a list of possible names for shared object library files. Used by C to search for library files. By default, the list is: =over =item libexpat.so =item libexpat.so.0 =item libexpat.so.0.0.1 =item libexpat.dylib =item libexpat.0.dylib =item libexpat.0.0.1.dylib =back =cut sub search_so_lib_names { my $self = shift; return $self->SUPER::search_so_lib_names, map { "libexpat.$_"} qw(so so.0 so.0.0.1 dylib 0.dylib 0.0.1.dylib); } ############################################################################## =head3 search_lib_dirs my @search_lib_dirs = $expat->search_lib_dirs; Returns a list of possible directories in which to search for libraries. By default, it returns all of the paths in the C and C attributes defined by the Perl L module -- plus F (in support of all you Fink users out there). =cut sub search_lib_dirs { shift->SUPER::search_lib_dirs, $u->lib_dirs, '/sw/lib' } ############################################################################## =head3 search_inc_names my @search_inc_names = $expat->search_inc_names; Returns a list of include file names to search for. Used by C to search for an include file. By default, the only name returned is F. =cut sub search_inc_names { my $self = shift; return $self->SUPER::search_inc_names, "expat.h"; } ############################################################################## =head3 search_inc_dirs my @search_inc_dirs = $expat->search_inc_dirs; Returns a list of possible directories in which to search for include files. Used by C to search for an include file. By default, the directories are: =over 4 =item /usr/local/include =item /usr/include =item /sw/include =back =cut sub search_inc_dirs { shift->SUPER::search_inc_dirs, qw(/usr/local/include /usr/include /sw/include); } 1; __END__ =head1 KNOWN ISSUES This is a pretty simple class. It's possible that there are more directories that ought to be searched for libraries and includes. And if anyone knows how to get the version numbers, let me know! The format of the version number seems to have changed recently (1.95.1-2), and now I don't know where to find the version number. Patches welcome. =head1 BUGS Please send bug reports to or file them at L. =head1 AUTHOR David Wheeler based on code by Sam Tregar that Sam, in turn, borrowed from Clark Cooper's L module. =head1 SEE ALSO L documents the event handling interface. L is the App::Info::Lib::Expat parent class. L uses Expat to parse XML. L provides Perl configure-time information used by App::Info::Lib::Expat to locate Expat libraries and files. L is the Expat home page. =head1 COPYRIGHT AND LICENSE Copyright (c) 2002-2006, David Wheeler. All Rights Reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut