diff options
Diffstat (limited to 'support/autolatex/pm/AutoLaTeX/TeX/TeXDependencyAnalyzer.pm')
-rw-r--r-- | support/autolatex/pm/AutoLaTeX/TeX/TeXDependencyAnalyzer.pm | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/support/autolatex/pm/AutoLaTeX/TeX/TeXDependencyAnalyzer.pm b/support/autolatex/pm/AutoLaTeX/TeX/TeXDependencyAnalyzer.pm new file mode 100644 index 0000000000..253ac844af --- /dev/null +++ b/support/autolatex/pm/AutoLaTeX/TeX/TeXDependencyAnalyzer.pm @@ -0,0 +1,393 @@ +# autolatex - TeXDependencyAnalyzer.pm +# Copyright (C) 2013-17 Stephane Galland <galland@arakhne.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +=pod + +=head1 NAME + +TeXDependencyAnalyzer.pm - Detect dependencies of a TeX file. + +=head1 DESCRIPTION + +Tool that is extracting the dependencies of the TeX file. + +To use this library, type C<use AutoLaTeX::TeX::TeXDependencyAnalyzer;>. + +=head1 FUNCTIONS + +The provided functions are: + +=over 4 + +=cut +package AutoLaTeX::TeX::TeXDependencyAnalyzer; + +$VERSION = '7.0'; +@ISA = ('Exporter'); +@EXPORT = qw( &getDependenciesOfTeX ) ; +@EXPORT_OK = qw(); + +require 5.014; +use strict; +use utf8; +use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION); +use Config; # Perl configuration +use File::Spec; +use File::Basename; +use AutoLaTeX::Core::Util; +use AutoLaTeX::TeX::TeXParser; + +my %MACROS = ( + 'input' => '!{}', + 'include' => '!{}', + 'makeindex' => '[]', + 'printindex' => '[]', + 'makeglossaries' => '', + 'printglossaries' => '', + 'printglossary' => '![]', + 'usepackage' => '![]!{}', + 'RequirePackage' => '![]!{}', + 'documentclass' => '![]!{}', + 'addbibresource' => '![]!{}', + ); + +=pod + +=item B<getDependenciesOfTeX($)> + +Parse a TeX file and detect the included files. + +=over 4 + +=item * C<file> is the name of the TeX file to parse. + +=item * C<dir> is the reference directory for the relative path. + +=back + +I<Returns:> the included files from the TeX file into an associative array. + +=cut +sub getDependenciesOfTeX($$) { + my $input = shift; + my $rootdir = shift; + + local *FILE; + open(*FILE, "< $input") or printErr("$input: $!"); + my $content = ''; + while (my $line = <FILE>) { + $content .= $line; + } + close(*FILE); + + my $listener = AutoLaTeX::TeX::TeXDependencyAnalyzer->_new($input,$rootdir); + + my $parser = AutoLaTeX::TeX::TeXParser->new("$input", $listener); + + while (my ($k,$v) = each(%MACROS)) { + $parser->addTextModeMacro($k,$v); + $parser->addMathModeMacro($k,$v); + } + + $parser->parse( $content ); + + my %analysis = ( %{$listener->{'dependencies'}} ); + + foreach my $cat ('sty', 'tex') { + if ($analysis{$cat}) { + my @t = keys %{$analysis{$cat}}; + $analysis{$cat} = \@t; + } + } + + while (my ($bibdb,$bibdt) = each(%{$analysis{'biblio'}})) { + foreach my $cat ('bib', 'bst', 'bbx', 'cbx') { + if ($bibdt->{$cat}) { + my @t = keys %{$bibdt->{$cat}}; + $bibdt->{$cat} = \@t; + } + } + } + + $analysis{'biber'} = $listener->{'is_biber'}; + + return %analysis; +} + +sub _expandMacro($$@) : method { + my $self = shift; + my $parser = shift; + my $macro = shift; + + if ( $macro eq '\\include' || $macro eq '\\input' ) { + foreach my $param (@_) { + my $value = $param->{'text'}; + if ($value) { + my $texFile = "$value.tex"; + if (!File::Spec->file_name_is_absolute($texFile)) { + $texFile = File::Spec->catfile($self->{'dirname'}, "$texFile"); + } + if (-f "$texFile") { + $self->{'dependencies'}{'tex'}{$texFile} = 1; + } + } + } + } + elsif ( $macro eq '\\makeindex' || $macro eq '\\printindex' ) { + $self->_addidxreference(@_); + } + elsif ( $macro eq '\\makeglossaries' || $macro eq '\\printglossaries' || $macro eq '\\printglossary' ) { + $self->{'dependencies'}{'glo'} = 1; + } + elsif ( $macro eq '\\usepackage' || $macro eq '\\RequirePackage' ) { + my $sty = $_[1]{'text'}; + my $styFile = "$sty"; + if ($styFile !~ /\.sty$/i) { + $styFile .= ".sty"; + } + if ($styFile eq 'multibib.sty') { + $self->{'is_multibib'} = 1; + } + if ($styFile eq 'biblatex.sty') { + $self->{'is_biblatex'} = 1; + # Parse the biblatex parameters + if ($_[0] && $_[0]->{'text'}) { + my @params = split(/\s*\,\s*/, trim($_[0]->{'text'} || '')); + foreach my $p (@params) { + my ($k, $v); + if ($p =~ /^([^=]+)\s*=\s*(.*?)\s*$/) { + $k = $1; + $v = $2 || ''; + } + else { + $k = $p; + $v = ''; + } + if ($k eq 'backend') { + $self->{'is_biber'} = ($v eq 'biber'); + } + elsif ($k eq 'style') { + my $bbxFile = "$v"; + if ($bbxFile !~ /\.bbx$/i) { + $bbxFile .= ".bbx"; + } + if (!File::Spec->file_name_is_absolute($bbxFile)) { + $bbxFile = File::Spec->catfile($self->{'dirname'}, "$bbxFile"); + } + if (-f "$bbxFile") { + $self->{'dependencies'}{'biblio'}{''}{'bbx'}{$bbxFile} = 1; + } + my $cbxFile = "$v"; + if ($cbxFile !~ /\.cbx$/i) { + $cbxFile .= ".cbx"; + } + if (!File::Spec->file_name_is_absolute($cbxFile)) { + $cbxFile = File::Spec->catfile($self->{'dirname'}, "$cbxFile"); + } + if (-f "$cbxFile") { + $self->{'dependencies'}{'biblio'}{''}{'cbx'}{$cbxFile} = 1; + } + } + elsif ($k eq 'bibstyle') { + my $bbxFile = "$v"; + if ($bbxFile !~ /\.bbx$/i) { + $bbxFile .= ".bbx"; + } + if (!File::Spec->file_name_is_absolute($bbxFile)) { + $bbxFile = File::Spec->catfile($self->{'dirname'}, "$bbxFile"); + } + if (-f "$bbxFile") { + $self->{'dependencies'}{'biblio'}{''}{'bbx'}{$bbxFile} = 1; + } + } + elsif ($k eq 'citestyle') { + my $cbxFile = "$v"; + if ($cbxFile !~ /\.cbx$/i) { + $cbxFile .= ".cbx"; + } + if (!File::Spec->file_name_is_absolute($cbxFile)) { + $cbxFile = File::Spec->catfile($self->{'dirname'}, "$cbxFile"); + } + if (-f "$cbxFile") { + $self->{'dependencies'}{'biblio'}{''}{'cbx'}{$cbxFile} = 1; + } + } + } + } + } + if (!File::Spec->file_name_is_absolute($styFile)) { + $styFile = File::Spec->catfile($self->{'dirname'}, "$styFile"); + } + if (-f "$styFile") { + $self->{'dependencies'}{'sty'}{"$styFile"} = 1; + } + } + elsif ($macro eq '\\documentclass' ) { + my $cls = $_[1]{'text'}; + my $clsFile = "$cls"; + if ($clsFile !~ /\.cls$/i) { + $clsFile .= ".cls"; + } + if (!File::Spec->file_name_is_absolute($clsFile)) { + $clsFile = File::Spec->catfile($self->{'dirname'}, "$clsFile"); + } + if (-f "$clsFile") { + $self->{'dependencies'}{'cls'} = [ "$clsFile" ]; + } + } + elsif ($macro =~ /^\\bibliographystyle(.*)$/s ) { + my $bibdb = $1; + $bibdb = $self->{'basename'} unless ($bibdb && $self->{'is_multibib'}); + foreach my $param (@_) { + my $value = $param->{'text'}; + if ($value) { + foreach my $svalue (split(/\s*,\s*/,trim($value))) { + if ($svalue) { + my $bstFile = "$svalue"; + if ($bstFile !~ /\.bst$/i) { + $bstFile .= ".bst"; + } + if (!File::Spec->file_name_is_absolute($bstFile)) { + $bstFile = File::Spec->catfile($self->{'dirname'}, "$bstFile"); + } + if (-f "$bstFile") { + $self->{'dependencies'}{'biblio'}{$bibdb}{'bst'}{$bstFile} = 1; + } + } + } + } + } + } + elsif ($macro =~ /^\\bibliography(.*)$/s) { + my $bibdb = $1; + $bibdb = $self->{'basename'} unless ($bibdb && $self->{'is_multibib'}); + $self->_addbibreference($bibdb,@_); + } + elsif ($macro eq '\\addbibresource') { + my $bibdb = $self->{'basename'}; + $self->_addbibreference($bibdb,@_); + } + return ''; +} + +sub _addidxreference($@) { + my $self = shift; + for my $param (@_) { + my $text = $param->{'text'} || ''; + if ($text =~ /name=([^,]+)/) { + my $name = "$1"; + if (!$self->{'dependencies'}{'idx'}) { + $self->{'dependencies'}{'idx'} = []; + } + push @{$self->{'dependencies'}{'idx'}}, $name; + } + } + if (!$self->{'dependencies'}{'idx'}) { + $self->{'dependencies'}{'idx'} = []; + } + push @{$self->{'dependencies'}{'idx'}}, ""; +} + +sub _addbibreference($@) { + my $self = shift; + my $bibdb = shift || ''; + foreach my $param (@_) { + my $value = $param->{'text'}; + if ($value) { + foreach my $svalue (split(/\s*,\s*/, $value)) { + if ($svalue) { + my $bibFile = "$svalue"; + if ($bibFile !~ /\.bib$/i) { + $bibFile .= ".bib"; + } + if (!File::Spec->file_name_is_absolute($bibFile)) { + $bibFile = File::Spec->catfile($self->{'dirname'}, "$bibFile"); + } + if (-f "$bibFile") { + $self->{'dependencies'}{'biblio'}{$bibdb}{'bib'}{$bibFile} = 1; + } + } + } + } + } +} + +sub _discoverMacroDefinition($$$$) : method { + my $self = shift; + my $parser = shift; + my $macro = shift; + my $special = shift; + if (!$special) { + if ($macro =~ /^bibliographystyle/s ) { + return '!{}'; + } + elsif ($macro =~ /^bibliography/s ) { + return '!{}'; + } + } + return undef; +} + +sub _new($$) : method { + my $proto = shift; + my $class = ref($proto) || $proto; + my $parent = ref($proto) && $proto ; + + my $self ; + if ( $parent ) { + %{$self} = %{$parent} ; + } + else { + $self = { + 'basename' => basename($_[0],'.tex'), + 'file' => $_[0], + 'dirname' => File::Spec->rel2abs($_[1]), + 'expandMacro' => \&_expandMacro, + 'discoverMacroDefinition' => \&_discoverMacroDefinition, + 'dependencies' => { + 'biblio' => {}, + 'tex' => {}, + 'sty' => {}, + 'cls' => [], + 'idx' => [], + }, + }; + } + bless( $self, $class ); + return $self; +} + +1; +__END__ +=back + +=head1 BUG REPORT AND FEEDBACK + +To report bugs, provide feedback, suggest new features, etc. visit the AutoLaTeX Project management page at <http://www.arakhne.org/autolatex/> or send email to the author at L<galland@arakhne.org>. + +=head1 LICENSE + +S<GNU Public License (GPL)> + +=head1 COPYRIGHT + +S<Copyright (c) 2013 Stéphane Galland E<lt>galland@arakhne.orgE<gt>> + +=head1 SEE ALSO + +L<autolatex-dev> |