<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;"># autolatex - TeXDependencyAnalyzer.pm
# Copyright (C) 2013-17  Stephane Galland &lt;galland@arakhne.org&gt;
#
# 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&lt;use AutoLaTeX::TeX::TeXDependencyAnalyzer;&gt;.

=head1 FUNCTIONS

The provided functions are:

=over 4

=cut
package AutoLaTeX::TeX::TeXDependencyAnalyzer;

$VERSION = '7.0';
@ISA = ('Exporter');
@EXPORT = qw( &amp;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'			=&gt; '!{}',
	'include'		=&gt; '!{}',
	'makeindex'		=&gt; '[]',
	'printindex'		=&gt; '[]',
	'makeglossaries'	=&gt; '',
	'printglossaries'	=&gt; '',
	'printglossary'		=&gt; '![]',
	'usepackage'		=&gt; '![]!{}',
	'RequirePackage'	=&gt; '![]!{}',
	'documentclass'		=&gt; '![]!{}',
	'addbibresource'	=&gt; '![]!{}',
	);

=pod

=item B&lt;getDependenciesOfTeX($)&gt;

Parse a TeX file and detect the included files.

=over 4

=item * C&lt;file&gt; is the name of the TeX file to parse.

=item * C&lt;dir&gt; is the reference directory for the relative path.

=back

I&lt;Returns:&gt; the included files from the TeX file into an associative array.

=cut
sub getDependenciesOfTeX($$) {
	my $input = shift;
	my $rootdir = shift;
	
	local *FILE;
	open(*FILE, "&lt; $input") or printErr("$input: $!");
	my $content = '';
	while (my $line = &lt;FILE&gt;) {
		$content .= $line;
	}
	close(*FILE);

	my $listener = AutoLaTeX::TeX::TeXDependencyAnalyzer-&gt;_new($input,$rootdir);

	my $parser = AutoLaTeX::TeX::TeXParser-&gt;new("$input", $listener);

	while (my ($k,$v) = each(%MACROS)) {
		$parser-&gt;addTextModeMacro($k,$v);
		$parser-&gt;addMathModeMacro($k,$v);
	}

	$parser-&gt;parse( $content );

	my %analysis = ( %{$listener-&gt;{'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-&gt;{$cat}) {
				my @t = keys %{$bibdt-&gt;{$cat}};
				$bibdt-&gt;{$cat} = \@t;
			}
		}
	}

	$analysis{'biber'} = $listener-&gt;{'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-&gt;{'text'};
			if ($value) {
				my $texFile = "$value.tex";
				if (!File::Spec-&gt;file_name_is_absolute($texFile)) {
					$texFile = File::Spec-&gt;catfile($self-&gt;{'dirname'}, "$texFile");
				}
				if (-f "$texFile") {
					$self-&gt;{'dependencies'}{'tex'}{$texFile} = 1;
				}
			}
		}
	}
	elsif ( $macro eq '\\makeindex' || $macro eq '\\printindex' ) {
		$self-&gt;_addidxreference(@_);
	}
	elsif ( $macro eq '\\makeglossaries' || $macro eq '\\printglossaries' || $macro eq '\\printglossary' ) {
		$self-&gt;{'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-&gt;{'is_multibib'} = 1;
		}
		if ($styFile eq 'biblatex.sty') {
			$self-&gt;{'is_biblatex'} = 1;
			# Parse the biblatex parameters
			if ($_[0] &amp;&amp; $_[0]-&gt;{'text'}) {
				my @params = split(/\s*\,\s*/, trim($_[0]-&gt;{'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-&gt;{'is_biber'} = ($v eq 'biber');
					}
					elsif  ($k eq 'style') {
						my $bbxFile = "$v";
						if ($bbxFile !~ /\.bbx$/i) {
							$bbxFile .= ".bbx";
						}
						if (!File::Spec-&gt;file_name_is_absolute($bbxFile)) {
							$bbxFile = File::Spec-&gt;catfile($self-&gt;{'dirname'}, "$bbxFile");
						}
						if (-f "$bbxFile") {
							$self-&gt;{'dependencies'}{'biblio'}{''}{'bbx'}{$bbxFile} = 1;
						}
						my $cbxFile = "$v";
						if ($cbxFile !~ /\.cbx$/i) {
							$cbxFile .= ".cbx";
						}
						if (!File::Spec-&gt;file_name_is_absolute($cbxFile)) {
							$cbxFile = File::Spec-&gt;catfile($self-&gt;{'dirname'}, "$cbxFile");
						}
						if (-f "$cbxFile") {
							$self-&gt;{'dependencies'}{'biblio'}{''}{'cbx'}{$cbxFile} = 1;
						}
					}
					elsif ($k eq 'bibstyle') {
						my $bbxFile = "$v";
						if ($bbxFile !~ /\.bbx$/i) {
							$bbxFile .= ".bbx";
						}
						if (!File::Spec-&gt;file_name_is_absolute($bbxFile)) {
							$bbxFile = File::Spec-&gt;catfile($self-&gt;{'dirname'}, "$bbxFile");
						}
						if (-f "$bbxFile") {
							$self-&gt;{'dependencies'}{'biblio'}{''}{'bbx'}{$bbxFile} = 1;
						}
					}
					elsif ($k eq 'citestyle') {
						my $cbxFile = "$v";
						if ($cbxFile !~ /\.cbx$/i) {
							$cbxFile .= ".cbx";
						}
						if (!File::Spec-&gt;file_name_is_absolute($cbxFile)) {
							$cbxFile = File::Spec-&gt;catfile($self-&gt;{'dirname'}, "$cbxFile");
						}
						if (-f "$cbxFile") {
							$self-&gt;{'dependencies'}{'biblio'}{''}{'cbx'}{$cbxFile} = 1;
						}
					}
				}
			}
		}
		if (!File::Spec-&gt;file_name_is_absolute($styFile)) {
			$styFile = File::Spec-&gt;catfile($self-&gt;{'dirname'}, "$styFile");
		}
		if (-f "$styFile") {
			$self-&gt;{'dependencies'}{'sty'}{"$styFile"} = 1;
		}
	}
	elsif ($macro eq '\\documentclass' ) {
		my $cls = $_[1]{'text'};
		my $clsFile = "$cls";
		if ($clsFile !~ /\.cls$/i) {
			$clsFile .= ".cls";
		}
		if (!File::Spec-&gt;file_name_is_absolute($clsFile)) {
			$clsFile = File::Spec-&gt;catfile($self-&gt;{'dirname'}, "$clsFile");
		}
		if (-f "$clsFile") {
			$self-&gt;{'dependencies'}{'cls'} = [ "$clsFile" ];
		}
	}
	elsif ($macro =~ /^\\bibliographystyle(.*)$/s ) {
		my $bibdb = $1;
		$bibdb = $self-&gt;{'basename'} unless ($bibdb &amp;&amp; $self-&gt;{'is_multibib'});
		foreach my $param (@_) {
			my $value = $param-&gt;{'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-&gt;file_name_is_absolute($bstFile)) {
							$bstFile = File::Spec-&gt;catfile($self-&gt;{'dirname'}, "$bstFile");
						}
						if (-f "$bstFile") {
							$self-&gt;{'dependencies'}{'biblio'}{$bibdb}{'bst'}{$bstFile} = 1;
						}
					}
				}
			}
		}
	}
	elsif ($macro =~ /^\\bibliography(.*)$/s) {
		my $bibdb = $1;
		$bibdb = $self-&gt;{'basename'} unless ($bibdb &amp;&amp; $self-&gt;{'is_multibib'});
		$self-&gt;_addbibreference($bibdb,@_);
	}
	elsif ($macro eq '\\addbibresource') {
		my $bibdb = $self-&gt;{'basename'};
		$self-&gt;_addbibreference($bibdb,@_);
	}
	return '';
}

sub _addidxreference($@) {
	my $self = shift;
	for my $param (@_) {
		my $text = $param-&gt;{'text'} || '';
		if ($text =~ /name=([^,]+)/) {
			my $name = "$1";
			if (!$self-&gt;{'dependencies'}{'idx'}) {
				$self-&gt;{'dependencies'}{'idx'} = [];
			}
			push @{$self-&gt;{'dependencies'}{'idx'}}, $name;
		}
	}
	if (!$self-&gt;{'dependencies'}{'idx'}) {
		$self-&gt;{'dependencies'}{'idx'} = [];
	}
	push @{$self-&gt;{'dependencies'}{'idx'}}, "";
}

sub _addbibreference($@) {
	my $self = shift;
	my $bibdb = shift || '';
	foreach my $param (@_) {
		my $value = $param-&gt;{'text'};
		if ($value) {
			foreach my $svalue (split(/\s*,\s*/, $value)) {
				if ($svalue) {
					my $bibFile = "$svalue";
					if ($bibFile !~ /\.bib$/i) {
						$bibFile .= ".bib";
					}
					if (!File::Spec-&gt;file_name_is_absolute($bibFile)) {
						$bibFile = File::Spec-&gt;catfile($self-&gt;{'dirname'}, "$bibFile");
					}
					if (-f "$bibFile") {
						$self-&gt;{'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) &amp;&amp; $proto ;

	my $self ;
	if ( $parent ) {
		%{$self} = %{$parent} ;
	}
	else {
		$self = {
			'basename' =&gt; basename($_[0],'.tex'),
			'file' =&gt; $_[0],
			'dirname' =&gt; File::Spec-&gt;rel2abs($_[1]),
			'expandMacro' =&gt; \&amp;_expandMacro,
			'discoverMacroDefinition' =&gt; \&amp;_discoverMacroDefinition,
			'dependencies' =&gt; {
				'biblio' =&gt; {},
				'tex' =&gt; {},
				'sty' =&gt; {},
				'cls' =&gt; [],
				'idx' =&gt; [],
			},
		};
	}
	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 &lt;http://www.arakhne.org/autolatex/&gt; or send email to the author at L&lt;galland@arakhne.org&gt;.

=head1 LICENSE

S&lt;GNU Public License (GPL)&gt;

=head1 COPYRIGHT

S&lt;Copyright (c) 2013 StÃŠphane Galland E&lt;lt&gt;galland@arakhne.orgE&lt;gt&gt;&gt;

=head1 SEE ALSO

L&lt;autolatex-dev&gt;
</pre></body></html>