#!/usr/bin/env perl
# injection file written by latex plugin 2.1-SNAPSHOT

# This is valid Perl code but not yet .latexmkrc. 
# It is a template where the names of settings of this builder 
# must be replaced by their values. 
# As a result, a true .latexmkrc files arises which reflects the current settings of this builder. 

# The names to be replaced have the form $-[parameterName] or $-[functionName()] but without '-' 
# and both variants may only occur within double quoted literal Perl strings. 
# Although they look similarly, they are no variables to be substituted. 
# They are not allowed within single quoted strings 
# because if the java strings they represent contain a " this is always replaced by \" 
# before substituting. 
# This holds even if there is no variable substitution so " is superfluous and ' would do also from the point of view of Perl. 
# Perl interprets the sequence \" within double quoted strings as just " in the Perl string, 
# so that the Perl string is exactly what the original java string was. 

# A word on the difference between $-[parameterName] and $-[functionName()]: 
# Whereas the former is just the name of a setting, i.e. a parameter in class Settings, 
# the latter is the result of some computation based on the Settings. 
# As an example, $-[patternLatexMainFilePerl()] is the parameter $-[patternLatexMainFile] 
# which is a regex pattern in the java world, which is useless in this context, 
# whereas $-[patternLatexMainFilePerl()] is the translation into an equivalent Perl pattern. 


# The following is to check whether the pom really overwrites the settings in defaultSettingsAs.properties
# -q -b0<chkTexOptions>-q -b0 -L -H1</chkTexOptions>

# to create pdf via lualatex 
#$pdflatex = 'lualatex -file-line-error %O %S';

our ($pdf_mode, $lualatex, $bibtex_use, $bibtex, $max_repeat, $Pbase, 
    $cleanup_includes_cusdep_generated, $cleanup_includes_generated, $makeindex, $extra_rule_spec, 
    $compiling_cmd, $success_cmd, $warning_cmd, $failure_cmd);

# PDF-generating modes are:
# 0: do NOT generate a pdf version of the document. (default)
# 1: pdflatex, as specified by $pdflatex variable (still largely in use)
# 2: via postscript conversion, as specified by the $ps2pdf variable (useless)
# 3: via dvi conversion, as specified by the $dvipdf variable (useless)
# 4: lualatex, as specified by the $lualatex variable (best)
#    Note that we abuse that by replacing lualatex by a variable
# 5: xelatex, as specified by the $xelatex variable (second best)
# This is abused here, just to invoke $lualatex which leads to run_latex

# specifies creation of pdf via lualatex 
$pdf_mode = 4;


# If additional parameters must be passed to lualatex 
#'svg' package.
# It converts raw SVG files to the PDF+PDF_TEX combo using InkScape.
# $lualatex = "lualatex --shell-escape";

# note that -recorder is implicitly added by latexmk, 
# so may be duplicated, but no disadvantage 
# %O is the options (additional options passed by latexmk)
# %S source file (maybe %A and %B more appropriate: without ending)
#$lualatex = "lualatex -interaction=nonstopmode -synctex=1 -recorder -shell-escape %O %S";
$lualatex = "internal run_latex %A %O";

# superfluous for perl >=5.36 according to documentation, but does not work for me (perl 5.38?)
use v5.10; # by 2007 
use strict;
use warnings;


__PACKAGE__ eq 'main' or die "Not in main package!";


package SrcTargetDesc;

#=for tex \setcounter{secnumdepth}{3}

=head1 Package C<SrcTargetDesc>

The package contains meta information on the latex main file, 
and, provided reproduction is requested, on the PDF file to be reproduced. 
All these pieces of information need to be bundled, 
to make tem available to various functions without polluting the main package. 

Latex main files are identified via a regular expression pattern 
which provides various pieces of information: 

=over 4

=item * L</$docClass>, the document class given by C<documentclass> or C<documentstyle>

=item * L</%docMeta>, optional meta information given by C<DocumentMetadata>

=item * L</%withMagicComments> those settings which may be overwritten by a magic comment. 

=item * L</$doNeedVerification> whether L</%withMagicComments> defines a PDF standard which is verifyable by the specified verification tool. 

=item * L</$pdfFileDiff> a PDF file to be reproduced if reproduction is required and the file to be reproduced exists. 

=back

=cut

=head2 Attributes and their Getters

The attributes are all declared with C<my> so for external access getters are required. 

=cut

# TBD: distinguish between DocumentMetadata and SetKeys 


my %withMagicComments;

sub withMagicComments { my ($key) = @_; # key defaults to undef if not provided
  # if the key is given, return the according value 
  return $withMagicComments{$key} if defined $key;
    # else return the reference on the hash 
  return \%withMagicComments; 
}


=head3 C<%withMagicComments>

The attribute L</%withMagicComments> is 
written as side effect of L<_parseMagicComments()|/parseMagicComments()> invoked by L<init()|/init()>. 
Note that this does not just contain the magic comments, 
but it contains the settings determined by base configuration 
possibly overwritten by magic comments. 
The keys are as in the configuration given by the pom. 
Unlike L</docMeta>, 
this is always defined after L<compiling_cmd|/Hook-main::compiling_cmd-and-function-run_preDecideCompilation> has been invoked. 

TBD: In the long run, all basic settings shall be overwritable by magic comments. 
Then C<%withMagicComments> contains just all settings. 
It is initialized with the basic settings and overwritten if an according magic comment is present. 


The according getter C<withMagicComments()> invoked without parameter 
returns a reference on the attribute. 
If it has a parameter, this is interpreted as a key, 
and it returns the value corresponding with the key. 

=cut


my $docClass;

sub docClass {
  return $docClass;
}

=head3 C<$docClass>

The attribute L</$docClass> is 
written as side effect of L<init()|/init()>. 
Currently not needed but in the long this may be valuable, 
e.g. double compilation of beamer and beamer article 
Getter function is C<docClass()>. 

=cut


my %docMeta;

# sub docMeta ($key = undef) {
sub docMeta { my ($key) = @_; # key defaults to undef if not provided
  # like in java: return null (undef) if ... 
  return undef unless %docMeta; 

  # if the key is given, return the according value 
  return $docMeta{$key} if defined $key;
  # else return the reference on the hash 
  return \%docMeta; 
}

=head3 C<%docMeta>

The attribute is 
written as side effect of L<_parseDocMetadataByNeed()|/parseDocMetadataByNeed()> invoked by init 
may be undef or empty which must be distinguished. 

The according getter C<docMeta> returns an undefined value if the attribute is undefined. 
Else invoked without parameter returns a reference on the attribute 
and if it has a parameter, this is interpreted as a key and it returns the value corresponding with the key. 

=cut


my $doNeedVerification;

sub doNeedVerification {
  return $doNeedVerification;
}

=head3 C<$doNeedVerification>

Initialized by L<_parseDocMetadataByNeed()|/parseDocMetadataByNeed()> to C<FALSE> 
and L<_treatPdfStandards()|/treatPdfStandards()> to C<TRUE> if really verifiation is needed. 
Indicates whether DocumentMetadata defines at least one pdfstandard 
which can be verified by the given verification tool. 

The according getter is C<doNeedVerification()>. 

=cut


my $pdfFileDiff;

sub pdfFileDiff {
  return $pdfFileDiff;
}

=head3 C<$pdfFileDiff>

Undefined in L</init()> and defined in L<_initPdfMetadataHash()|/initPdfMetadataHash()> if diffing, 
i.e. reproduction is required (currently for PDF files only)
and if a PDF file to be reproduced exists. 
In this case, C<$pdfFileDiff> is the name of this file. 

Used in L</run_latex()> to find out whether the PDF to be created newly shall be reproduced 
and in L<run_onSuccessWarn()|/Function-run_onSuccessWarn-for-hooks-success_cmd-and-warning_cmd> to check whether reproduction succeeded. 
To reproduce, L</run_latex> invokes L</getTimestampDiff()> to extract metadata from that PDF file 
(see L</%metaPdfFlat> and L</%metaPdfXmp>), 
and to this end, reads pdfFileDiff also. 

The according getter is C<pdfFileDiff()>. 

=cut


my %metaPdfFlat;

=head3 C<%metaPdfFlat>

It contains metainfo in the info directory of L</$pdfFileDiff> if defined and exists; else undefined 
in contrast to XMP metadata this is flat 
It is written as side effect of L</init>: C<undef> directly, rest in L</_initPdfMetadataHash>
and it is used in L</getTimestampDiff()> only like L</%metaPdfXmp>. Thus has no getter method. 

=cut



my %metaPdfXmp;

=head3 C<%metaPdfXmp>

It contains xmp metainfo of L</$pdfFileDiff> if defined and exists; else undefined 
It is written as side effect of L</init>: C<undef> directly, rest in L</_initPdfMetadataHash>
and it is used in L</getTimestampDiff()> only like L</%metaPdfFlat>. Thus has no getter method. 

=cut



use constant boolStrToVal => {true => 1, false => 0};
use constant TRUE => 1;
use constant FALSE => 0;


# for printing hashes 
# is in perl-core since Perl 5.005 
use Data::Dumper;

use Cwd qw(getcwd);# in core-modules since Perl 5.000
use File::Spec::Functions qw(canonpath catfile catdir);# in core-modules since Perl 5.005 




=head2 FUNCTIONS

The functions of this package except getters/ 

=cut


sub init { my ($fileNameSrc, $fileNameTarget) = @_;


  undef $docClass;

  my ($programMagic, $chkDiffMagic, 
    $chkDiffMagicVal, $docMetadata, $targetsMagic);
  my $patternLatexMainFile = "\\A(%\\s*!\\s*T[eE]X (TXS|spellcheck|encoding|root).*\\R)*(%\\s*!\\s*T[eE]X program\\s*=\\s*(?<programMagic>[^} ]+)\\R)?(%\\s*!\\s*T[eE]X .*\\R)*(%\\s*!\\s*LMP (?<chkDiffMagic>chkDiff)(=(?<chkDiffMagicVal>true|false))?\\R)?(%\\s*!\\s*LMP (?<latexmkMagic>latexmk)(=(?<latexmkMagicVal>true|false))?\\R)?(%\\s*!\\s*LMP targets=(?<targetsMagic>(\\p{Lower}|,)+)\\R)?(\\s*(\\\\RequirePackage\\s*(\\[(\\s|\\w|[,=])*\\])?\\s*\\{(\\w|-)+\\}\\s*(\\[(\\d|[-./])+\\])?|\\\\PassOptionsToPackage\\s*\\{(\\s|\\w|[,=])*\\}\\s*\\{(\\w|-)+\\}|\\\\newbool\\s*\\{\\w+\\}|\\\\setbool\\s*\\{\\w+\\}\\{(true|false)\\}|\\\\UseName\\s*\\{[^{}]*\\}(?<arg>\\{(?:[^{}]|(?&arg))*\\})*|\\\\(DocumentMetadata|SetKeys\\[document/metadata\\])(?<docMetadata>\\{(?:[^{}]|(?&docMetadata))*\\})|\\\\input\\s*\\{[^{}]*\\})?\\s*(%.*)?\\R)*\\\\(documentstyle|documentclass)\\s*(\\[[^]]*\\])?\\s*\\{(?<docClass>[^} ]+)\\}";
  print("patternLatexMainFile: \n$patternLatexMainFile\n");
  open my $info, $fileNameSrc or die "Could not open $fileNameSrc : $!";
  # the lines read so far (each line with newline)
  my $lines = "";
  while (my $line = <$info>) {
    print "$line\n";
    $lines = "$lines$line";
    if ($line =~ /$patternLatexMainFile/) {
      #print("line matches |$+{programMagic}|\n");
      #print("line matches |$+{docClass}|\n");
      #print("lines: \n$lines\n");
      close $info;
      if ($lines !~ /$patternLatexMainFile/) {
        die("$fileNameSrc is no latex main file although documentclass/style found. \n");
      }
      print("preamble matches: \n");
      $programMagic    = $+{programMagic}    if $+{programMagic};
      $chkDiffMagic    = $+{chkDiffMagic}    if $+{chkDiffMagic};
      $chkDiffMagicVal = $+{chkDiffMagicVal} if $+{chkDiffMagicVal};
      $docMetadata     = $+{docMetadata}     if $+{docMetadata};
      $targetsMagic    = $+{targetsMagic}    if $+{targetsMagic};
      # This initializes part of this module 
      $docClass        = $+{docClass}        if $+{docClass};
      last;
    }
    # Here, the line does not match: go on 
    #last if $docClass;
  } # while 

  die "$fileNameSrc is no latex main file: no documentclass/style found at proper place. \n" 
    unless defined $docClass;
  # Here $docClass is initialized 

  print("docClass: $docClass\n");
  print("docMetas: $docMetadata\n") if defined $docMetadata;

  _parseDocMetadataByNeed($docMetadata);

  print ("docMeta: %docMeta");

  my $chkDiffB = _parseMagicComments($programMagic, $chkDiffMagic, $chkDiffMagicVal, $targetsMagic);


  undef $pdfFileDiff;
  undef %metaPdfFlat;
  undef %metaPdfXmp;


  # my $latexCommand = $programMagic // "lualatex";

  # # Make default value explicit 
  # # $chkDiffMagic = ($chkDiffMagic and not $chkDiffMagicVal)
  # #   ? 'true' : $+{chkDiffMagicVal};
  # $chkDiffMagic = ($chkDiffMagic and not $chkDiffMagicVal)
  #   ? 'true' : $chkDiffMagicVal;
  # my $chkDiffB  = $chkDiffMagic // "true";
  # $chkDiffB     = boolStrToVal->{$chkDiffB};
  # %withMagicComments = (
  #   latex2pdfCommand => $latexCommand,
  #   chkDiff          => $chkDiffB,
  # );


  if (!$chkDiffB) {
    return;
  }

  my $pdfFileOrg = canonpath(catfile(getcwd(), $fileNameTarget));
  #print "getcwd @{[ getcwd() ]}\n";
  #print "fileNameTarget $fileNameTarget\n";
  my $texSrcDirectory = canonpath(catdir("/home/ernst/OpenSource/LatexMavenPluginGit/latex-maven-plugin/maven-latex-plugin", "src/site/tex"));
  my $diffDirectory   = canonpath(catdir("/home/ernst/OpenSource/LatexMavenPluginGit/latex-maven-plugin/maven-latex-plugin", "src/main/resources/docsCmp"));
  my $pdfFileDiffE = $pdfFileOrg;
  $pdfFileDiffE =~ s/^\Q$texSrcDirectory/$diffDirectory/;
  if (-e $pdfFileDiffE) {
    # Here, $epoch_timestamp is not defined
    _initPdfMetadataHash($pdfFileDiffE);
  }

}

=head3 C<init>

  # parses the tex file to find out 
  # - magic comments which may modify basic settings 
  #   The result initializes `withMagicComments`
  # - documentmetadata which initializes `docMeta` 


  # The pattern is used to read magic comments. 
  # Double quotes because the pattern contains single quotes; 
  # no interpolation 

Used in C<Main::run_preDecideCompilation> which is invoked by the hook C<Main::$compiling_cmd> 
and initializes L</SrcTargetDesc>. 
Accordingly it parses the main tex file to find out magic coments, DocumentMetadata, the documentclass and other pieces of information. 
If 

TBD: in fact, if it is clear that we only create PDF, then we could simplify: 
$fileName and the rest is by adding the ending. 
If not then we must add detection of the ending of the target 
and do only part of the functionality if no PDF file. 


=head4 Inputs 

=over 4 

=item C<$fileNameSrc>

blabla 

=item C<$fileNameTarget>

blabla 

=back 

=cut




sub _parseMagicComments() { my ($programMagic, $chkDiffMagic, $chkDiffMagicVal, $targetsMagic) = @_;
  my $latexCommand = $programMagic // "lualatex";

  # Make default value explicit 
  # $chkDiffMagic = ($chkDiffMagic and not $chkDiffMagicVal)
  #   ? 'true' : $+{chkDiffMagicVal};
  $chkDiffMagic = ($chkDiffMagic and not $chkDiffMagicVal)
    ? 'true' : $chkDiffMagicVal;
  my $chkDiffB  = $chkDiffMagic // "true";
  $chkDiffB     = boolStrToVal->{$chkDiffB};
  my $targets = $targetsMagic // "chk,pdf,html";
  my @targetSet = map { $_ => undef } split(/\s*,\s*/, $targets);
  %withMagicComments = (
    latex2pdfCommand => $latexCommand,
    chkDiff          => $chkDiffB,
    targets          => \@targetSet
  );
  return $chkDiffB;
}


=head3 C<_parseMagicComments()>

Initializes L</%withMagicComments> 
based on the basic settings and magic comments given by the parameter list overwriting the basic settings. 
The function is invoked by L<init()|/init()> only, 
which recognizes the latex main file and in particular identifies the magic comments. 

TBD: In the long run, each (general) setting shall be modifiable by a magic comment, 
so the whole parsing must be shifted into C<_parseMagicComments()>. 
The attribute shall contain all settings then and the value is given by the magic comment if present, else by the basic setting. 

=head4 Inputs 

=over 4 

=item C<$programMagic>

blabla 

=item C<$chkDiffMagic>

blabla 

=item C<$chkDiffMagicVal>

blabla 

=item C<$$targetsMagic>

blabla 

=back 

=head4 Outputs 

=over 4 

=item C<$chkDiff>

blabla 

=back 

=cut



sub _parseDocMetadataByNeed { my ($docMetadata) = @_;
  # This may be overwritten later on in _treatPdfStandards
  $doNeedVerification = FALSE;

  if (!defined $docMetadata) {
    undef %docMeta;
    return;
  }
  my $patternMetadata = "(?:\\s*,\\s*)?(?<key>[^,={}]+)\\s*(?:=\\s*(?<value>\\{(?:[^{}]++|(?&value))*\\}|[^,{}]+))?";

  while ($docMetadata =~ /$patternMetadata/g) {
    my $key   = $+{key};
    my $value = $+{value};   # may be undefined 

    # This cannot be finalized because of weakness of l3pdf 
    # tex.stackexchange.com/questions/763060/error-handling-with-respect-to-documentmetadata-seems-inconsistent
    # steamlining this must take also LatexProcessor.reate into account. 

    if ($key eq 'uncompress') {
      # This is the only key without value 
      # Maybe this is not needed, because latex will issue an error anyway 
      warn "DMxx: Expected no value for key '$key' but found '$value'. " if defined $value;
      warn "DMxx: Found key '$key' more than once." if defined $docMeta{$key};
      # note that in the original in java, the value is null 
      # strictly speaking we should replace defined by exists. and put 
      # $docMeta{$key} = undef. but then one has to query with `exists $docMetadata{'uncompress'}` 
      # maybe this is to fine pointed and thus error prone 
      $docMeta{$key} = undef;
      next;
    }

    if (!defined $value) {
      warn "DMxx: Ignoring key '$key' without expected value, compiler behavior is unspecified. ";
      # Currently, the latex compiler does not complain. 
      # Filed a question already; TBD: maybe adapt if they change it 
      # ignoring means the value is not set in docMeta
      next;
    }

    # TBD: Here, we assume, that the user does not use superfluous {...}
    # and the keys treated correctly are 
    # pdfversion, uncompress, lang, pdfstandard, xmp, tagging and colorprofiles

    # eliminate enclosing {}; works only because {},{} do not occur. 
    my $patternCore = "^\\{\\s*(.*)\\s*\\}\$";
    $value =~ s/$patternCore/$1/s;

    if ($key eq 'pdfstandard') {
      # may occur more than once 
      my $oldVal = $docMeta{$key};
      $docMeta{$key} = defined $oldVal ? "$oldVal,$value" : $value;
      next;
    }

    warn "DMxx: Overwritten value '$docMeta{$key}' by '$value' for key '$key'." 
      if exists $docMeta{$key};
    $docMeta{$key} = $value;
  }

  # postprocessing of pdfstandards by need 
  if (defined $docMeta{pdfstandard}) {
    _treatPdfStandards();
  }

  $docMeta{xmp} //= 'true';
  $docMeta{tagging} //= 'off';
  warn "DMxx: Found preliminary tagging status 'draft'." if $docMeta{tagging} eq 'draft';
}

=head3 C<_parseDocMetadataByNeed()>

Initializes L</%docMeta> and L</$doNeedVerification> 
based on the scanned metadata C<$docMetadata> read from C<DocumentMetadata>. 

If !defined C<$docMetadata> then !defined L</%docMeta> and L</$doNeedVerification> = FALSE
Else L</%docMeta> has at least values for xmp and tagging 
given implicitly or explicitly. 
Also key uncompressed is recorded. Note it has no value. 
Special is the key `pdfstandard` which is treated separately in L<_treatPdfStandards()|/treatPdfStandards()>. 

if !defined L</%docMeta> or contains no value for `pdfstandards`, 
L</$doNeedVerification> is false but also if the verification tool 
cannot verify any of the prescribed standards. 

May throw a variety of warnings concerning metadata. 

=head4 Inputs 

=over 4 

=item C<$docMetadata>

=back

=head4 Warnings 

=over 4 

=item C<DMxx: Expected no value for key '$key' but found '$value'. >

=item C<DMxx: Found key '$key' more than once. >

=item C<DMxx: Ignoring key '$key' without expected value, compiler behavior is unspecified. >

=item C<DMxx: Overwritten value '$docMeta{$key}' by '$value' for key '$key'.>

=item C<DMxx: Found preliminary tagging status 'draft'.>

=back
=cut



sub _treatPdfStandards {
  my %kind2std;
  my $patternStdKindConf = "\\b(?<std>(?<kind>\\w+)-(?<conf>\\d+\\w*))\\b";
  while ($docMeta{pdfstandard} =~ /$patternStdKindConf/g) {
    my $kind = uc($+{kind});
    my $conf = lc($+{conf});
    my $std = "$kind-$conf";
    if (exists $kind2std{$kind}) {
      # even if twice the same 
      warn "DMxx: Standard $std overwrites $kind2std{$kind}."
    }
    $kind2std{$kind} = $std;
  }
  my @stdReq = values %kind2std;
  $docMeta{pdfstandard} = join(',', @stdReq);
 
  my $verifyCmd = "verapdf";
  my $verifyStdOptions = "--list";
  
  # Check whether all standards specified can be verified 
  my %stdVerifiable;
  my $stdout = qx"$verifyCmd $verifyStdOptions";
  my $res = $? >> 8;
  die "Could not determine verifiable standards; return code $res. " unless $res == 0;
  my @lines = split /\r?\n/, $stdout;
  my $patternVerifyList = sprintf("^(veraPDF supported PDF/A and PDF/UA profiles:|  [^-]+ - (WTPDF .*|PDF/%s) (\\+ Tagged PDF )?validation profile)\$", $patternStdKindConf);
  foreach my $line (@lines) {
    $line =~ /$patternVerifyList/ 
      or die "FATAL: Unexpected $verifyCmd output format or empty line: '$line'. ";
    if (defined $+{std}) {
      $stdVerifiable{$+{std}} = 1;
    }
  }

  my $stdCannotVerify = join(', ', grep { !exists $stdVerifiable{$_} } @stdReq);
  if ($stdCannotVerify) {
    warn("DMxx: Standard(s) $stdCannotVerify are specified but cannot be verified.");
  }

  my @stdCanVerify = grep { exists $stdVerifiable{$_} } @stdReq;
  if (@stdCanVerify == 0) {
    # TBD: better commas than blanks in list of standards 
    warn("DMxx: Skip checking specified standard(s) @stdReq, because $verifyCmd cannot verify neither. ");
    # Here, $doNeedVerification is FALSE;
  } else {
    $doNeedVerification = TRUE;
  }
}

=head3 C<_treatPdfStandards()>

Invoked in _parseDocMetadataByNeed only which ensures that $docMeta{pdfstandard} is defined. 
In this case, 

=over 4
=item -
normalizes $docMeta{pdfstandard} as regards casing, removal of duplicates 
and according warnings 
=item -
checks against verifiable warning if a standard is not verifyable 
and if verification is skipped altogether if no standard is verifyable 
=item -
sets $doNeedVerification if verification is needed 
Note that it is init where L</$doNeedVerification> is generally set false 

=back

=cut




sub _initPdfMetadataHash {my ($pdfFileDiffE) = @_;
  $pdfFileDiff = $pdfFileDiffE;
  my $stdout = qx"pdfinfo -isodates \"$pdfFileDiff\"";
  my $res = $? >> 8;
  $res == 0 or die "FATAL: pdfinfo failed with return value $res.";
  my @lines = split /\r?\n/, $stdout;

  my $pattern = "^(?<key>[^:]+):\\s*(?<value>.*)\$";
  print "$pattern\n";
  foreach my $line (@lines) {
    $line =~ /$pattern/ 
      or die "FATAL: Unexpected pdfinfo output format or empty line: ['$line']";
    $metaPdfFlat{$+{key}} = $+{value};
  }

  # TBD: factor out hash print. 
  print "\n[DEBUG] Flat Metadata Hash Dump:\n";
  while (my ($k, $v) = each %metaPdfFlat) {
    print "    Key: '$k' -> Value: '$v'\n";
  }

  # 2. XMP-Metadata (modern, precise)
  $res == 0 or die "FATAL: pdfinfo failed with return value $res.";
  $stdout = qx"pdfinfo -meta \"$pdfFileDiff\"";
  $res = $? >> 8;

  # TBD: read completely all 
  $pattern = "<(?<key>(?:pdf|pdfaid|pdfuaid|xmp|xmpMM|prism):\\w+)>(?<value>[^<>]+)</\\k<key>>";
  while ($stdout =~ m{$pattern}sg) {
    print("key $+{key}\n");
    # TBD: reactivate after architecture switch. 
    #die "FATAL: Duplicate XMP key found: ['$+{key}']" if exists $metaPdfXmp{$+{key}};
    $metaPdfXmp{$+{key}} = $+{value};
  }
  print "\n[DEBUG] XMP Metadata Hash Dump:\n";
  while (my ($k, $v) = each %metaPdfXmp) {
    print "    Key: '$k' -> Value: '$v'\n";
  }
}

=head3 C<_initPdfMetadataHash()>

Presupposes that $pdfFileDiffE is defined and exists 
initializes L</$pdfFileDiff>, L</%metaPdfFlat> and L</%metaPdfXmp> which were undefined before 

=head4 Inputs 

=over 4 

=item C<$pdfFileDiffE>

blabla 

=back

=cut




use Time::Piece; # using strptime core module with 5.10 which was packaged with texlive 2008 for windows 

sub getTimestampDiff {
  my $creationDate = $metaPdfXmp{'xmp:CreateDate'} // $metaPdfFlat{'CreationDate'};
  defined $creationDate or die "Found no creation date";

  # replace Zulu time by explicit time 
  $creationDate =~ s/Z$/+0000/;  
  # if the timezone has the form [+-]dd:dd, the : must be removed. 
  $creationDate =~ s/([+-]\d{2}):(\d{2})$/$1$2/;
  # if the timezone has 2 digits only, add the minutes.
  $creationDate =~ s/([+-]\d{2})$/${1}00/;
  # Note that the above replacements apply only if a time zone is given 

  # This determines whether there is a time zone at all 
   my $format = ($creationDate =~ /[+-]\d{4}$/)
    ? "%Y-%m-%dT%H:%M:%S%z"
    : do { 
      warn "WARNING: PDF date '$creationDate' without time zone; using local system time.\n"; 
      "%Y-%m-%dT%H:%M:%S" 
    }; 

  return Time::Piece->strptime($creationDate, $format)->epoch;
}

=head3 C<getTimestampDiff()>

Used in Main::run_latex to run with the correct timestamp 


=cut

1;

package main;

=head1 package C<main>

blablabla 

=head2 Rules and Functions 

=cut

sub run_chktex { my $source = @_;
  my $ret = system("chktex -q -b0 $source > ");
  $ret >>= 8; # reconstruct return value of the application 

  return $ret;
}


# TBD: not ideal for pdfViaDvi=true: conversion dvi to pdf is needed only once at the end, 
# whereas this method does conversion dvi to pdf each time also tex to dvi is performed. 
# Thus in the long run only run dvi2pdf; the rest is done with rules. 
sub run_latex { my ($fileName, @opts) = @_;

  # Already initialized in run_preDecideCompilation
  my $ref = SrcTargetDesc::withMagicComments();
  # {$ref} dereferences the hash 
  my ($latexCommand, $chkDiffB) = @{$ref}{'latex2pdfCommand', 'chkDiff'};
  # This is just short for 
  # my $latexCommand = $ref->{latex2pdfCommand};
  # my $chkDiffB     = $ref->{chkDiff};

  #$chkDiffB = SrcTargetDesc::boolStrToVal->{$chkDiffB};

  my $timeEnv = "";
  my $epoch_timestamp;
  if ($chkDiffB) {
    # # The following is to determing PDF file to diff if chkDiff is set 

    # Here, getTimestampDiff is undef if there is no PDF to reproduce. 
    # else it is the timestamp of the pdf to be reproduced. 
    
    
    if (defined(SrcTargetDesc::pdfFileDiff())) {
      # Here, the reference file exists 
      $epoch_timestamp = SrcTargetDesc::getTimestampDiff();
      # For lualatex setting TZ=UTC is needed but FORCE_SOURCE_DATE is ignored 
      # For pdflatex setting TZ=UTC is superfluous but FORCE_SOURCE_DATE is needed; 
      # the same for xelatex 
      $timeEnv = "TZ=UTC SOURCE_DATE_EPOCH=$epoch_timestamp FORCE_SOURCE_DATE=1";
    } else {
      # Here, the reference PDF file does not exist, so local time but with GMT timezone 
      $timeEnv = "TZ=UTC";
    }
    # in both cases note the trailing blank 
    # The settings are required both for direct compilation into PDF and for compilation via DVI 
  }

  my $pdfViaDvi = SrcTargetDesc::boolStrToVal->{"false"};
  # note that exactly one of the two options -no-pdf -output-format=dvi applies; 
  # the other is ignored. 
  # TBD: eliminate: xelatex emits a warning because -output-format is unknown 
  my $addArgs = $pdfViaDvi ? "-no-pdf -output-format=dvi " : "";
  # TBD: evaluate return values properly.  
  my $res = system("$timeEnv $latexCommand -interaction=nonstopmode -synctex=1 -recorder -shell-escape $addArgs @opts $fileName");
  if ($pdfViaDvi) {
    # Note that $timeEnv is first of all suitable for the latex compiler. 
    # strictly speaking FORCE_SOURCE_DATE is not needed; the other variables are needed 
    # to set up 
    # TBC: ignores options %O from latexmk, acceptable 
    $res = $res or
      system("$timeEnv dvipdfmx -V1.7 $fileName");
  }
  return $res;
}


=head3 run_latex

blablabla 

=cut

#$postscript_mode = $dvi_mode = 0;

# to configure bibtex 
# bbl files are never precious 
$main::bibtex_use = 2;
$main::bibtex = "bibtex  %O %S";# default: bibtex %O %S

# this cannot be done according to the according latex maven plugin, 
# because the according parameter maxNumReRunsLatex may be set to -1 
# which signifies an infinite number of runs. 
$main::max_repeat = 30;

# default are tex and eps, but could also be pdf and ptx and mps
# Currently, all those files are given with explicit endings, 
# so no extensions to be added. 
#add_input_ext('');

# It is what it seems to be: clean inludes what was generated by cus 
$main::cleanup_includes_cusdep_generated = 1;
$main::cleanup_includes_generated = 1;

# TBD: clarify: xdv and dvi seem to be internal. 
# maybe missing other extensions in conjunction with synctex 
# maybe better @generated_exts see below 
push @main::generated_exts, "%R.synctex.gz", "%R.synctex";;

# bbl does not work
#@generated_exts = (@generated_exts, 'lol', 'bbl', 'glo', 'ist')
#print "Hello!"
#foreach (@generated_exts) {
#print "Generated exts: $_\n";
#}
#print "clean_ext\n";
# Here @generated_exts is ('aux', 'fls', 'log', # generated by latex already
# 'toc', 'lof', 'lot', 'out', # generated by latex conditionally 
# 'idx', 'ind', 'blg', 'ilg', # concerning indices 
# # strange enough: nothing for bibtex 
# 'xdv', 'bcf'

# extensions ext to be deleted by latexmk -c 
# Note that the file names are %R.ext. 
# this may cause problems with extensions containing a dot. 
# Also this is not general enough 
# if the generated file deviates from %R by more than an extension. 
# In this case, use the form with explicit '%R'. 
# list of listings, whereas lof and lot are already present. 
push @main::generated_exts, "lol";
push @main::generated_exts, "dvi", "xdv";
# for beamer class 
push @main::generated_exts, "nav", "snm", "vrb", 'run.xml';
push @main::generated_exts, "clg";# log file for chktex: specific for latex builder LMP 
push @main::generated_exts, "soc";# package changes, also adding toc if not present 
push @main::generated_exts, "sil";# package silence
push @main::generated_exts, "tdo";# package todonotes
push @main::generated_exts, "xmpdata";# written by filecontents for pdfx 

# why are 'ist' and 'xdy' not under generated_exts? 
# note that currently, either %R or what is present is the extension only! 
# this does not make sense very much. 
# $clean_ext .= " stateMachine.log"; does not work, because stateMachine.log is the extension! 

# should be under indexing 
push @main::generated_exts, '%ist', '%xdy', '%R-*.ind', '%R-*.idx', '%R-*.ilg', '%R-*.ind';

# many arguments shall be quoted 
# but in many cases it is immaterial; except in metapost 
sub _quote { my ($inString) = @_;
  my $outString = $inString;
  $outString =~ s/^ */'/; # add leading ' after eliminating leading blanks 
  $outString =~ s/ *$/'/; # add closing ' after eliminating trailing blanks 
  # so far: quote after trim 
  $outString =~ s/ +/' '/g; # replace a sequence of blanks by a single one 
  $outString =~ s/''//;     # unquote the empty string 
  
  #print "out: $outString\n";
  return $outString;
}


add_cus_dep('fig', 'ptx', 0, 'fig2dev');
sub fig2dev { my ($file) = @_;
  print("create 'ptx' from '$file.fig'\n");
  rdb_add_generated("$file.pdf", "$file.eps");
  my ($name, $path) = fileparse($file);
  pushd($path);
    #fig2dev -L pstex    <fig2devGenOptions> <fig2devPdfEpsOptions>        xxx.fig xxx.eps   
    #fig2dev -L pdftex   <fig2devGenOptions> <fig2devPdfEpsOptions>        xxx.fig xxx.pdf   
    #fig2dev -L pdftex_t <fig2devGenOptions> <fig2devPtxOptions>    -p xxx xxx.fig xxx.ptx
    # TBD: evaluate return values properly 
    my $ret1 = system("fig2dev -L  pstex           $name.fig $name.eps");
    my $ret2 = system("fig2dev -L pdftex           $name.fig $name.pdf");
    my $ret3 = system("fig2dev -L pdftex_t   -p $name $name.fig $name.ptx");
  popd();
  $ret1 >>= 8;
  $ret2 >>= 8;
  $ret3 >>= 8;
 return ($ret1 or $ret2 or $ret3);
}

=head3 Transforming C<fig> to C<ptx> and related via C<fig2dev>

blablabla 

=cut

add_cus_dep('gp', 'ptx', 0, 'gnuplot');
sub gnuplot { my ($file) = @_;
  print("create 'ptx' from '$file.gp'\n");
  rdb_add_generated("$file.pdf", "$file.eps");
  my ($name, $path) = fileparse($file);
  pushd($path);
    # here in the java code no quoting occurs 
    #my $gnuplotOptionsQ = _quote(qq//);
    my $ret1 = system(qq/gnuplot -e "set terminal cairolatex pdf ;\
            set output '$name.ptx';\
            load '$name.gp'"/);
    # my $ret2 = system("gnuplot -e \"set terminal cairolatex eps $[gnuplotOptions};\
    #           set output '$name.ptx';\
    #           load '$name.gp'\"");
  popd();
  $ret1 >>= 8; # reconstruct return value of the application 
  return $ret1;
}

=head3 Transforming C<gp> to C<ptx> and related via C<gnuplot>

blablabla 

=cut

# metapost rule from http://tex.stackexchange.com/questions/37134
#add_cus_dep('mp', 'mps', 0, 'mpost');
add_cus_dep('mp', 'mps', 0, 'mpost');
sub mpost { my ($file) = @_;
  print("create 'mps' from '$file.mp'\n");
  rdb_add_generated("$file.mpx", "$file.fls", "$file.log");
  # TBD: mpx files are written only if the mp file contains latex parts. 
  # It would be cleaner to add the mpx file after invoking mpost 
  # with the condition that an mpx file is present. 
  # Likewise, 
  my ($name, $path) = fileparse($file);
  pushd($path);
    my $metapostOptionsQ = _quote("-interaction=nonstopmode -recorder -s prologues=2 -s outputtemplate=\"%j.mps\"");
    #print "quoted: $metapostOptionsQ\n";
    my $ret = system(qq/mpost $metapostOptionsQ $name/);
  popd();
  $ret >>= 8; # reconstruct return value of the application 
  return $ret;
}

=head3 Transforming C<mp> to C<mps> and related via C<mpost>

blablabla 

=cut

add_cus_dep('svg', 'ptx', 0, 'inkscape');
sub inkscape { my ($file) = @_;
  print("create  'ptx'from '$file.svg'\n");
  rdb_add_generated("$file.pdf", "$file.eps");
  my ($name, $path) = fileparse($file);
  pushd($path);
    my $ret1 = system(qq/inkscape --export-filename=$name.pdf --export-area-drawing --export-latex $name.svg/);
    #my $ret2 = system("inkscape --export-filename=$name.eps -D --export-latex $name.svg ");
    #use File::Copy;
    # This works only for pdf, not for eps. 
    #unlink($name.pdf_tex) or die "cannot unlink $name.pdf_tex";
    rename("$name.pdf_tex", "$name.ptx");# or die "cannot move $name.pdf_tex";
  popd();
  $ret1 >>= 8; # reconstruct return value of the application 
  return $ret1;# or $ret2;
}

=head3 Transforming C<svg> to C<ptxs> and related via C<inkscape>

blablabla 

=cut

# graphics for xfig (not appropriate for mixed tex/pdf)

# add_cus_dep('fig', 'pdf', 0, 'fig2pdf');

# sub fig2pdf {
# system( "fig2dev -Lpdf \"$_[0].fig\" \"$_[0].pdf\"" );
# }

$main::makeindex = 'internal run_makeSplitindex %A %O';

# invoked only by run_makeSplitindex 
# if used \sindex[idx]{} and no other index name, this is misleading: is a single multi-index
sub _parseIdxFileForMultiIdx { my ($fileName) = @_;
  $fileName = "$fileName.idx";
  # since the patter is likely to end in $, we use single quotes here 
  my $patternMultiIndex = "^(\\\\indexentry)\\[([^]]*)\\](.*)\$";
  open my $info, $fileName or die "Could not open $fileName: $!";
  my $foundDefaultEntry = 0;
  my %res = ();
  while (my $line = <$info>) {
    if ($line =~ /$patternMultiIndex/) {
      # Here, it is clear that we have a multi-index 
      $res{$2} = 1;
    } else {
      $foundDefaultEntry = 1;
    }
  }

  if ($foundDefaultEntry && %res) {
    # Here, both \index and \sindex are used 
    $res{"idx"} = 1;
  }
  close $info;
  # Here, it is clear that we have no multi-index 
  return keys %res;
}

sub run_makeSplitindex { my ($fileName, @opts) = @_;
  # $fileName is without ending 

  my @indexLabels = _parseIdxFileForMultiIdx($fileName);
  my $ret;
  if (@indexLabels) {
    # create dummy ind file 
    my $ind_fh;
    open($ind_fh, '>', "$fileName.ind");
    close $ind_fh;

    foreach (@indexLabels) {
      # splitindex splits up the idx file into many 
      # and then generates ind file each 
      rdb_add_generated("$fileName-$_.idx", "$fileName-$_.ind");
    }

    $ret = system("splitindex --makeindex makeindex " . 
                  "-V $fileName --  @opts");
  } else {
    $ret = system("makeindex  @opts $fileName");
  }
  $ret >>= 8; # reconstruct return value of the application 
  return $ret
}

# This set of dependencies is only complete 
# if we restrict ourselves to types defined by options 
# without using \newglossary explicitly 
add_cus_dep( 'acn', 'acr', 0, 'run_makeglossaries' );
add_cus_dep( 'slo', 'sls', 0, 'run_makeglossaries' );
add_cus_dep( 'nlo', 'nls', 0, 'run_makeglossaries' );
#add_cus_dep( 'idx', 'ind', 0, 'run_makeglossaries' ); # would collide with indexing 
add_cus_dep( 'glo', 'gls', 0, 'run_makeglossaries' );
# TBD: add file endings for symbols, 
# not only here but also in the java code. 
push @main::generated_exts, 'glo', 'gls', 'glg';
push @main::generated_exts, 'acn', 'acr', 'alg';
push @main::generated_exts, 'slo', 'sls', 'slg';
push @main::generated_exts, 'nlo', 'nls', 'nlg';
# push @generated_exts, 'idx', 'ind', 'ilg'; # would collide with indexing 
push @main::generated_exts, "ist", "xdy"; # index stylefile created by the glossaries package 
# TBD: add xdy also in java code 

#$clean_ext .= " acr acn alg glo gls glg";# TBD: clarify: better in @generated_exts? 

sub run_makeglossaries { my ($file) = @_;
  my $options = "";
  if ($main::silent) {
    $options = "$options -q";
  }
  my $ret = system("makeglossaries $options $file");
  $ret >>= 8; # reconstruct return value of the application 
  return $ret;
}

# !!! ONLY WORKS WITH VERSION 4.54 or higher of latexmk
#TBD: take into account: modified:
#  '$_[0]'->

# #############
# # makeindex #
# #############
# @ist = glob("*.ist");
# if (scalar(@ist) > 0) {
#         $makeindex = "makeindex -s $ist[0] %O -o %D %S";
# }

# Implementing glossary with bib2gls and glossaries-extra, with the
#  log file (.glg) analyzed to get dependence on a .bib file.

# !!! ONLY WORKS WITH VERSION 4.54 or higher of latexmk

push @main::generated_exts, 'glg', '%R*.glstex';
# TBD: clarify 
#push @generated_exts, 'glg', '%R*.glstex';

# TBD: clarify treatment of encoding. 
# why we can avoid  
#        "--tex-encoding", "UTF-8",
#        "--log-encoding", "UTF-8",
# We need tex sources encoding in editor: utf8 
# in header 
# \iftutex%
#   \usepackage{fontspec}% sets utf8
# \else
#   % this seems to work with beamer also 
#   \usepackage[utf8]{inputenc}
#   \usepackage[T1]{fontenc}
# \fi
# in bib file: 1st line: 
# % Encoding: UTF-8


add_cus_dep('aux', 'glstex', 0, 'run_bib2gls');
# Explanation can be found in 
# https://tex.stackexchange.com/questions/400325/latexmkrc-for-bib2gls
sub run_bib2gls { my ($file) = @_;
  my $options = "--group";
  if ($main::silent) {
    $options = "--silent $options";
  }
  my $ret = system("bib2gls $options $file");
  $ret >>= 8; # reconstruct return value of the application 
  if ($ret) {
    warn "Run_bib2gls: Error, running bib2gls; return value $ret.\n";
    return $ret;
  }

  # my ($base, $path) = fileparse($file);
  # if ($path && -e "$base.glstex") {
  #   rename "$base.glstex", "$path$base.glstex";
  # }

  # Analyze log file to find the bib-files.
  my $glg= "$file.glg";
  my $isopen = open(my $glg_fh, '<', $glg);
  if (not $isopen) {
    warn "Run_bib2gls: Error opening log file '$glg'\n";
    return not $isopen;
  }
  rdb_add_generated($glg); 
 
  while (<$glg_fh>) {
    s/\s*$//;
    if (/^Reading\s+(.+)$/) {
      rdb_ensure_file($main::rule, $1);
    }
    if (/^Writing\s+(.+)$/) {
      rdb_add_generated($1);
    }
  }
  close $glg_fh;

  return $ret;
}

=head3 Transforming C<aux> to C<glstex*> via C<run_bib2gls>

blablabla 

=cut

# The following code was developed out of a suggestion from John Collins 
# It is complementary to code in changes/PythonTeXdep
# This code shall not be erased, it may only be deactivated. 

push @main::generated_exts, "pytxcode", "plg";
push @main::generated_exts, "depytx", "%R.depytx.tex", "dplg";

push @main::generated_exts, "pythontex-files-%R/*", "pythontex-files-%R";
#$extra_rule_spec{'pythontex'}  = [ 'internal', '', 'mypythontex', "%Y%R.pytxcode", "%Ypythontex-files--%R/%R.pytxmcr", "%R", 1 ];
$main::extra_rule_spec{'pythontex'} = [
  'internal', '', 'mypythontex', 
  "%R.pytxcode", "pythontex-files-%R/%R.pytxmcr", "%R", 1
];

# Explanation for PythonTeX dependency 
# can be found in changes/PythonTeXdep 
# In fact, to make this work, 
# the code provided there must be included in package pythontex
# TBD: this function is invoked based on global variables. 
# For me, this is quite ugly... no parameters 
sub mypythontex {
  my $prefixPytexOutFolder = "pythontex-files-";
  my $result_dir = "$main::aux_dir1$prefixPytexOutFolder$$main::Pbase";
  my $pythontex = "pythontexW --rerun=always %R";#'pythontexW %O %R';
  my $ret        = Run_subst($pythontex, 2);
  rdb_add_generated(glob "$result_dir/*");

  my $fh = FileHandle->new($$main::Pdest, "r");
  #open( my $fh, "<", $$Pdest );
  if ($fh) {
    #print "path: $ENV{PATH}";
    while (<$fh>) {
      if (/^%PythonTeX dependency:\s+'([^']+)';/) {
        print "Found pythontex dependency '$1'\n";
        rdb_ensure_file( $main::rule, $main::aux_dir1 . $1 );
      }
    }
    undef $fh;
  } else {
    warn "mypythontex: I could not read '$$main::Pdest'\n",
         "  to check dependencies\n";
  }
  return $ret;
}


# for htlatex 
push @main::generated_exts, "4tc", "4ct", "tmp", "xref", "css", "idv", "lg";
# TBD: for -C remove also html and xhtml
# TBD: check that this plugin also removes all these extensions.. think of lg. 


# biblatex
# push @generated_exts, "run.xml";# does run.xml work? 
# $clean_ext .= " %R-blx.bib";

=head2 Hooks and Functions 

Code C<latexmk> invokes hooks at certain events. 
Most of these hooks invoke just C<internal> functions. 

=cut

sub run_preDecideCompilation { my ($sourceBase, $target) = @_;
  my $source = "$sourceBase.tex";
  print("prior to decision whether compiling $source to $target needed\n");
  SrcTargetDesc::init($source, $target);
  my $ref = SrcTargetDesc::withMagicComments();
  my %targetSet = ${$ref}{'targets'};
  if (exists $targetSet{'chk'}) {
    run_chktex($sourceBase);
  }
}

$main::compiling_cmd = "internal run_preDecideCompilation %A %D";

=head3 Hook C<$main::compiling_cmd> and function C<run_preDecideCompilation>

The hook C<$main::compiling_cmd> is defined 
by the internal function C<run_preDecideCompilation>


=head4 Inputs 

=over 4 

=item C<$sourceBase>

The base name of the main TEX file. 

=item C<$target>

The target file to be created with ending. 

=back 

=cut

$main::success_cmd="internal run_onSuccess %D";

sub run_onSuccess { my ($target) = @_;
  print("Compilation succeeded without warning.\n");
  run_onSuccessWarn($target);
}

=head3 Hook C<$main::success_cmd> and function C<run_onSuccess>

The hook C<$main::success_cmd> is defined by the internal function C<run_onSuccess> 
which in turn invokes 


=head4 Inputs 

=over 4 

=item C<$source>

blabla 

=item C<$target>

blabla 

=back 

=cut


$main::warning_cmd="internal run_onWarn %D";

sub run_onWarn { my ($target) = @_;
  print("Compilation succeeded with warning(s).\n");
  run_onSuccessWarn($target);
}

sub run_onSuccessWarn { my ($target) = @_;
  # TBD: find out the cases where $target is empty 
  if ($target !~ m/.pdf$/) {
    print("created no pdf\n");
    return;
  }
  # currently, postprocessing occurs for pdf files only 


  # my $ref = SrcTargetDesc::docMeta();

  # if (!defined $ref) {
  #   print "no \\DocumentMetadata/SetKeys";
  #   return;
  # }
  # my %all = %$ref;
  # print "All Metadata:\n";
  # for my $k (keys %all) {
  #   print "  $k => $all{$k}\n";
  # }

  #
  return unless SrcTargetDesc::doNeedVerification();


  if (SrcTargetDesc::doNeedVerification()) {
    my $res = system("verapdf -f 0 --format text \"$target\"");
    $res >>= 8; # reconstruct return value of the application 
    # TBD: distinguish between detected non-conformance and could not perform verification 
    if ($res == 0) {
      print("Conformance as requested. \n");
    } elsif ($res == 1)  {
      print("Conformance ***not*** as requested. \n");
    }else {
      warn("Could not check whether pdf file conforms the declared standards. \n");
    }
  }

  my $pdfFileDiff = SrcTargetDesc::pdfFileDiff();
  if (defined $pdfFileDiff) {
    # TBD: distinguish between detected difference and could not check 
    my $res = system("diff $target $pdfFileDiff");
    $res >>= 8; # reconstruct return value of the application 
    if ($res == 0) {
      print("PDF file was reproduced. \n");
    } elsif ($res == 1) {
      print("PDF file was ***not*** reproduced. \n");
    } else {
      warn("Could not check whether pdf file could be reproduced. \n");
    }
  }
}

=head3 Function C<run_onSuccessWarn> for hooks C<success_cmd> and C<warning_cmd>

The hook C<success_cmd> is defined by the internal function C<run_onSuccess> 
and the hook C<warning_cmd> is defined by the internal function C<run_onWarn> 
both of which invoke C<run_onSuccessWarn>. 


=head4 Inputs 

=over 4  

=item C<$target>

blabla 

=back 

=cut


$main::failure_cmd="echo '...compilation with failure'";

=head3 C<$main::failure_cmd>

In case of a failure, currently no function is invoked, just a failure messgage is echoed. 

=cut

