# autolatex - TeXDependencyAnalyzer.pm # Copyright (C) 2013-17 Stephane Galland # # 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. =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 Parse a TeX file and detect the included files. =over 4 =item * C is the name of the TeX file to parse. =item * C is the reference directory for the relative path. =back I 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 = ) { $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 or send email to the author at L. =head1 LICENSE S =head1 COPYRIGHT Sgalland@arakhne.orgE> =head1 SEE ALSO L