summaryrefslogtreecommitdiff
path: root/support/autolatex/pm/AutoLaTeX/TeX/TeXDependencyAnalyzer.pm
diff options
context:
space:
mode:
Diffstat (limited to 'support/autolatex/pm/AutoLaTeX/TeX/TeXDependencyAnalyzer.pm')
-rw-r--r--support/autolatex/pm/AutoLaTeX/TeX/TeXDependencyAnalyzer.pm393
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>