From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- support/autolatex/pm/AutoLaTeX/Core/Config.pm | 876 ++++++++++++++++++++++++++ 1 file changed, 876 insertions(+) create mode 100644 support/autolatex/pm/AutoLaTeX/Core/Config.pm (limited to 'support/autolatex/pm/AutoLaTeX/Core/Config.pm') diff --git a/support/autolatex/pm/AutoLaTeX/Core/Config.pm b/support/autolatex/pm/AutoLaTeX/Core/Config.pm new file mode 100644 index 0000000000..9859ee8dec --- /dev/null +++ b/support/autolatex/pm/AutoLaTeX/Core/Config.pm @@ -0,0 +1,876 @@ +# autolatex - Config.pm +# Copyright (C) 1998-15 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 + +Config.pm - Configuration Files + +=head1 DESCRIPTION + +Provides a set of utilities for manipulating cofiguration files. + +To use this library, type C. + +=head1 FUNCTIONS + +The provided functions are: + +=over 4 + +=cut +package AutoLaTeX::Core::Config; + +$VERSION = '36.0'; +@ISA = ('Exporter'); +@EXPORT = qw( &getProjectConfigFilename &getUserConfigFilename &getSystemConfigFilename + &getSystemISTFilename &readConfiguration &readConfigFile &getUserConfigDirectory + &cfgBoolean &doConfigurationFileFixing &cfgToBoolean &writeConfigFile + &readOnlySystemConfiguration &readOnlyUserConfiguration &readOnlyProjectConfiguration + &setInclusionFlags &reinitInclusionFlags &cfgIsBoolean &rebuiltConfigValue ) ; +@EXPORT_OK = qw(); + +require 5.014; +use strict; +use utf8; +use warnings; +use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION); + +use File::Spec; +use Config::Simple; + +use AutoLaTeX::Core::OS; +use AutoLaTeX::Core::Util; +use AutoLaTeX::Core::IntUtils; + +# Constant that is representing an empty string in the INI file. +# This constant is mandatory because Config::Simple does not +# support the empty values, such as "KEY =". In place, +# the INI file must contains something like "KEY = <<<>>>". +use constant EMPTY_INI_VALUE => '<<<>>>'; + +####################################################### +# Comments for the sections of the configuration file +my %SECTION_COMMENTS = ( + 'viewer' => _T("Configuration of the viewing functions"), + 'generation' => _T("Configuration of the generation functions"), + 'clean' => _T("Configuration of the several cleaning functions"), + 'scm' => _T("Configuration of the SCM functions"), + 'gtk' => _T("GTK interface configuration"), + 'qt' => _T("Qt interface configuration"), + 'windows' => _T("Windows interface configuration"), + 'macos' => _T("MacOS interface configuration"), + 'wxwidget' => _T("wxWidgets interface configuration"), +); + +####################################################### +# Comments for the public configuration entries +my %CONFIGURATION_COMMENTS = ( + # + # CLEAN + 'clean.files to clean' => _T( "List of additional files to remove when cleaning (shell ". + "wild cards are allowed). This list is used when the ". + "target 'clean' is invoked."), + 'clean.files to desintegrate' => _T( "List of additional files to remove when all cleaning ". + "(shell wild cards are allowed). This list is used when ". + "the target 'cleanall' is invoked."), + # GENERATION + 'generation.biblio' => _T( "Indicates if bibliography tool (bibtex,biber) should be run ('yes' or 'no')."), + 'generation.generate images' => _T( "Does the figures must be automatically generated ('yes' or 'no')?"), + 'generation.image directory' => _T( "Specify the directories inside which AutoLaTeX ". + "will find the pictures which must be processed ". + "by the translators. Each time this option is ". + "put on the command line, a directory is added ". + "inside the list of the directories to explore. ". + "The different paths are separated by the ". + "path-separator character (':' on Unix, ';' on ". + "Windows)"), + 'generation.main file' => _T( "Main filename (this option is only available in project's ". + "configuration files)."), + 'generation.generation type' => _T( "Type of generation.\n pdf : use pdflatex to create a ". + "PDF document."), + 'generation.makeglossaries' => _T( "Indicates if glossary tool (makeglossaries) should be run ('yes' or 'no')."), + 'generation.makeindex style' => _T( "Specify the style that must be used by makeindex.\n". + "Valid values are:\n if a filename ". + "was specified, AutoLaTeX assumes that it is the .ist ". + "file;\n \@system AutoLaTeX uses the system ". + "default .ist file (in AutoLaTeX distribution);\n". + " \@detect AutoLaTeX will tries to find a .ist ". + "file in the project's directory. If none was found, ". + "AutoLaTeX will not pass a style to makeindex;\n \@none". + " AutoLaTeX assumes that no .ist file must be ". + "passed to makeindex;\n AutoLaTeX assumes ". + "that no .ist file must be passed to makeindex."), + 'generation.post compilation runs' => _T( "Defines the minimal number of times the LaTeX compilation tools (usually ". + "pdflatex) is run during the last running stage of AutoLaTeX. The default value is 1."), + 'generation.translator include path' => _T( "Defines the paths from which the translators could be ". + "loaded. This is a list of paths separated by the path ". + "separator character used by your operating system: ':' ". + "on Unix platforms or ';' on Windows platforms for example."), + 'generation.synctex' => _T( "Indicates if the PDF document must be produced with the SyncTeX flag on or not. ". + "SyncTeX enables to link a PDF viewer (as evince) and a text editor (as Gedit). ". + "When you click inside one, the other is highlighting the line in its side."), + # SCM + 'scm.scm commit' => _T( "Tool to launch when a SCM commit action is requested. ". + "Basically the SCM tools are CVS, SVN, or GIT."), + 'scm.scm update' => _T( "Tool to launch when a SCM update action is requested. ". + "Basically the SCM tools are CVS, SVN, or GIT."), + # VIEWER + 'viewer.view' => _T( "Indicates if a viewer should be launch after the compilation. ". + "Valid values are 'yes' and 'no'."), + 'viewer.viewer' => _T( "Specify, if not commented,the command line of the viewer."), + 'viewer.asynchronous run' => _T( "Indicates if the viewer is launched in background, or not."), +); + +####################################################### +# Comments for the private configuration entries +# action.create config file +# action.create ist file +# action.fix config file +# config.command line +# input.project directory +# input.latex file +# output.directory +# ouput.ist file +# output.latex basename + + +=pod + +=item B + +Replies the name of a project's configuration file +which is located inside the given directory. + +I + +=over 8 + +=item * the components of the paths, each parameter is a directory in the path. + +=back + +I the configuration filename according to the current operating system rules. + +=cut +sub getProjectConfigFilename(@) { + my $operatingsystem = getOperatingSystem(); + if (("$operatingsystem" eq 'Unix')||(("$operatingsystem" eq 'Cygwin'))) { + return File::Spec->rel2abs(File::Spec->catfile(@_,".autolatex_project.cfg")); + } + else { + return File::Spec->rel2abs(File::Spec->catfile(@_,"autolatex_project.cfg")); + } +} + +=pod + +=item B + +Replies the name of a user's configuration file. + +I the configuration filename according to the current operating system rules. + +=cut +sub getUserConfigFilename() { + my $confdir = getUserConfigDirectory(); + if (-d "$confdir") { + return File::Spec->catfile("$confdir","autolatex.conf"); + } + my $operatingsystem = getOperatingSystem(); + if (("$operatingsystem" eq 'Unix')||(("$operatingsystem" eq 'Cygwin'))) { + return File::Spec->rel2abs(File::Spec->catfile($ENV{'HOME'},".autolatex")); + } + elsif ("$operatingsystem" eq 'Win32') { + return File::Spec->rel2abs(File::Spec->catfile("C:","Documents and Settings",$ENV{'USER'},"Local Settings","Application Data","autolatex.conf")); + } + else { + return File::Spec->rel2abs(File::Spec->catfile($ENV{'HOME'},"autolatex.conf")); + } +} + +=pod + +=item B + +Replies the name of a user's configuration directory. + +I the configuration directory according to the current operating system rules. + +=cut +sub getUserConfigDirectory() { + my $operatingsystem = getOperatingSystem(); + if (("$operatingsystem" eq 'Unix')||(("$operatingsystem" eq 'Cygwin'))) { + return File::Spec->rel2abs(File::Spec->catfile($ENV{'HOME'},".autolatex")); + } + elsif ("$operatingsystem" eq 'Win32') { + return File::Spec->rel2abs(File::Spec->catfile("C:","Documents and Settings",$ENV{'USER'},"Local Settings","Application Data","autolatex")); + } + else { + return File::Spec->rel2abs(File::Spec->catfile($ENV{'HOME'},"autolatex")); + } +} + +=pod + +=item B + +Replies the name of the configuration file for all users. + +I the configuration filename according to the current operating system rules. + +=cut +sub getSystemConfigFilename() { + return File::Spec->catfile(getAutoLaTeXDir(),"default.cfg"); +} + +=pod + +=item B + +Replies the name of the MakeIndex style file for all users. + +I the filename according to the current operating system rules. + +=cut +sub getSystemISTFilename() { + return File::Spec->catfile(getAutoLaTeXDir(),"default.ist"); +} + +=pod + +=item B + +Replies the Perl boolean value that corresponds to the specified +string. If the first parameter is not a valid boolean string, the second +parameter will be replied if it is specified; if not undef will be replied; + +The valid string are (case insensitive): S, S, S, S. + +I + +=over 8 + +=item * the value to test. + +=item * the data structure to fill + +=back + +I nothing + +=cut +sub cfgBoolean($;$) { + if ($_[0]) { + my $v = lc($_[0]); + return 1 if (($v eq 'yes')||($v eq 'true')); + return 0 if (($v eq 'no')||($v eq 'false')); + } + return $_[1]; +} + +=pod + +=item B + +Replies the configuration's file boolean value that corresponds to the specified +Perl boolean value. + +I + +=over 8 + +=item * the value to test. + +=back + +I nothing + +=cut +sub cfgToBoolean($) { + return ($_[0]) ? 'true' : 'false'; +} + +=pod + +=item B + +Replies if the specified string is a valid boolean string. + +I + +=over 8 + +=item * the value to test. + +=back + +I nothing + +=cut +sub cfgIsBoolean($) { + if (defined($_[0])) { + my $v = lc($_[0]); + return 1 + if (($v eq 'yes')||($v eq 'no')||($v eq 'true')||($v eq 'false')); + } + return 0; +} + +=pod + +=item B + +Replies the current configuration. The configuration +is extracted from the system configuration file +(from AutoLaTeX distribution) and from the user +configuration file. + +I a hashtable containing (attribute name, attribute value) pairs. +The attribute name could be S to describe the attribute inside +a section. + +=cut +sub readConfiguration() { + my %configuration = (); + my $systemFile = getSystemConfigFilename(); + my $userFile = getUserConfigFilename(); + readConfigFile("$systemFile",\%configuration); + readConfigFile("$userFile",\%configuration); + # Remove the main intput filename + if (exists $configuration{'generation.main file'}) { + delete $configuration{'generation.main file'}; + } + return %configuration; +} + +=pod + +=item B + +Replies the current configuration. The configuration +is extracted from the system configuration file +(from AutoLaTeX distribution) only. + +I a hashtable containing (attribute name, attribute value) pairs. +The attribute name could be S to describe the attribute inside +a section. + +=cut +sub readOnlySystemConfiguration(;$) { + my %configuration = (); + my $systemFile = getSystemConfigFilename(); + readConfigFile("$systemFile",\%configuration,$_[0]); + # Remove the main intput filename + if (exists $configuration{'generation.main file'}) { + delete $configuration{'generation.main file'}; + } + return %configuration; +} + +=pod + +=item B + +Replies the current configuration. The configuration +is extracted from the user configuration file +($HOME/.autolatex or $HOME/.autolatex/autolatex.conf) only. + +I a hashtable containing (attribute name, attribute value) pairs. +The attribute name could be S to describe the attribute inside +a section. + +=cut +sub readOnlyUserConfiguration(;$) { + my %configuration = (); + my $userFile = getUserConfigFilename(); + readConfigFile("$userFile",\%configuration,$_[0]); + # Remove the main intput filename + if (exists $configuration{'generation.main file'}) { + delete $configuration{'generation.main file'}; + } + return %configuration; +} + +=pod + +=item B + +Replies the current configuration. The configuration +is extracted from the project configuration file +($PROJECT_PATH/.autolatex_project.cfg) only. + +I a hashtable containing (attribute name, attribute value) pairs. +The attribute name could be S to describe the attribute inside +a section. + +=cut +sub readOnlyProjectConfiguration(@) { + my %configuration = (); + my $userFile = getProjectConfigFilename(@_); + if (-r "$userFile") { + readConfigFile("$userFile",\%configuration); + $configuration{'__private__'}{'input.project directory'} = File::Spec->catfile(@_); + return \%configuration; + } + return undef; +} + +=pod + +=item B + +Fill the configuration data structure from the specified file information. +The structure of the filled hashtable is a set of (attribute name, attribute value) pairs. +The attribute name could be S to describe the attribute inside +a section. + +I + +=over 8 + +=item * the name of the file to read + +=item * the data structure to fill + +=item * boolean value that indicates if a warning message should be ignored when an old fashion file was detected. + +=back + +I nothing + +=cut +sub readConfigFile($\%;$) { + my $filename = shift; + die('second parameter of readConfigFile() is not a hash') unless(isHash($_[0])); + printDbg(formatText(_T("Opening configuration file {}"),$filename)); + if (-r "$filename") { + my $cfgReader = new Config::Simple("$filename"); + if ($cfgReader) { + my %config = $cfgReader->vars(); + my $warningDisplayed = $_[1]; + while (my ($k,$v) = each (%config)) { + $k = lc("$k"); + if ($k !~ /^__private__/) { + ($k,$v) = ensureAccendentCompatibility("$k",$v,"$filename",$warningDisplayed); + $v = rebuiltConfigValue("$k",$v); + if ($v) { + $_[0]->{"$k"} = $v; + } + } + } + } + printDbg(_T("Succeed on reading")); + } + else { + printDbg(formatText(_T("Failed to read {}: {}"),$filename,$!)); + } + 1; +} + +# Put formatted comments inside an array +sub pushComment(\@$;$) { + my $limit = $_[2] || 60; + $limit = 1 unless ($limit>=1); + my @lines = split(/\n/, $_[1]); + foreach my $line (@lines) { + my @words = split(/\s+/, $line); + my $wline = ''; + if ($line =~ /^(\s+)/) { + $wline .= $1; + } + foreach my $w (@words) { + if ((length($wline)+length($w)+1)>$limit) { + push @{$_[0]}, "#$wline\n"; + if (length($w)>$limit) { + while (length($w)>$limit) { + push @{$_[0]}, "# ".substr($w,0,$limit)."\n"; + $w = substr($w,$limit); + } + } + $wline = " $w"; + } + else { + $wline .= " $w"; + } + } + if ($wline) { + push @{$_[0]}, "#$wline\n"; + } + } +} + +# Reformat the value to be written inside a configuration file. +# $_[0]: value name, +# $_[1]: value to validated. +sub serializeConfigValue($$) { + my $v = $_[1]; + if (isArray($v)) { + $v = ''; + foreach my $ev (@{$_[1]}) { + if ($v) { + $v .= ', '; + } + $v .= &serializeConfigValue($_[0], $ev); + } + } + elsif (isHash($v)) { + $v = ''; + while (my ($key, $value) = each(%{$_[1]})) { + if ($v) { + $v .= ', '; + } + $v .= "$key:{"; + $v .= &serializeConfigValue($_[0], $value); + $v .= "}"; + } + } + return $v; +} + +=pod + +=item B + +Write the specified configuration into a file. + +I + +=over 8 + +=item * the name of the file to write + +=item * the configuration data structure to write + +=item * (optional) indicates if the comments must be written + +=back + +I nothing + +=cut +sub writeConfigFile($\%;$) { + my $filename = shift; + die('second parameter of writeConfigFile() is not a hash') unless(isHash($_[0])); + + # Write the values + printDbg(formatText(_T("Writing configuration file {}"),$filename)); + printDbgIndent(); + my $cfgWriter = new Config::Simple(syntax=>'ini'); + my $has = 0; + while (my ($attr,$value) = each (%{$_[0]})) { + if ($value && $attr ne '__private__') { + $value = serializeConfigValue($attr, $value); + if ($value) { + $cfgWriter->param("$attr",$value); + $has = 1; + } + } + } + if ($has) { + $cfgWriter->write("$filename") or printErr($cfgWriter->error()); + + if ($_[1]) { + # Updating for comments + printDbg(_T("Adding configuration comments")); + local *CFGFILE; + open (*CFGFILE, "< $filename") or printErr("$filename:","$!"); + my @lines = (); + my $lastsection = undef; + while (my $l = ) { + if ($l =~ /^\s*\[\s*(.+?)\s*\]\s*$/) { + $lastsection = lc($1); + if ($SECTION_COMMENTS{"$lastsection"}) { + push @lines, "\n"; + pushComment @lines, $SECTION_COMMENTS{"$lastsection"}; + } + else { + push @lines, "\n"; + pushComment @lines, _T("Configuration of the translator")." '$lastsection'"; + } + } + elsif (($l =~ /^\s*(.*?)\s*=/)&&($lastsection)) { + my $attr = lc($1); + if ($CONFIGURATION_COMMENTS{"$lastsection.$attr"}) { + push @lines, "\n"; + pushComment @lines, $CONFIGURATION_COMMENTS{"$lastsection.$attr"}; + } + } + push @lines, $l; + } + close(*CFGFILE); + + printDbg(_T("Saving configuration comments")); + local *CFGFILE; + open (*CFGFILE, "> $filename") or printErr("$filename:","$!"); + print CFGFILE (@lines); + close(*CFGFILE); + } + } + else { + # Create an empty file + open (*CFGFILE, "> $filename") or printErr("$filename:","$!"); + close(*CFGFILE); + } + printDbgUnindent(); + 1; +} + +=pod + +=item B + +Fix the specified configuration file. + +I + +=over 8 + +=item * the name of the file to fix + +=back + +I nothing + +=cut +sub doConfigurationFileFixing($) { + my $filename = shift; + my %configuration = (); + + readConfigFile("$filename",%configuration,1); + + writeConfigFile("$filename",%configuration); + + 1; +} + +# Try to detect an old fashioned configuration file +# and fix the value +sub ensureAccendentCompatibility($$$$) { + my $k = $_[0]; + my $v = $_[1]; + my $changed = 0; + $v = '' unless (defined($v)); + + if ($k eq 'generation.bibtex') { + $k = 'generation.biblio'; + $changed = 1; + } + + if (!isArray($v)) { + # Remove comments on the same line as values + if ($v =~ /^\s*(.*?)\s*\#.*$/) { + $v = "$1" ; + $changed = 1; + } + + if ($v eq '@detect@system') { + $v = ['detect','system']; + $changed = 1; + } + } + + if (($changed)&&(!$_[3])) { + printWarn(formatText(_T("AutoLaTeX has detecting an old fashion syntax for the configuration file {}\nPlease regenerate this file with the command line option --fixconfig."), $_[2])); + $_[3] = 1; + } + + return ($k,$v); +} + +# Reformat the value from a configuration file to apply several rules +# which could not be directly applied by the configuration readed. +# $_[0]: value name, +# $_[1]: value to validated. +sub rebuiltConfigValue($$) { + my $v = $_[1]; + if (($_[0])&&($v)) { + my $v_str = trim($v); + # If the string "empty value" is detected, delete the value + if ($v_str eq EMPTY_INI_VALUE) { + $v = ''; + } + # Split the include path of translators + elsif ($_[0] eq 'generation.translator include path') { + my $sep = getPathListSeparator(); + if (isHash($v)) { + while (my ($key,$val) = each(%{$v})) { + my @tab = split(/\s*$sep\s*/,"$val"); + if (@tab>1) { + $v->{"$key"} = @tab; + } + else { + $v->{"$key"} = pop @tab; + } + } + } + else { + my @paths = (); + if (isArray($v)) { + foreach my $p (@{$v}) { + push @paths, split(/\s*$sep\s*/,"$p"); + } + } + else { + push @paths, split(/\s*$sep\s*/,"$v"); + } + if (@paths>1) { + $v = \@paths; + } + else { + $v = pop @paths; + } + } + } + } + return $v; +} + +=pod + +=item B + +Set the translator inclusion flags obtained from the configurations. + +This function assumed that the translator list is an hashtable of +(translator_name => { 'included' => { level => boolean } }) pairs. + +I + +=over 8 + +=item * the translator list. + +=item * the system configuration. + +=item * the user configuration. + +=item * the project configuration. + +=back + +I nothing + +=cut +sub setInclusionFlags(\%\%;\%\%) { + die('first parameter of setInclusionFlags() is not a hash') + unless (isHash($_[0])); + die('second parameter of setInclusionFlags() is not a hash') + unless (isHash($_[1])); + foreach my $trans (keys %{$_[0]}) { + if (!$_[0]->{"$trans"}{'included'}) { + $_[0]->{"$trans"}{'included'} = {}; + } + + if ((exists $_[1]->{"$trans.include module"})&&(cfgIsBoolean($_[1]->{"$trans.include module"}))) { + $_[0]->{"$trans"}{'included'}{'system'} = cfgBoolean($_[1]->{"$trans.include module"}); + } + else { + # On system level, a module which was not specified as not includable must + # be included even if it will cause conflicts + $_[0]->{"$trans"}{'included'}{'system'} = undef; + } + + if (($_[2])&& + (exists $_[2]->{"$trans.include module"})&& + (cfgIsBoolean($_[2]->{"$trans.include module"}))) { + $_[0]->{"$trans"}{'included'}{'user'} = cfgBoolean($_[2]->{"$trans.include module"}); + } + else { + $_[0]->{"$trans"}{'included'}{'user'} = undef; + } + + if (($_[3])&& + (exists $_[3]->{"$trans.include module"})&& + (cfgIsBoolean($_[3]->{"$trans.include module"}))) { + $_[0]->{"$trans"}{'included'}{'project'} = cfgBoolean($_[3]->{"$trans.include module"}); + } + else { + $_[0]->{"$trans"}{'included'}{'project'} = undef; + } + } +} + +=pod + +=item B + +Init the translator inclusion flags obtained from the configurations. + +This function assumed that the translator list is an hashtable of +(translator_name => { 'included' => { level => undef } }) pairs. + +I + +=over 8 + +=item * the translator list. + +=item * the system configuration. + +=item * the user configuration. + +=item * the project configuration. + +=back + +I nothing + +=cut +sub reinitInclusionFlags(\%\%;\%\%) { + die('first parameter of setInclusionFlags() is not a hash') + unless (isHash($_[0])); + die('second parameter of setInclusionFlags() is not a hash') + unless (isHash($_[1])); + foreach my $trans (keys %{$_[0]}) { + if (!$_[0]->{"$trans"}{'included'}) { + $_[0]->{"$trans"}{'included'} = {}; + } + + $_[0]->{"$trans"}{'included'}{'system'} = undef; + + if ($_[2]) { + $_[0]->{"$trans"}{'included'}{'user'} = undef; + } + + if ($_[3]) { + $_[0]->{"$trans"}{'included'}{'project'} = undef; + } + } +} + +1; +__END__ +=back + +=head1 BUG REPORT AND FEEDBACK + +To report bugs, provide feedback, suggest new features, etc. (in prefered order): a) visit the developer site on GitHub , b) visit the AutoLaTeX main page , or c) send email to the main author at galland@arakhne.org. + +=head1 LICENSE + +S + +=head1 COPYRIGHT + +Sgalland@arakhne.orgE> + +=head1 SEE ALSO + +L -- cgit v1.2.3