# autolatex - Main.pm
# Copyright (C) 1998-16 Stephane Galland <
[email protected]>
#
# 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
Main.pm - Main program
=head1 FUNCTIONS
The provided functions are:
=over 4
=cut
package AutoLaTeX::Core::Main;
$VERSION = '37.0';
$COPYRIGHT_YEAR = '2016';
@ISA = ('Exporter');
@EXPORT = qw( &analyzeCommandLineOptions &mainProgram &detectMainTeXFile ) ;
@EXPORT_OK = qw();
require 5.014;
use strict;
use utf8;
use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION $COPYRIGHT_YEAR);
use Getopt::Long;
use File::Basename;
use AutoLaTeX::Core::Util;
use AutoLaTeX::Core::Config;
use AutoLaTeX::Core::OS;
use AutoLaTeX::Core::IntUtils;
use AutoLaTeX::TeX::DocumentDetector;
#------------------------------------------------------
#
# FUNCTION: analyze the command line options and update the configuration accordingly
#
#------------------------------------------------------
sub analyzeCommandLineOptions(\%) {
printDbg(_T("Reading command line options"));
my $realcfg = $_[0];
$_[0]->{'__private__'}{'CLI.warning level'} = 1;
$_[0]->{'__private__'}{'config.command line'} = {};
my $cfg = \%{$_[0]->{'__private__'}{'config.command line'}};
my $debugLevel = getDebugLevel();
Getopt::Long::Configure ("bundling");
if (!GetOptions(
'auto!' => sub { $cfg->{'generation.generate images'} = ($_[1] ? 'yes' : 'no'); },
'asyncview!' => sub { $cfg->{'viewer.asynchronous run'} = ($_[1] ? 'yes' : 'no'); },
'imgdirectory=s' => sub { $cfg->{'generation.image directory'} = $_[1]; },
'continuous:i' => sub {
my $duration = int($_[1]);
$duration = 0 if ($duration<0);
$realcfg->{'__private__'}{'action.continuous mode'} = $duration;
$cfg->{'viewer.asynchronous run'} = 'true';
},
'nocontinuous' => sub {
delete $realcfg->{'__private__'}{'action.continuous mode'};
},
'createconfig:s' => \$realcfg->{'__private__'}{'action.create config file'},
'createist' => \$realcfg->{'__private__'}{'action.create ist file'},
'debug' => \$realcfg->{'__private__'}{'action.create ist file'},
'defaultist' => sub {
$cfg->{'generation.makeindex style'} = '@system';
},
'directory=s' => sub {
if (!$realcfg->{'__private__'}{'action.input directories'}) {
$realcfg->{'__private__'}{'action.input directories'} = [];
}
push @{$realcfg->{'__private__'}{'action.input directories'}}, $_[1];
},
'dvi' => sub {
$cfg->{'generation.generation type'} = 'dvi';
},
'biblio!' => sub {
$cfg->{'generation.biblio'} = ($_[1] ? 'yes' : 'no');
$realcfg->{'__private__'}{'CLI.biblio'} = ($_[1] ? 'yes' : 'no'); # For makeflat action
},
'exclude=s' => sub {
foreach my $module (split /\s*,\s*/,$_[1]) {
$module =~ s/[^a-zA-Z0-9_+-]+//g;
$cfg->{lc("$module").".include module"} = 'no';
}
},
'f=s' => \$realcfg->{'__private__'}{'input.latex file'},
'file=s' => \$realcfg->{'__private__'}{'input.latex file'},
'file-line-warning' => \$realcfg->{'__private__'}{'CLI.is extended tex warnings'},
'fixconfig:s' => sub {
$realcfg->{'__private__'}{'action.fix config file'} = $_[1];
$realcfg->{'__private__'}{'action.fix config file'} = File::Spec->rel2abs($realcfg->{'__private__'}{'action.fix config file'}) if ($realcfg->{'__private__'}{'action.fix config file'});
},
'gloss!' => sub { $cfg->{'generation.makeglossaries'} = ($_[1] ? 'yes' : 'no'); },
'help|?' => sub { showManual(getAutoLaTeXDocDir(),"autolatex.pod"); },
'I=s@' => sub {
if (!$cfg->{'generation.translator include path'}) {
$cfg->{'generation.translator include path'} = [];
}
push @{$cfg->{'generation.translator include path'}}, $_[1];
},
'include=s' => sub {
my $sep = getPathListSeparator();
foreach my $module (split /\s*$sep\s*/,$_[1]) {
$module =~ s/[^a-zA-Z0-9_+-]+//g;
$cfg->{lc("$module").".include module"} = 'yes';
}
},
'index:s' => sub {
if ($_[1]) {
$cfg->{'generation.makeindex style'} = $_[1];
}
else {
$cfg->{'generation.makeindex style'} = ['@detect','@none'];
}
},
'latex' => sub {
$cfg->{'generation.tex compiler'} = 'latex';
},
'lualatex' => sub {
$cfg->{'generation.tex compiler'} = 'lualatex';
},
'noindex' => sub {
delete $cfg->{'generation.makeindex style'};
},
'noview' => sub { $cfg->{'viewer.view'} = 'no'; },
'pdf' => sub { $cfg->{'generation.generation type'} = 'pdf'; },
'pdflatex' => sub {
$cfg->{'generation.tex compiler'} = 'pdflatex';
},
'postcompilationruns=i' => sub {
my $number = int($_[1]);
$number = 1 if ($number<1);
$cfg->{'generation.post compilation runs'} = $number;
},
'progress:s' => sub {
if ($_[1] eq 'n') {
$realcfg->{'__private__'}{'action.show progress'} = 'n';
}
else {
$realcfg->{'__private__'}{'action.show progress'} = 'r';
}
},
'ps' => sub {
$cfg->{'generation.generation type'} = 'ps';
},
'quiet' => sub { $debugLevel = 0; },
'search-project-from=s' => sub {
$realcfg->{'__private__'}{'action.search project from'} = $_[1];
},
'set=s%' => sub { $cfg->{'generation.set'}{$_[1]} = $_[2]; },
'stdout' => sub { redirectToSTDOUT(); },
'stderr' => sub { redirectToSTDERR(); },
'synctex!' => sub { $cfg->{'generation.synctex'} = ($_[1] ? 'yes' : 'no'); },
'xelatex' => sub {
$cfg->{'generation.tex compiler'} = 'xelatex';
},
'v+' => \$debugLevel,
'version' => sub { if (getAutoLaTeXLaunchingName() ne getAutoLaTeXName()) {
print formatText(_T("{} {} ({}) - {} platform\n(c) 1998-{} Stephane GALLAND <galland\@arakhne.org> (under GPL)\n"),
getAutoLaTeXLaunchingName(),
getAutoLaTeXVersion(),
getAutoLaTeXName(),
getOperatingSystem(),
$COPYRIGHT_YEAR);
}
else {
print formatText(_T("{} {} - {} platform\n(c) 1998-{} Stephane GALLAND <galland\@arakhne.org> (under GPL)\n"),
getAutoLaTeXLaunchingName(),
getAutoLaTeXVersion(),
getOperatingSystem(),
$COPYRIGHT_YEAR);
}
exit(0); },
'view:s' => sub {
$cfg->{'viewer.view'} = 'yes';
if ($_[1]) {
$cfg->{'viewer.viewer'} = $_[1];
}
},
'W' => sub {
$realcfg->{'__private__'}{'CLI.warning level'}++;
},
'Wall' => sub {
$realcfg->{'__private__'}{'CLI.warning level'} = 2;
},
'Wnone' => sub {
$realcfg->{'__private__'}{'CLI.warning level'} = 0;
},
)) {
exit(255);
}
setDebugLevel($debugLevel);
}
#------------------------------------------------------
#
# FUNCTION: Seach for a TeX main file in the current directory
#
# Requires $_[0]->{__private__}{output.directory}
# Set $_[0]->{__private__}{input.latex file}
#
#------------------------------------------------------
sub detectMainTeXFile(\%) {
my $configuration = shift;
local *DIR;
opendir(*DIR,$configuration->{'__private__'}{'output.directory'}) or printErr($configuration->{'__private__'}{'output.directory'}.":","$!");
my @texfiles = ();
while (my $subfile = readdir(*DIR)) {
if ((!isIgnorableDirectory($subfile)) && $subfile =~ /\.tex$/i && $subfile ne 'autolatex_autogenerated.tex') {
push @texfiles, "$subfile";
}
}
closedir(*DIR);
if (@texfiles==1) {
my $basename = pop @texfiles;
$configuration->{'__private__'}{'input.latex file'} =
File::Spec->catfile(
$configuration->{'__private__'}{'output.directory'},
$basename);
printDbg(formatText(_T("Selecting TeX file '{}'"), $basename));
}
else {
printDbg(formatText(_T("Detecting several TeX files: {}"),join(' ',@texfiles)));
# Issue #9: try to detect the file with the \documentclass
my @documents = ();
foreach my $file (@texfiles) {
if (isLaTeXDocument($file)) {
push @documents, $file;
}
}
if (@documents==1) {
my $basename = pop @documents;
$configuration->{'__private__'}{'input.latex file'} =
File::Spec->catfile(
$configuration->{'__private__'}{'output.directory'},
$basename);
printDbg(formatText(_T("Selecting TeX file '{}'"), $basename));
}
}
}
#------------------------------------------------------
#
# FUNCTION: Main program
#
#------------------------------------------------------
sub mainProgram(;$) {
my $exitOnError = shift;
$exitOnError = 1 if (!defined($exitOnError));
# Get system and user configurations
my %configuration = readConfiguration();
# Put the internationalization values in the configuration
$configuration{'__private__'}{'internationalization'}{'locale'} = getCurrentLocale();
$configuration{'__private__'}{'internationalization'}{'language'} = getCurrentLanguage();
$configuration{'__private__'}{'internationalization'}{'codeset'} = getCurrentCodeset();
$configuration{'__private__'}{'internationalization'}{'domains'} = getActiveTextDomains();
# Analyze and apply the command line
analyzeCommandLineOptions(%configuration);
# -- Bug fix: avoid the "No TeX file found" error message to be
# displayed when a non-generation action was requested
# on the command line
if (($exitOnError)&&
(($configuration{'__private__'}{'action.fix config file'})||
($configuration{'__private__'}{'action.create config file'})||
($configuration{'__private__'}{'action.create ist file'}))) {
$exitOnError = 0;
}
# Search for the root directory
if ($configuration{'__private__'}{'action.search project from'}) {
my $file = $configuration{'__private__'}{'action.search project from'};
if (-e "$file" && ! -d "$file") {
$file = dirname($file);
}
$file = File::Spec->rel2abs($file);
my $autolatex_dir = undef;
my $parent = dirname($file);
while (!$autolatex_dir && $parent && $file && $parent ne $file) {
my $cfgFile = getProjectConfigFilename($file);
if (-e "$cfgFile") {
$autolatex_dir = $file;
}
$file = $parent;
$parent = dirname($file);
}
if ($autolatex_dir && -d "$autolatex_dir") {
chdir($autolatex_dir) or printErr("$autolatex_dir: $!");
}
}
# detect main TeX file
$configuration{'__private__'}{'output.directory'} = File::Spec->curdir();
$configuration{'__private__'}{'output.latex basename'} = undef;
if (!$configuration{'__private__'}{'input.latex file'}) {
detectMainTeXFile(%configuration);
}
# read project's configuration
{
my $projectConfigFilename = getProjectConfigFilename($configuration{'__private__'}{'output.directory'});
if ( -f "$projectConfigFilename") {
readConfigFile("$projectConfigFilename",%configuration);
# Remove the main intput filename
if (exists $configuration{'generation.main file'}) {
if (!$configuration{'__private__'}{'input.latex file'}) {
$configuration{'__private__'}{'input.latex file'} = basename($configuration{'generation.main file'});
printDbg(formatText(_T("Detecting TeX file from project's configuration: '{}'"),$configuration{'__private__'}{'input.latex file'}));
}
delete $configuration{'generation.main file'};
}
$configuration{'__private__'}{'input.project directory'} = dirname("$projectConfigFilename");
}
}
if (!$configuration{'__private__'}{'input.latex file'}) {
$configuration{'__private__'}{'input.latex file'} = 'Main.tex';
}
# final project main file management
if ($configuration{'__private__'}{'input.latex file'}) {
# check its value
if (-f $configuration{'__private__'}{'input.latex file'}) {
$configuration{'__private__'}{'output.directory'} = dirname($configuration{'__private__'}{'input.latex file'});
$configuration{'__private__'}{'output.latex basename'} = basename($configuration{'__private__'}{'input.latex file'});
$configuration{'__private__'}{'output.latex basename'} =~ s/\.tex$//i;
}
elsif (-f $configuration{'__private__'}{'input.latex file'}.".tex") {
$configuration{'__private__'}{'input.latex file'} .= ".tex";
$configuration{'__private__'}{'output.directory'} = dirname($configuration{'__private__'}{'input.latex file'});
$configuration{'__private__'}{'output.latex basename'} = basename($configuration{'__private__'}{'input.latex file'});
}
}
if ($configuration{'__private__'}{'input.latex file'}) {
printDbg(formatText(_T("Using TeX file '{}'"),$configuration{'__private__'}{'input.latex file'}));
}
elsif ($exitOnError) {
printErr(formatText(_T("No LaTeX file found nor specified for the directory '{}'.\n You must specify one on the command line option -f, or set the the variable 'generation.main file' in your configuration file, rename one of your files 'Main.tex'."), $configuration{'__private__'}{'output.directory'}));
}
# now apply the command line options into the configuration
{
printDbg(_T("Applying command line options"));
if ($configuration{'__private__'}{'config.command line'}) {
while (my ($k,$v) = each(%{$configuration{'__private__'}{'config.command line'}})) {
if (isArray($v)) {
if (!$configuration{"$k"}) {
$configuration{"$k"} = $v;
}
elsif (isHash($configuration{"$k"})) {
for(my $i=0; $i<$#{$v}; $i=$i+2) {
$configuration{"$k"}{$v->[$i]} = $v->[$i+1];
}
}
elsif (isArray($configuration{"$k"})) {
push @{$configuration{"$k"}}, @{$v};
}
else {
unshift @{$v}, $configuration{"$k"};
$configuration{"$k"} = $v;
}
}
elsif (isHash($v)) {
if (!$configuration{"$k"}) {
$configuration{"$k"} = $v;
}
elsif (isArray($configuration{"$k"})) {
push @{$configuration{"$k"}}, @{$v};
}
elsif (isHash($configuration{"$k"})) {
while (my ($key,$val) = each(%{$v})) {
$configuration{"$k"}{$key} = $val;
}
}
else {
$v->{$configuration{"$k"}} = undef;
$configuration{"$k"} = $v;
}
}
else {
$configuration{"$k"} = $v;
}
}
if ($configuration{'generation.set'}) {
while (my ($k,$v) = each(%{$configuration{'generation.set'}})) {
$configuration{$k} = $v;
}
delete $configuration{'generation.set'}
}
delete $configuration{'__private__'}{'config.command line'};
}
}
# check MakeIndex parameters
if ($configuration{'generation.makeindex style'}) {
if (!isArray($configuration{'generation.makeindex style'})) {
$configuration{'generation.makeindex style'} = [$configuration{'generation.makeindex style'}];
}
foreach my $isttype (@{$configuration{'generation.makeindex style'}}) {
if ($isttype) {
if ($isttype eq '@detect') {
# Seach for a MakeIndex style file in the current directory
local *DIR;
opendir(*DIR,$configuration{'__private__'}{'output.directory'}) or printErr($configuration{'__private__'}{'output.directory'}.":","$!");
my @istfiles = ();
while (my $subfile = readdir(*DIR)) {
if (!isIgnorableDirectory($subfile) && $subfile =~ /\.ist$/i) {
push @istfiles, File::Spec->catfile(
$configuration{'__private__'}{'output.directory'},
"$subfile");
}
}
closedir(*DIR);
if (@istfiles==1) {
$configuration{'__private__'}{'output.ist file'} = File::Spec->rel2abs(pop @istfiles);
printDbg(formatText(_T("Selecting project's style for MakeIndex: {}"), $configuration{'__private__'}{'output.ist file'}));
}
else {
delete $configuration{'__private__'}{'output.ist file'};
printDbg(formatText(_T("Unable to selected a project's style for MakeIndex: no file or too many .ist files in directory {}"), $configuration{'__private__'}{'output.directory'}));
}
}
elsif ($isttype eq '@system') {
$configuration{'__private__'}{'output.ist file'} = getSystemISTFilename();
printDbg(formatText(_T("Selecting the system default style for MakeIndex")));
}
elsif ($isttype eq '@none') {
delete $configuration{'__private__'}{'output.ist file'};
printDbg(formatText(_T("Unselecting any style for MakeIndex")));
}
if (($configuration{'__private__'}{'output.ist file'})&&
(-r $configuration{'__private__'}{'output.ist file'})) {
last;
}
else {
delete $configuration{'__private__'}{'output.ist file'};
}
}
}
}
# set the project's directory if never set before
if (!$configuration{'__private__'}{'input.project directory'} &&
$configuration{'__private__'}{'input.latex file'}) {
$configuration{'__private__'}{'input.project directory'} = File::Spec->rel2abs(dirname($configuration{'__private__'}{'input.latex file'}));
}
# Set the directory of the pictures to a default value if not defined in
# the configuration nor given on the CLI
if (! defined($configuration{'generation.image directory'})) {
$configuration{'generation.image directory'} = $configuration{'__private__'}{'input.project directory'};
}
return %configuration;
}
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<
[email protected]>.
=head1 LICENSE
S<GNU Public License (GPL)>
=head1 COPYRIGHT
S<Copyright (c) 1998-2016 Stéphane Galland E<lt>
[email protected]<gt>>
=head1 SEE ALSO
L<autolatex-dev>