# ---------------------------------------------------------------------------
# 
# File:    PARAM.pm
#
# Copyright (c) Programming Research, 2004.
#
# Written: C.Waddingham 20-Aug-04
#
# Version: 1.15
#
# Purpose: To provide Singleton type functions yielding constant parameters
#          for qaw.pl.
#
# History:
# 1.15        21-Mar-01 - VERSION_IS_LATER_THAN_QAC6_2() added.
#                         LOOKUP_CUSTOM_REPORT_DETAILS() and
#                         LOOKUP_PROJECT_ANALYSIS_DETAILS() updated to use
#                         SPLIT_COMMAND_LINE_STRING().
# 1.14        23-Apr-09 - CR12977 Windows cannot execute: `"<path>" "<path" -ver`.
# 1.13        17-Apr-08 - EXECUTE_FOR_PROG_VERSION improved for progs with spaces.
# 1.12        05-Feb-07 - CFG_FILENAME now works for QAC & QACPP always.
# 1.11        13-Mar-06 - Corrected derivation of qacpp.ini in GET_CONFIG_SECTION.
# 1.10        24-Sep-05 - Analysis prog run from qa_bin and not PATH.
# 1.9         23-Aug-05 - Suppression of FlexLm interactive pop-ups added.
# 1.8         09-May-05 - Added PROGRAM_USES_STANDARD_SETTINGS_FILE.
# 1.7         20-Apr-05 - Added PURE_SETTINGS_VIA, QA_PURE_FILELIST.
# 1.6         18-Apr-05 - Calc of temp dir corrected in EXECUTE_FOR_PROG_VERSION.
# 1.5         25-Mar-05 - QA_RESULTS_TEMP added.
# 1.4         16-Mar-05 - Added QAW path when finding <analyser>.return_codes.
# 1.3         23-Feb-05 - Updated for project analysis inheriting command line
#                         display options.
# 1.2         12-Jan-05 - Handling added for Cygwin.
# 1.1         23-Dec-04 - -crep & -canal implementation completed for Unix.
#
# ---------------------------------------------------------------------------

package PARAM; #=============================================================

require Exporter;
our @ISA = qw( Exporter );

use strict;

#----------------------------------------------------------------------------

BEGIN
{
   use strict;

   require GEN;

   our $qaw_version;         # Version of this the QAW program.

   our $qaw_usage;           # QAW usage message.

   our @argv = @ARGV;        # Require that @ARGV is not accessed in this program.
                             # Instead use PARAM::WORKING_ARGV() which returns the parameters.
                             # Reason for doing this is that may want to strip
                             # 'qac' / '-qac' / 'qacpp' / '-qacpp' / 'qac++' / '-qac++'
                             # from first element of arguments list.

   our $ar_standard_settings_programs  = [ "errdsp", "errwrt", "pal", "prjdsp", "qac", "viewer" ];

   our %analysis_options;    # List of QAC/PP options that affect analysis.  Used by MODE.

   our %cumulatives;         # List of QAC/PP options that repeat without overriding 
                             # previous options.  Used by QPARSE.

   our %options_with_upper_case_parameter; # List of QAC/PP options whose parameter
                                           # is forced to upper-case.  Used by QPARSE.

   our %options_with_lower_case_parameter; # List of QAC/PP options whose parameter
                                           # is forced to lowewr-case.  Used by QPARSE.

   our %preferred_options;   # List of (shortest) versions of option names.  Main 
                             # purpose is to give one notation when an option has
                             # several forms, e.g. -tab can be -t, -tab or -tabstop.
                             # Used by QPARSE wrt .opt files.

   our $qa_version = "";     # Set (once) to version of QAC / QACPP if needed (constant)

   our $hr_error_codes = {}; # Hash { <program base name> }{ <error code> }
                             # [ <quoted message string>,
                             #   <fail boolean>,
                             #   <perform diagnostics boolean>
                             # ]

   our @windows_gui_ini_file = (); # Content of QAC/bin/qac.ini

   our @unix_xqarc_file = (); # Content of QAC/.xqacrc + ( ./.xqacrc | ~/.xqacrc )

   our $errdsp_program = "errdsp";   # constant
   our $prjdsp_program = "prjdsp";   # constant

   our $settings_via;        # Holds name of settings file written by QAW (constant)
   our $pure_settings_via;   # Holds name of PURE settings file written by QAW (constant)
   our $qa_filelist;         # Holds name of filelist file written by QAW (constant)
   our $qa_pure_filelist;    # Holds name of PURE filelist file written by QAW (constant)

   our $qa_path;             # Holds value of environment variable QAC[PP]PATH (constant)
   our $qa_bin;              # Holds value of environment variable QAC[PP]BIN (constant)
   our $qa_temp;             # Holds value of environment variable QAC[PP]TEMP or /tmp etc (constant)
   our $qa_outpath;          # Holds value of environment variable QAC[PP]OUTPATH (constant)
   our $qa_program;          # Holds 'QAC' or 'QACPP' (constant)
   our $qa_analysis_program; # Holds name of analyser program ie 'qac' or 'qacpp' or 'qac++' (constant)

   our $settings_via_increment_number = 0; # Number to be appended to $[pure_]settings_via
                                           # to get full instance name (variable)
   our $file_list_increment_number = 0;    # Number to be appended to $qa_[pure_]filelist
                                           # to get full instance name (variable)

   our $qa_full_analysis_program_name; # Same as $qa_analysis_program, but with full extension.

   #-- This program relies on its own name!
   #
   our $executable_name = GEN::STEM_NAME( $0 );

   #-- Set DIE prefix
   #   
   GEN::SET_DIE_PREFIX( uc( $executable_name ) );

   #-- Set primary analyser based settings ----------------------------------
   #

   my $parameters_required;

   if ( lc( $executable_name ) eq "qacw" )
   {
      $parameters_required = "QAC";
   }
   elsif ( lc( $executable_name ) eq "qacppw" )
   {
      $parameters_required = "QACPP";
   }
   else
   {
      my $first_arg;
      ( $first_arg, @argv ) = @ARGV;
 
      if ( lc( $first_arg ) eq "qac" )
      {
         $parameters_required = "QAC";
      }
      elsif ( $first_arg =~ /^(qacpp|qac\+\+)$/oi )
      {
         $parameters_required = "QACPP";
      }
      else
      {
         GEN::DIE( ": Unknown analyser. Require 'QAC' or 'QACPP'." );
      }
   }

   # Here with valid executable name and analyser selection.
   # Make the necessary settings.
   #
   if ( $parameters_required eq "QAC" )
   {
      # Set PARAM.pm settings needed when running in QAC mode.
      #
      if ( ! defined( $ENV{ 'QACPATH' } ) )
      {
   	 GEN::DIE( ": QACPATH must be set.  Set QAC environment variables." );
      }
      
      if ( ! defined( $ENV{ 'QACBIN' } ) )
      {
   	 GEN::DIE( ": QACBIN must be set.  Set QAC environment variables." );
      }
      
      $qa_path             = GEN::APPLY_FINAL_SEP( $ENV{ 'QACPATH' } );
      $qa_bin              = GEN::APPLY_FINAL_SEP( $ENV{ 'QACBIN' } );
      $qa_program          = "QAC";
      $qa_analysis_program = "qac";

      if ( GEN::PLATFORM_IS_WINDOWS() ||
           GEN::PLATFORM_IS_CYGWIN()
         )
      {
   	 if ( ! defined( $ENV{ 'QACTEMP' } ) )
   	 {
   	    GEN::DIE( ": QACTEMP must be set.  Set QAC environment variables." );
   	 }
   	 $qa_temp                       = GEN::APPLY_FINAL_SEP( $ENV{ 'QACTEMP' } );
         $qa_full_analysis_program_name = "qac.exe";
      }
      else
      {
   	 $qa_temp                       = "/tmp/";
         $qa_full_analysis_program_name = "qac";
      }
      
      if ( defined( $ENV{ 'QACOUTPATH' } ) )
      {
   	 $qa_outpath = GEN::APPLY_FINAL_SEP( $ENV{ 'QACOUTPATH' } );
      }
      else
      {
   	 $qa_outpath = "";
      }
   }
   elsif ( $parameters_required eq "QACPP" )
   {
      # Set PARAM.pm settings needed when running in QACPP mode.
      #
      if ( ! defined( $ENV{ 'QACPPPATH' } ) )
      {
   	 GEN::DIE( ": QACPPPATH must be set.  Set QAC++ environment variables." );
      }
      
      if ( ! defined( $ENV{ 'QACPPBIN' } ) )
      {
   	 GEN::DIE( ": QACPPBIN must be set.  Set QAC++ environment variables." );
      }
      
      if ( GEN::PLATFORM_IS_WINDOWS() ||
           GEN::PLATFORM_IS_CYGWIN()
         )
      {
   	 if ( ! defined( $ENV{ 'QACPPTEMP' } ) )
   	 {
   	    GEN::DIE( ": QACPPTEMP must be set.  Set QAC++ environment variables." );
   	 }
   	 $qa_temp = GEN::APPLY_FINAL_SEP( $ENV{ 'QACPPTEMP' } );
      }
      else
      {
   	 $qa_temp = "/tmp/";
      }
      
      if ( defined( $ENV{ 'QACPPOUTPATH' } ) )
      {
   	 $qa_outpath = GEN::APPLY_FINAL_SEP( $ENV{ 'QACPPOUTPATH' } );
      }
      else
      {
   	 $qa_outpath = "";
      }
      
      $qa_path      = GEN::APPLY_FINAL_SEP( $ENV{ 'QACPPPATH' } );
      $qa_bin       = GEN::APPLY_FINAL_SEP( $ENV{ 'QACPPBIN' } );
      $qa_program   = "QACPP";
      
      if ( GEN::PLATFORM_IS_WINDOWS() )
      {
   	 $qa_analysis_program           = "qacpp";
         $qa_full_analysis_program_name = "qacpp.exe";
      }
      else
      {
   	 $qa_analysis_program           = "qac++";
         $qa_full_analysis_program_name = "qac++";
      }
   }
   else
   {
      GEN::DIE( ": Program Error - Unable to set parameters" );
   }

   #-- Save existance of errdsp ---------------------------------------------
   #
   our $errdsp_exists = GEN::EXECUTABLE_EXISTS( $qa_bin . $errdsp_program );

   #-- Set temporary directory settings -------------------------------------
   #

   # At this point we have $qa_temp as the desired (by QAW) temporary
   # directory.  Must check that this exists.
   #
   if ( ! -d $qa_temp )
   {
     GEN::DIE( ": Required working directory \"$qa_temp\" does not exist. " .
               "Please create it and try again." );
   }

   # In order to be able to find %R+ output file(s) use following
   # special temp dir purely for this purpose.
   #
   our $qa_results_temp = GEN::APPLY_FINAL_SEP( $qa_temp );

   # Create session temporary directory within $qa_temp, and assign 
   # $qa_temp to be this directory.
   #
   $qa_temp = GEN::CREATE_NEW_DIRECTORY( "QAW_TEMP_", $qa_temp );
   $qa_temp = GEN::APPLY_FINAL_SEP( $qa_temp );

   if ( GEN::PLATFORM_IS_UNIX() )
   {
      # For Unix cannot allow $qa_results_temp to be /tmp as
      # file /tmp/<prog>.html will potentially be written and read by
      # many users, each with different access rights, and hence once
      # one user has written say /tmp/tcm_rc.html, then all other 
      # users will get access violations when they subsequently try to
      # write the same file.
      #
      # So put $qa_results_temp (for Unix) inside the current user
      # temporary directory space.
      #
      $qa_results_temp = $qa_temp;
   }

   # Reset QAC[PP}TEMP environment variable to new temp dir.
   # NB This dir is within the QAC[PP]TEMP dir as set on entry to QAW.
   #
   if ( $parameters_required eq "QAC" )
   {
      $ENV{ 'QACTEMP' } = $qa_temp;
   }
   elsif ( $parameters_required eq "QACPP" )
   {
      $ENV{ 'QACPPTEMP' } = $qa_temp;
   }

   # Set suppression of FlexLm interative pop-ups.
   # This is done here as part of initialisation as a once off setting
   # for the entire QAW run.
   #
   $ENV{ 'FLEXLM_BATCH' } = "1";

   #-- Set (qaw).options file path ------------------------------------------
   #
   # NB Say this is qaw.exe, then will have qaw.exe.options
   # And this will be located in the same directory as qaw.exe and if not
   # then check in $qa_bin directory.
   #

   # Use GEN::WHICH( $0 ) to find the full path of the $0 file.
   # NB GEN::WHICH() has the limitation that for Windows, if the
   # supplied file is not an executable file, e.g. fred.txt,
   # then GEN::WHICH() will return the first full path of fred.txt in
   # PATH, whether it is executable or not.  It is the responsibility
   # of the user of GEN::WHICH() to ensure that a truely executable
   # filename is supplied.
   #
   my $this_qaw_program_full_path = GEN::WHICH( $0 );

   if ( $this_qaw_program_full_path eq "" )
   {
      GEN::DIE( ": Program error deriving full program name for \"$0\"." );
   }

   our $options_file;

   $options_file = $this_qaw_program_full_path . ".options";

   if ( ! -e $options_file )
   {
      my $options_file_2 = $qa_bin . 
                           GEN::BASE_NAME( $this_qaw_program_full_path ) . 
                           ".options";

      if ( -e $options_file_2 )
      {
         $options_file = $options_file_2;
      }
   }

   #-- Set (qaw).options.properties file path -------------------------------
   #

   our $options_properties_file;

   $options_properties_file = $this_qaw_program_full_path . ".options.properties";

   if ( ! -e $options_properties_file )
   {
      my $options_properties_file_2 = $qa_bin .
        			      GEN::BASE_NAME( $this_qaw_program_full_path ) .
        			      ".options.properties";

      if ( -e $options_properties_file_2 )
      {
         $options_properties_file = $options_properties_file_2;
      }
   }
}

#----------------------------------------------------------------------------

sub SET_QAW_VERSION
{
   # Usage: SET_QAW_VERSION( "2.0" );
   #
   $PARAM::qaw_version = shift;
}

#----------------------------------------------------------------------------

sub QAW_VERSION
{
   return $PARAM::qaw_version;
}

#----------------------------------------------------------------------------

sub SET_QAW_USAGE
{
   $PARAM::qaw_usage = shift;
}

#----------------------------------------------------------------------------

sub SET_ANALYSIS_OPTIONS
{
   %PARAM::analysis_options = @_;
}

#----------------------------------------------------------------------------

sub SET_CUMULATIVES
{
   %PARAM::cumulatives = @_;
}

#----------------------------------------------------------------------------

sub SET_OPTIONS_WITH_UPPER_CASE_PARAMETER
{
   %PARAM::options_with_upper_case_parameter = @_;
}

#----------------------------------------------------------------------------

sub SET_OPTIONS_WITH_LOWER_CASE_PARAMETER
{
   %PARAM::options_with_lower_case_parameter = @_;
}

#----------------------------------------------------------------------------

sub SET_PREFERRED_OPTIONS
{
   %PARAM::preferred_options = @_;
}

#----------------------------------------------------------------------------

sub QAW_USAGE
{
   return $PARAM::qaw_usage;
}

#----------------------------------------------------------------------------

sub EXECUTABLE_NAME
{
   return $PARAM::executable_name;
}

#----------------------------------------------------------------------------

sub PRQA_DEFAULT_BROWSER_PARAM_NAME
{
   return "PRQA_DEFAULT_BROWSER";
}

#----------------------------------------------------------------------------

sub CMAF_PROJECT_NAME
{
   return "cmaf_non_project_files";
}

#----------------------------------------------------------------------------

sub BROWSER_NAME
{
   my $prqa_default_browser_param_name = PRQA_DEFAULT_BROWSER_PARAM_NAME();

   my $browser_name = $ENV{ $prqa_default_browser_param_name };

   if ( defined $browser_name )
   {
      return $browser_name;
   }
   
   return "";
}

#----------------------------------------------------------------------------

sub WORKING_ARGV
{
   # Usage: @argv = WORKING_ARGV();
   # Returns the working argument set.  NB this is the full list of command
   # line arguments less the first (ie 'QAC' / 'QACPP' etc) if using QAW mode,
   # or the full set if using QACW or QACPPW mode.
   # 
   return @PARAM::argv;
}

#----------------------------------------------------------------------------

sub VERSION_IS_LATER_THAN_QAC453_OR_QACPP142
{
   # Usage: $bool_is_new_version = VERSION_IS_LATER_THAN_QAC453_OR_QACPP142();
   #
   my $qa_version = QA_VERSION();

   if ( $PARAM::qa_program eq "QAC" )
   {
      return GEN::MIXED_TYPE_GT( $PARAM::qa_version, "4.5" );
   }
   else
   {
      return GEN::MIXED_TYPE_GT( $PARAM::qa_version, "1.4" );
   }
}
   
# -------------------------------------------------------------------------

sub VERSION_IS_QAC6_OR_QACPP2_OR_LATER
{
   # Usage: $bool_is_new_version = VERSION_IS_QAC6_OR_QACPP2_OR_LATER
   #
   my $qa_version = QA_VERSION();

   if ( $PARAM::qa_program eq "QAC" )
   {
      return ( $qa_version eq "6.0" ) ||
             GEN::MIXED_TYPE_GT( $qa_version, "6.0" );
   }
   else
   {
      return ( $qa_version eq "2.0" ) ||
             GEN::MIXED_TYPE_GT( $qa_version, "2.0" );
   }
}
   
# -------------------------------------------------------------------------

sub VERSION_IS_LATER_THAN_QAC6_OR_QACPP2
{
   # Usage: $bool_is_new_version = VERSION_IS_LATER_THAN_QAC6_OR_QACPP2();
   #
   my $qa_version = QA_VERSION();

   if ( $PARAM::qa_program eq "QAC" )
   {
      return GEN::MIXED_TYPE_GT( $qa_version, "6.0" );
   }
   else
   {
      return GEN::MIXED_TYPE_GT( $qa_version, "2.0" );
   }
}
   
# -------------------------------------------------------------------------

sub VERSION_IS_LATER_THAN_OR_EQUAL_TO_QAC8_OR_QACPP2_5_2
{
   # Usage: $bool_is_new_version = 
   #           VERSION_IS_LATER_THAN_OR_EQUAL_TO_QAC8_OR_QACPP2_5_2();
   #
   my $qa_version = QA_VERSION();

   if ( $PARAM::qa_program eq "QAC" )
   {
      return ( $qa_version eq "8.0" ) ||
             GEN::MIXED_TYPE_GT( $qa_version, "8.0" );
   }
   else
   {
      return ( $qa_version eq "2.5.2" ) ||
             GEN::MIXED_TYPE_GT( $qa_version, "2.5.2" );
   }
}
   
# -------------------------------------------------------------------------

sub VERSION_IS_LATER_THAN_QAC6_2
{
   # Usage: $bool_is_new_version = VERSION_IS_LATER_THAN_QAC6_OR_QACPP2
   #
   my $qa_version = QA_VERSION();

   if ( $PARAM::qa_program eq "QAC" )
   {
      return ( $qa_version eq "6.2" ) ||
             GEN::MIXED_TYPE_GT( $qa_version, "6.2" );
   }
   else
   {
      # Not relevant for QACPP
      return 1;  # true
   }
}
   
# -------------------------------------------------------------------------

sub QA_VERSION
{
   # Usage: $product_version = QA_VERSION();
   #
   if ( $PARAM::qa_version eq "" )
   {
      $PARAM::qa_version = COMPUTE_QA_VERSION();

      return $PARAM::qa_version;
   }

   return $PARAM::qa_version;
}

# -------------------------------------------------------------------------

sub COMPUTE_QA_VERSION
{
   # Usage: $product_version = COMPUTE_QA_VERSION();
   #
   # Gets version of QAC / QACPP
   
   if ( $PARAM::qa_version ne "" )
   {
      return $PARAM::qa_version;
   }
   elsif ( QA_PROGRAM() eq "QAC" )
   {
      # Get QAC version string.
      # NB Have to throw away the usage info that is written to STDERR.
      #
 
      my $tmp_file = QA_TEMP() . "QAW_TMP_QA_Version";
      GEN::DELETE_FILE_OR_DIRECTORY( $tmp_file );
      $tmp_file = GEN::QUOTE_ARG( $tmp_file );

      # Use specific invokation, ie not EXECUTE_FOR_PROG_VERSION
      #
      my $ver_string = GEN::EXECUTE_PROGRAM_FOR_STDOUT
              		  ( $PARAM::qa_bin . $PARAM::qa_analysis_program,
              		    "-ver 2>$tmp_file"
              		  );

      if ( $ver_string =~ /security protocol failure/oi )
      {
         GEN::DIE( ": \"$PARAM::qa_program\" licence problem" );
      }

      $ver_string =~ s/.*?static analyser\s*([\d\.]+).*/$1/iso;

      $PARAM::qa_version = $ver_string;

      return $ver_string;
   }
   elsif ( $PARAM::qa_program eq "QACPP" )
   {
      # Here when getting version number for QAC++
      #

      # Use specific invokation, ie not EXECUTE_FOR_PROG_VERSION
      #
      my $ver_string = GEN::EXECUTE_PROGRAM_FOR_STDOUT
              		  ( $PARAM::qa_bin . $PARAM::qa_analysis_program,
              		    "-debug textoutput NUL"
              		  );

      if ( $ver_string =~ /security protocol failure/oi )
      {
         GEN::DIE( ": \"$PARAM::qa_program\" licence problem" );
      }

      # QAC++1.5 produces an identification string ending:
      #   ++ WARNING ++: <=0=(0999) Release 1.5 Jul  1 2003 12:13:27
      #
      # QACPP-2.0 produces an identification string ending:
      #   QAC++ Deep Flow Static Analyser 2.0 (build 12)
      #
      # Accommodate both by using either 'Release' or 'Analyser' as
      # the keyword which prefixes the version number.
      #
      $ver_string =~ s/.*?QAC\+\+.*?(Release|Analyser)\s*([\d\.]+).*/$2/iso;

      $PARAM::qa_version = $ver_string;

      return $ver_string;
   }
}

# -------------------------------------------------------------------------

sub EXECUTE_FOR_PROG_VERSION
{
   # Usage: $version_string = 
   #    EXECUTE_FOR_PROG_VERSION( $prog, $arg );
   #
   my $prog_name = shift;
   my $arg       = shift;

   # For Windows do the best we can to remove spaces in the program name
   #
   if ( GEN::PLATFORM_IS_WINDOWS() )
   {
      my $path = GEN_PATH::DIR_NAME( $prog_name );
      my $prog = GEN_PATH::BASE_NAME( $prog_name );

      if ( $path ne "" )
      {
         my $short_path = GEN::SHORT_PATH_NAME( $path );

         $prog_name = $short_path . $prog;
      }

      if ( index( $prog_name, " " ) != -1 )
      {
         # Have to treat this as a failure as won't be able to run $prog_name
         
         # Here when prog failed, so return fixed result of '*'
         #
         return "*";
      }
   }

   my $null = QA_TEMP() . "null";
   $null = GEN::QUOTE_ARG( $null );
   GEN::DELETE_FILE_OR_DIRECTORY( $null );

   my $return_code = GEN::EXECUTE_SYSTEM_COMMAND_WITH_RESULT( "$prog_name $arg >$null 2>&1" );

   if ( $return_code == 0 )
   {
      # Here when command executes, so get result
      #

      # For Unix, ensure path is quoted.
      #
      if ( GEN::PLATFORM_IS_UNIX() )
      {
         $prog_name = GEN::QUOTE_ARG( $prog_name );
      }

      return `$prog_name $arg`;
   }
   else
   {
      # Here when prog failed, so return fixed result of '*'
      #
      return "*";
   }
}

# -------------------------------------------------------------------------

sub ERRDSP_PROGRAM
{
   return $PARAM::errdsp_program;
}

# -------------------------------------------------------------------------

sub PRJDSP_PROGRAM
{
   return $PARAM::prjdsp_program;
}

# -------------------------------------------------------------------------

sub QA_PATH
{
   return $PARAM::qa_path;
}

# -------------------------------------------------------------------------

sub QA_BIN
{
   return $PARAM::qa_bin;
}

# -------------------------------------------------------------------------

sub QA_TEMP
{
   return $PARAM::qa_temp;
}

# -------------------------------------------------------------------------

sub QA_RESULTS_TEMP
{
   return $PARAM::qa_results_temp;
}

# -------------------------------------------------------------------------

sub QA_OUTPATH
{
   return $PARAM::qa_outpath;
}

# -------------------------------------------------------------------------

sub QA_PROGRAM
{
   return $PARAM::qa_program;
}

# -------------------------------------------------------------------------

sub QA_ANALYSIS_PROGRAM
{
   return $PARAM::qa_analysis_program;
}

# -------------------------------------------------------------------------

sub QA_FULL_ANALYSIS_PROGRAM_NAME
{
   return $PARAM::qa_full_analysis_program_name;
}

# -------------------------------------------------------------------------

sub CFG_FILENAME
{
   # Returns name of QAC / QACPP configuration file
   #

   my $cfg_filename = $PARAM::qa_bin;

   if ( $PARAM::qa_program eq "QAC" )
   {
      $cfg_filename .= "qac.cfg";
   }
   else
   {
      $cfg_filename .= "qacpp.cfg";
   }

   # Force all separators to QAC universal one of '/'
   #
   # NB See also sub: NORMALISE_PATH_SEPARATORS
   #
   $cfg_filename =~ s/\\/\//go;

   return $cfg_filename;
}

# -------------------------------------------------------------------------

sub INCREMENT_SETTINGS_VIA_INSTANCE_NUMBER
{
   ++$PARAM::settings_via_increment_number;
}

# -------------------------------------------------------------------------

sub SET_SETTINGS_VIA_STEM
{
   $PARAM::settings_via = shift;
}

# -------------------------------------------------------------------------

sub SET_PURE_SETTINGS_VIA_STEM
{
   $PARAM::pure_settings_via = shift;
}

# -------------------------------------------------------------------------

sub SETTINGS_VIA
{
   return $PARAM::settings_via . "." . $PARAM::settings_via_increment_number;
}

# -------------------------------------------------------------------------

sub PURE_SETTINGS_VIA
{
   return $PARAM::pure_settings_via . "." . $PARAM::settings_via_increment_number;
}

# -------------------------------------------------------------------------

sub INCREMENT_FILE_LIST_INSTANCE_NUMBER
{
   ++$PARAM::file_list_increment_number;
}

# -------------------------------------------------------------------------

sub SET_QA_FILELIST_STEM
{
   $PARAM::qa_filelist = shift;
}

# -------------------------------------------------------------------------

sub QA_FILELIST
{
   return $PARAM::qa_filelist . "." . $PARAM::file_list_increment_number;
}

# -------------------------------------------------------------------------

sub SET_PURE_QA_FILELIST_STEM
{
   $PARAM::qa_pure_filelist = shift;
}

# -------------------------------------------------------------------------

sub QA_PURE_FILELIST
{
   return $PARAM::qa_pure_filelist . "." . $PARAM::file_list_increment_number;
}

# -------------------------------------------------------------------------

sub ERRDSP_EXISTS
{
   return $PARAM::errdsp_exists;
}

# -------------------------------------------------------------------------

sub DOT_XQARC_FILE_NAME
{
   if ( QA_PROGRAM() eq "QAC" )
   {
      return ".xqacrc";
   }
   else
   {
      return ".xqacpprc";
   }
}

# -------------------------------------------------------------------------

sub OPTIONS_FILE_PATH
{
   # Usage: $path = OPTIONS_FILE_PATH();
   #
   return $PARAM::options_file;
}

# -------------------------------------------------------------------------

sub OPTIONS_PROPERTIES_FILE_PATH
{
   # Usage: $path = OPTIONS_PROPERTIES_FILE_PATH();
   #
   return $PARAM::options_properties_file;
}

# -------------------------------------------------------------------------

sub PROGRAM_USES_STANDARD_SETTINGS_FILE
{
   # Usage: $bool_uses_std = PROGRAM_USES_STANDARD_SETTINGS_FILE( $prog_file );
   #
   my $prog_file = shift;

   my $prog_stem = GEN::STEM_NAME( $prog_file );

   if ( GEN::PLATFORM_IS_WINDOWS() )
   {
      return grep( /^$prog_stem$/i, @$PARAM::ar_standard_settings_programs ) != 0;
   }
   else
   {
      return grep( /^$prog_stem$/, @$PARAM::ar_standard_settings_programs ) != 0;
   }
}

# -------------------------------------------------------------------------

sub LOAD_OPTION_PARAMETER_DETAILS
{
   # Usage: LOAD_OPTION_PARAMETER_DETAILS( $options_properties_file_path );
   #
   # This sub reads the file $options_properties_file_path and
   # derives QAW parameters used within QPARSE and MODE.
   #
   # $options_properties_file_path is a list of text records of
   # structure:
   # { full_name_of_option, 
   #   preferred_name_of_option,
   #   option_is_analysis_option,
   #   option_is_cumulative,
   #   option_parameter_case,
   #   option personality_category
   # }
   #
   # The following QPARSE data structures are created:
   #   %preferred_options,
   #   %analysis_options
   #   %cumulatives,
   #   %options_with_upper_case_parameter,
   #   %options_with_lower_case_parameter,
   #  
   #   The option_personality_category is currently (10-12-04) not used in QAW.
   #
   my $options_properties_file_path = shift;

   my @option_parameter_records = GEN::LOAD_FILE_AS_ARRAY( $options_properties_file_path );

   # Parse the parameter records into their component fields and assign
   # to lookup hashes.
   #

   my %preferred_options                 = ();
   my %analysis_options                  = ();
   my %cumulatives                       = ();
   my %options_with_upper_case_parameter = ();
   my %options_with_lower_case_parameter = ();

   RECORD:
   foreach my $rec ( @option_parameter_records )
   {
      chomp $rec;

      # Skip empty lines and comment lines (Comment lines start with *)
      #
      next RECORD if ( $rec =~ /^(\*.*|\s*)$/o );

      # NB $option_personality_category field is not used.
      # (In fact this field may not be present in the .options.properties file.)
      #
      my ( $full_name_of_option, 
           $preferred_name_of_option,
           $option_is_analysis_option,
           $option_is_cumulative,
           $option_parameter_case,
           $option_personality_category ) =
              GEN::SPLIT_STRING_ALLOWING_QUOTED_TERMS( " \t", $rec );

      $preferred_options{ $full_name_of_option } = $preferred_name_of_option;

      if ( $option_is_analysis_option eq "<ANALYSIS>" )
      {
         $analysis_options{ $full_name_of_option } = 1;   # anything
      }

      if ( $option_is_cumulative eq "<CUMULATIVE>" )
      {
         $cumulatives{ $full_name_of_option } = 1;   # anything
      }

      if ( lc( $option_parameter_case ) eq "<upper_case>" )
      {
         $options_with_upper_case_parameter{ $full_name_of_option } = 1;  # anything
      }
      elsif ( lc( $option_parameter_case ) eq "<lower_case>" )
      {
         $options_with_lower_case_parameter{ $full_name_of_option } = 1;  # anything
      }
   }

   # %cumulatives is the list of all options that can appear more
   # than once in the settings without overriding the previous
   # values.  This is used to determine which options are affected
   # by -forget / -forgetall.
   #
   # Prior to QAW version 2.0, the following hard-coded settings were used:
   #
   #   my %cumulatives = (
   #	 "-a"       => 1,
   #	 "-canal"   => 1,
   #	 "-crep"    => 1,
   #	 "-d"       => 1,
   #	 "-ex"      => 1,
   #	 "-exec"    => 1,
   #	 "-glob"    => 1,
   #	 "-i"       => 1,
   #	 "-it"      => 1,
   #	 "-n"       => 1,
   #	 "-o"       => 1,
   #	 "-op"      => 1,
   #	 "-q"       => 1,
   #	 "-rem"     => 1,
   #	 "-s"       => 1,
   #     "-sat"     => 1,
   #	 "-sd"      => 1,
   #	 "-si"      => 1,
   #	 "-spragma" => 1,
   #	 "-su"      => 1,
   #	 "-thresh"  => 1,
   #	 "-wc"      => 1
   #   );
   #
   SET_CUMULATIVES( %cumulatives );

   # %options_with_upper_case_parameter list options for which the
   # parameter shall be forced to upper case.
   #
   # Prior to QAW version 2.0, the following hard-coded settings were used:
   #
   #   my %options_with_upper_case_parameter = (
   #	  "-co"     => 1,
   #	  "-en"	    => 1,
   #	  "-ex"	    => 1,
   #	  "-mode"   => 1,
   #	  "-sty"    => 1
   #   );
   #
   SET_OPTIONS_WITH_UPPER_CASE_PARAMETER( %options_with_upper_case_parameter );

   # %options_with_lower_case_parameter list options for which the
   # parameter shall be forced to lower case.
   #
   # Prior to QAW version 2.0, the following hard-coded settings were used:
   #
   #   my %options_with_lower_case_parameter = (
   #	  "-k+r"	    => 1,
   #   );
   #
   SET_OPTIONS_WITH_LOWER_CASE_PARAMETER( %options_with_lower_case_parameter );

   # %preferred_options is used to map any option, that has more than
   # one permissible form, to one consistent form.  ie the shortest.
   #
   # Prior to QAW version 2.0, the following hard-coded settings were used:
   #
   #   my %preferred_options = (
   #	  "-allowconstconvdeduction"           => "-accd",
   #	  "-align"                             => "-a",
   #	  "-arithrsh"                          => "-ar",
   #	  "-allowtempnonconstrefbind"          => "-atncrb",
   #	  "-bitsigned"                         => "-bits",
   #	  "-comment"	                       => "-co",
   #	  "-createpph"                         => "-cpph",
   #	  "-debug"                             => "-deb",
   #	  "-dontusec99decintlittypes"          => "-duc99dilt",
   #	  "-echo"		               => "-e",
   #	  "-encoding"	                       => "-en",
   #	  "-expandfilenames"                   => "-ef",
   #	  "-extensions"	                       => "-ex",
   #	  "-forceinclude"	               => "-fi",
   #	  "-forgetall"	                       => "-forget",
   #	  "-foldplainchar"                     => "-fpc",
   #	  "-hdrsuppress"                       => "-hdr",
   #	  "-help"   	                       => "-h",
   #	  "-hiddenwarnings"                    => "-hw",
   #	  "-hidemacrowarnings"                 => "-hmw",
   #	  "-indentlevel"                       => "-il",
   #	  "-intrinsictype"	               => "-it",
   #	  "-introducefriendnames"              => "-ifn",
   #	  "-layoutfile"	                       => "-lf",
   #	  "-layoutignoreheaders"               => "-lih",
   #	  "-layoutindentwidth"                 => "-liw",
   #	  "-layoutusehangingindent"            => "-luhi",
   #	  "-libraryusagefile"                  => "-luf",
   #	  "-line"                              => "-l",
   #	  "-maxcount" 	                       => "-max",
   #	  "-maxerrors" 	                       => "-maxerr",
   #	  "-maxrecursivetemplatenestingdepth"  => "-mrtnd",
   #	  "-messagesonly"                      => "-m",
   #	  "-metrics"                           => "-met",
   #	  "-namelength" 	               => "-na",
   #	  "-no" 		               => "-n",
   #	  "-nomsg" 		               => "-n",
   #	  "-onelineonly"                       => "-one",
   #	  "-only" 		               => "-o",
   #	  "-outputpath" 	               => "-op",
   #	  "-overwrite"                         => "-over",
   #	  "-ppfilename"                        => "-ppf",
   #	  "-pphversion"                        => "-pph",
   #	  "-pplist"                            => "-ppl",
   #	  "-ppmetrics"                         => "-ppm",      
   #	  "-pponly"                            => "-ppo",
   #	  "-propagatetemplateparameters"       => "-ptp",
   #	  "-quiet" 		               => "-q",
   #	  "-references"                        => "-ref",
   #	  "-remark" 	                       => "-rem",
   #	  "-searchdependentbaseclasses"        => "-sdep",
   #	  "-settings"                          => "-set",
   #	  "-size" 		               => "-s",
   #	  "-skippragma" 	               => "-spragma",
   #	  "-slashwhite"                        => "-sl",
   #	  "-standard"                          => "-st",
   #	  "-stdisglobal"                       => "-sig",
   ##      "-strict"                           => "-str",
   #	  "-style" 		               => "-sty",
   #	  "-summary"                           => "-summ",
   #	  "-suppresslvl" 	               => "-su",
   #	  "-systemdefine" 	               => "-sd",
   #	  "-systeminclude" 	               => "-si",
   #	  "-tab" 		               => "-t",
   #	  "-tablesummary"                      => "-tsumm",
   #	  "-tabstop" 	                       => "-t",
   #	  "-textonly"                          => "-text",
   #	  "-threshold" 	                       => "-thresh",
   #	  "-unsignedchar"                      => "-u",
   #	  "-usrfile" 	                       => "-usr",
   #	  "-usrpath" 	                       => "-up",
   #	  "-version"                           => "-ver",
   #	  "-warncall" 	                       => "-wc",
   #	  "-xcase"                             => "-xc",
   #	  "-xnamelength" 	               => "-xn" );
   #
   SET_PREFERRED_OPTIONS( %preferred_options );

   # %analysis_options contains all options that affect analysis
   # and hence need to be considered when checking if analysis options
   # have changed.  Non analysis options are not checked when 
   # determining whether an analysis needs to be repeated.
   #
   # %analysis_options is also used in QPARSE::GET_NON_ANALYSIS_OPTIONS();
   #
   # Entries in %analysis_options must be in lower case.
   #
   # Prior to QAW version 2.0, the following hard-coded settings were used:
   #
   #   my %analysis_options = (
   #	  "-align"                            => 1,
   #	  "-arithrsh"		              => 1,
   #	  "-ar"			              => 1,
   #	  "-a"			              => 1,
   #	  "-accd"                             => 1,
   #	  "-allowconstconvdeduction"          => 1,
   #	  "-atncrb"                           => 1,
   #	  "-allowtempnonconstrefbind"         => 1,
   #	  "-bitsigned"		              => 1,
   #	  "-bits"			      => 1,
   #	  "-calling"		              => 1,
   #	  "-callpack"		              => 1,
   #	  "-canal"                            => 1,
   #	  "-cmaf"                             => 1,
   #	  "-comment"		              => 1,
   #	  "-co"			              => 1,
   #	  "-createpph"                        => 1,
   #	  "-cpph"                             => 1,
   #	  "-dpft"			      => 1,
   #	  "-delayparsefntemplates"	      => 1,
   #	  "-d"			              => 1,
   #	  "-duc99dilt"                        => 1,
   #	  "-dontusec99decintlittypes"         => 1,
   #	  "-echo"			      => 1,
   #	  "-ehtml"                            => 1,
   #	  "-encoding"		              => 1,
   #	  "-en"			              => 1,
   #      "-etxt"                             => 1,
   #	  "-exec"                             => 1,
   #	  "-extensions"		              => 1,
   #	  "-ex"			              => 1,
   #	  "-expandfilenames"                  => 1,
   #	  "-ef"                               => 1,
   #	  "-e"			              => 1,
   #	  "-forceinclude"		      => 1,
   #	  "-fi"			              => 1,
   #	  "-foldplainchar"                    => 1,
   #	  "-forgetall"		              => 1,
   #	  "-forget"			      => 1,
   #	  "-fpc"                              => 1,
   #	  "-glob"                             => 1,
   #	  "-hidemacrowarnings"	              => 1,
   #	  "-hmw"			      => 1,
   #	  "-ifn"			      => 1,
   #	  "-il"                               => 1,
   #	  "-indentlevel"                      => 1,
   #	  "-intrinsictype"		      => 1,
   #	  "-introducefriendnames"	      => 1,
   #	  "-it"			              => 1,
   #	  "-i"			              => 1,
   #	  "-k+r"			      => 1,
   #	  "-layoutfile"		              => 1,
   #	  "-lf"			              => 1,
   #	  "-lih"			      => 1,
   #	  "-layoutignoreheaders"	      => 1,
   #	  "-layoutindentwidth"	              => 1,
   #	  "-liw"			      => 1,
   #	  "-luhi"			      => 1,
   #	  "-layoutusehangingindent"	      => 1,
   #	  "-libraryusagefile"                 => 1,
   #	  "-luf"                              => 1,
   #	  "-maxerrors"		              => 1,
   #	  "-maxerr"			      => 1,
   #	  "-maxrecursivetemplatenestingdepth" => 1,
   #	  "-mrtnd"                            => 1,
   #	  "-mll"                              => 1,
   #	  "-mode"                             => 1,
   #	  "-namelength"		              => 1,
   #	  "-namerulefile"                     => 1,
   #	  "-na"			              => 1,
   #	  "-op"			              => 1,
   #	  "-outputpath"		              => 1,
   #	  "-overwrite"		              => 1,
   #	  "-over"			      => 1,
   #	  "-ppfilename"		              => 1,
   #	  "-ppf"			      => 1,
   #	  "-pphversion"                       => 1,
   #	  "-pphv"                             => 1,
   #	  "-pplist"			      => 1,
   #	  "-ppl"			      => 1,
   #	  "-ppmetrics"		              => 1,
   #	  "-ppm"			      => 1,
   #	  "-pponly"			      => 1,
   #	  "-ppo"			      => 1,
   #	  "-propagatetemplateparameters"      => 1,
   #	  "-ptp"			      => 1,
   #	  "-quiet"			      => 1,
   #	  "-q"			              => 1,
   #	  "-remark"			      => 1,
   #	  "-rem"			      => 1,
   #	  "-sdep"			      => 1,
   #	  "-searchdependentbaseclasses"       => 1,
   #	  "-systemdefine"		      => 1,
   #	  "-sd"			              => 1,
   #	  "-settings"		              => 1,
   #	  "-set"			      => 1,
   #	  "-systeminclude"		      => 1,
   #	  "-si"			              => 1,
   #	  "-size"			      => 1,
   #	  "-slashwhite"		              => 1,
   #	  "-sl"			              => 1,
   #	  "-spragma"		              => 1,
   #	  "-skippragma"		              => 1,
   #	  "-sr"                               => 1,
   #	  "-ss"                               => 1,
   #	  "-style"			      => 1,
   #	  "-sty"			      => 1,
   #	  "-standard"		              => 1,
   #	  "-stoponfail"                       => 1,
   #	  "-st"			              => 1,
   #	  "-stdisglobal"                      => 1,
   ##      "-strict"                          => 1,
   ##      "-str"                             => 1,
   #	  "-sig"                              => 1,
   #	  "-s"			              => 1,
   #	  "-tabstop"		              => 1,
   #	  "-tab"			      => 1,
   #	  "-threshold"		              => 1,
   #	  "-thresh"			      => 1,
   #	  "-t"			              => 1,
   #	  "-unsignedchar"		      => 1,
   #	  "-u"			              => 1,
   #	  "-version"		              => 1,
   #	  "-ver"			      => 1,
   #	  "-via"			      => 1,
   #	  "-wc"			              => 1,
   #	  "-warncall"		              => 1,
   #	  "-xcase"			      => 1,
   #	  "-xc"			              => 1,
   #	  "-xnamelength"		      => 1,
   #	  "-xn"                               => 1 );
   #
   SET_ANALYSIS_OPTIONS( %analysis_options );
}

# -------------------------------------------------------------------------

sub ANALYSER_RETURN_CODE_LOOKUP
{
   # Usage: ( $error_message,
   #          $fail,
   #          $perform_diagnostics,
   #          $code_recognised ) =
   #            ANALYSER_RETURN_CODE_LOOKUP( $analyser_program,
   #                                         $error_code );
   #
   # NB $code_recognised ::= "<UNKNOWN CODE>" | "<KNOWN CODE>";
   #
   # Returns values used by RUN::EXECUTE_PRIMARY_ANALYSIS(), and
   # RUN::EXECUTE_POST_ANALYSIS().
   #
   # 1. Every analyser should have an associated file, in the same directory
   #    as the analyser program, which has name:
   #       <analyser base name>".return_codes".
   #    
   #    This (text) file has layout as follows:
   #     
   #    <return code> <message string> <fail boolean> <perform diagnostics boolean>
   #    
   #    Where:
   #    
   #    <return code> is any string (can be quoted), e.g. 255
   #    
   #    <message string> is text like: " Failed: Analysis error"
   #
   #    <fail boolean> is 0 if the error, flagged by this return code, allows
   #    	       further processing of the source file.
   #    
   #    <perform diagnostics boolean> is 0 if QAC/++ style diagnostics cannot
   #    			      be performed.  (This applies to nearly
   #    			      all post-analysis programs.  ie For
   #    			      post-analysis value is 0.)
   #
   # 2. Having ensured that the return code file for the supplied 
   #    $analyser_program_base_name is loaded (and parsed into array
   #    $ar_return_codes_lookup), return the entry for $error_code.
   #
   my $analyser_program = shift;
   my $error_code       = shift;

   my $analyser_program_base_name = GEN::BASE_NAME( $analyser_program );
 
   if ( ! exists( $$PARAM::hr_error_codes{ $analyser_program_base_name } ) )
   {
      $$PARAM::hr_error_codes{ $analyser_program_base_name } = 
         LOAD_ERROR_CODES( $analyser_program );
   }

   my $hr_codes = $$PARAM::hr_error_codes{ $analyser_program_base_name };

   if ( ( ! defined( $hr_codes ) ) ||
        ( ! exists( $$hr_codes{ $error_code } ) )
      )
   {
      return ( "Analyser \"$analyser_program\" returns unknown code: $error_code",
               1,
               0,
               "<UNKNOWN CODE>" );
   }
   else
   {
      return ( "Analyser \"$analyser_program\" returns code: $error_code - " .
               $$hr_codes{ $error_code }[ 0 ],
               $$hr_codes{ $error_code }[ 1 ],
               $$hr_codes{ $error_code }[ 2 ],
               "<KNOWN CODE>" );
   }
}

# -------------------------------------------------------------------------

sub LOAD_ERROR_CODES
{
   # Usage: $hr_program_error_codes = LOAD_ERROR_CODES( $analyser_program );
   #
   # 1.
   # For a given $analyser_program the return codes file is loaded from 
   # the following location:
   #
   #   WHICH( $analyser_program ) . ".return_codes"
   #
   # If this is not found the home directory of QAW is searched ie:
   #
   #   DIR_NAME( $0 ) . BASE_NAME( $analyser_program ) . ".return_codes"
   #
   # 2.
   # $analyser_program . ".return_codes" has layout as follows:
   #  
   # <return code> <message string> <fail boolean> <perform diagnostics boolean>
   # 
   # Where:
   # 
   # <return code> is any string (can be quoted), e.g. 255
   # 
   # <message string> is text like: " Failed: Analysis error"
   # 
   # <fail boolean> ::= ( <FAIL> | <NO_FAIL> ),
   #          Is <FAIL> if the error, flagged by this return code,
   #          does not allow further processing of the source file.
   # 
   # <perform diagnostics boolean> ::= ( <DIAGNOSE> | <NO_DIAGNOSE> )
   #          Is <DIAGNOSE> if QAC/++ style diagnostics are to be performed.
   #          (This applies to nearly all post-analysis programs.
   #          ie For post-analysis value is <NO_DIAGNOSE>.)
   #
   my $analyser_program = shift;

   my $filename = GEN::WHICH( $analyser_program ) . ".return_codes";

   if ( ! -e $filename )
   {
      # Here when file not found in same dir as analyser program
      #
      # Try the QAW home dir
      #
      my $this_qaw_program_full_path = GEN::WHICH( $0 );

      $filename = GEN::DIR_NAME( $this_qaw_program_full_path ) .
                  GEN::BASE_NAME( $analyser_program ) .
                  ".return_codes";
   }

   my %program_error_codes = ();

   if ( ! -e $filename )
   {
      # Here when no .return_codes file.
      # In this case return empty codes file.  This will have the
      # effect that when an error is found using the given analyser,
      # unknown error will be returned by RUN::EXECUTE_POST_ANALYSIS(), etc.
      #
      return \%program_error_codes;
   }
      
   my @codes_records = GEN::LOAD_FILE_AS_ARRAY( $filename );

   RECORD:
   foreach my $record ( @codes_records )
   {
      chomp $record;

      # Skip empty lines and comment lines (Comment lines start with a *)
      #
      next RECORD if ( $record =~ /^(\*.*|\s*)$/o );

      my @values = GEN::SPLIT_STRING_ALLOWING_QUOTED_TERMS( " \t", $record );

      if ( ( ! defined( $values[ 3 ] ) ) ||
           ( scalar( @values ) > 4 )
         )
      {
         GEN::DIE( ": Illegal file $filename" );
      }

      $program_error_codes{ $values[ 0 ] } = [
         $values[ 1 ],
         int( $values[ 2 ] eq "<FAIL>" ),
         int( $values[ 3 ] eq "<DIAGNOSE>" ) ];
   }

   return \%program_error_codes;
}

# ---------------------------------------------------------------------------

sub LOOKUP_CUSTOM_REPORT_DETAILS
{
   # Usage: ( $prog, $param ) = LOOKUP_CUSTOM_REPORT_DETAILS( $report_name );
   #
   my $report_name = shift;

   if ( GEN::PLATFORM_IS_WINDOWS() )
   {
      my @custom_report_details = GET_CONFIG_SECTION( "Custom Reports" );
   
      CONFIG_LINE:
      foreach my $config_line ( @custom_report_details )
      {
   	 if ( $config_line =~ /CustRepName(\d+)=\Q$report_name\E/i )
   	 {
   	    my $index = $1;
   	    my $prog;
   	    my $param;
   
   	    my @cust_rep_exe = grep( /CustRepExe$index=.*/, @custom_report_details );
   
   	    if ( $#cust_rep_exe == 0 )
   	    {
   	       ( $prog = $cust_rep_exe[ 0 ] ) =~ s/CustRepExe$index=(.*)/$1/;
   	    }
   	    else
   	    {
   	       last CONFIG_LINE;
   	    }
   
   	    my @cust_rep_param = grep( /CustRepParam$index=.*/, @custom_report_details );
   
   	    if ( $#cust_rep_param == 0 )
   	    {
   	       ( $param = $cust_rep_param[ 0 ] ) =~ s/CustRepParam$index=(.*)/$1/;
   	    }
   	    else
   	    {
   	       last CONFIG_LINE;
   	    }
   
   	    return ( $prog, $param );
   	 }
      }
   }
   else
   {
      # Here with platform is Unix
      #
      my $prog;
      my $param;

      my @unix_custom_report_details = GET_UNIX_CONFIG_SECTION( "-customReport" );
   
      CONFIG_LINE:
      foreach my $config_line ( @unix_custom_report_details )
      {
         # Example .xqarc syntax is:
         #
         # -customreport"'name' 'options' 'executable' "

         # Get the report parameters, i.e. everything in the double quotes
         # that follows -customreport
         #
         my $param_line;
         ( $param_line = $config_line ) =~ s/.*?\"(.*?)\"/$1/o;

         # Get the individual parameters (in ' quotes)
         #
#         my @params = GEN::SPLIT_STRING_ALLOWING_SPECIFIC_QUOTED_TERMS
#                         ( " ", $param_line, "'" );
         my @params = GEN::SPLIT_COMMAND_LINE_STRING( $param_line );

         # Have we found a matching report record?
         #
         if ( $params[ 0 ] eq $report_name )
         {
            $prog  = $params[ 2 ];
            $param = $params[ 1 ];
         }
      }

      if ( defined( $prog ) && defined( $param ) )
      {
         return ( $prog, $param );
      }
   }

   GEN::DIE( ": No details for Configured Report \"$report_name\"." );
}

# ---------------------------------------------------------------------------

sub LOOKUP_PROJECT_ANALYSIS_DETAILS
{
   # Usage: ( $prog, $param ) = LOOKUP_PROJECT_ANALYSIS_DETAILS( $analyser_program_name );
   #
   my $analyser_program_name = shift;

   my $full_analyser_program_name = GEN::WHICH( $analyser_program_name );

   if ( $full_analyser_program_name eq "" )
   {
      GEN::DIE( ": Supplied project analysis file \"$analyser_program_name\" does not exist." );
   }

   if ( GEN::PLATFORM_IS_WINDOWS() )
   {
      my @project_analysis_details = GET_CONFIG_SECTION( "Project Analysis" );
   
      CONFIG_LINE:
      foreach my $config_line ( @project_analysis_details )
      {
         $config_line =~ /AnalysisExe(\d+)=(.*?)$/io;

         my $index = $1;
         my $prog  = $2;

         my $candidate_prog_full_path = GEN::WHICH( $prog );

         if ( GEN::FILES_ARE_SAME( $candidate_prog_full_path, $full_analyser_program_name ) )
         {
   	    my @additional_params = grep( /AdditionalParams$index=.*/, @project_analysis_details );
   
   	    my $param;
   
   	    if ( $#additional_params == 0 )
   	    {
   	       ( $param = $additional_params[ 0 ] ) =~ s/AdditionalParams$index=(.*)/$1/;
   	    }
   	    else
   	    {
   	       last CONFIG_LINE;
   	    }
   
   	    return ( $prog, $param );
   	 }
      }
   }
   else
   {
      # Here with platform is Unix
      #
      my $prog;
      my $param;

      my @unix_project_analysis_details = GET_UNIX_CONFIG_SECTION( "ProjectWideAnalysis" );
   
      CONFIG_LINE:
      foreach my $config_line ( @unix_project_analysis_details )
      {
         # Example .xqarc syntax is:
         #
         # ProjectWideAnalysis="on 'executable' 'options' "

         # Get the report parameters, i.e. everything in the double quotes
         # that follows ProjectWideAnalysis
         #
         my $param_line;
         ( $param_line = $config_line ) =~ s/.*?\"(.*?)\"/$1/o;

         # Get the individual parameters (in ' quotes)
         #
#         my @params = GEN::SPLIT_STRING_ALLOWING_SPECIFIC_QUOTED_TERMS
#                         ( " ", $param_line, "'" );
         my @params = GEN::SPLIT_COMMAND_LINE_STRING( $param_line );

         # Have we found a matching analysis record?
         # NB The 'executable' is the second parameter
         #

         my $candidate_prog_full_path = GEN::WHICH( $params[ 1 ] );

         if ( GEN::FILES_ARE_SAME( $candidate_prog_full_path, $full_analyser_program_name ) )
         {
            $prog  = $params[ 1 ];
            $param = $params[ 2 ];
         }
      }

      if ( defined( $prog ) && defined( $param ) )
      {
         return ( $prog, $param );
      }
   }

   GEN::DIE( ": No details for Project Analyser \"$analyser_program_name\"." );
}

# ---------------------------------------------------------------------------

sub GET_CONFIG_SECTION
{
   # Usage: @section_details = GET_CONFIG_SECTION( $section_name );
   #
   my $section_name = shift;

   if ( $#PARAM::windows_gui_ini_file == -1 )
   {
      my $filename = QA_BIN() . lc( QA_PROGRAM() ) . ".ini";

      @PARAM::windows_gui_ini_file = GEN::LOAD_FILE_AS_ARRAY( $filename );
   }

   my $index = 0;

   while ( $index <= $#PARAM::windows_gui_ini_file )
   {
      my $line = $PARAM::windows_gui_ini_file[ $index ];

      chomp $line;

      if ( $line eq "[$section_name]" )
      {
         my @section_details = ();
         ++$index;

         while ( $index <= $#PARAM::windows_gui_ini_file )
         {
            my $line = $PARAM::windows_gui_ini_file[ $index ];
   
            chomp $line;

            if ( GEN::LEFT( $line, 1 ) eq "[" )
            {
               return @section_details;
            }

            push @section_details, $line;
         
            ++$index;
         }

         return @section_details;
      }

      ++$index;
   }

   GEN::DIE( ": \"" . GEN::APPLY_FINAL_SEP( "<" . QA_PROGRAM() . ">" ) .
             GEN::APPLY_FINAL_SEP( "bin" ) . lc( QA_PROGRAM() ) . ".ini\"" .
             " section \"$section_name\" not found." );
}          

# ---------------------------------------------------------------------------

sub GET_UNIX_CONFIG_SECTION
{
   # Usage: @section_details = GET_UNIX_CONFIG_SECTION( $section_name );
   #
   # Returns all records in the appropriate .xqacrc | .xqacpprc file.
   #
   # NB xqac[pp] loads .xqac[pp]rc from $QAC[PP]PATH and then 
   # the current directory.
   # If there is no .xqac[pp]rc in the current directory, xqac[pp] 
   # loads .xqac[pp] from the User's home directory.
   #
   # NB When loading the second file, the contents are appended to those
   # already read.  Records later in the resultant file override any
   # previous records with the same key details.
   #
   my $section_name = shift;

   if ( $#PARAM::unix_xqarc_file == -1 )
   {
      my $xqa_rc = DOT_XQARC_FILE_NAME();

      my $filename = QA_PATH() . $xqa_rc;

      @PARAM::unix_xqarc_file = GEN::LOAD_FILE_AS_ARRAY( $filename );

      my @appended_config = ();

      if ( -e "./$xqa_rc" )
      {
         # Here with .xqac[pp]rc file in current directory
         #
         @appended_config = GEN::LOAD_FILE_AS_ARRAY( "./$xqa_rc" );
      }
      elsif ( -e "~/$xqa_rc" )
      {
         # Here with .xqac[pp]rc file in User's home directory
         #
         @appended_config = GEN::LOAD_FILE_AS_ARRAY( "~/$xqa_rc" );
      }

      push @PARAM::unix_xqarc_file, @appended_config;
   }

   my @section_details = grep( /^$section_name/i, @PARAM::unix_xqarc_file );

   if ( $#section_details != -1 )
   {
      return @section_details;
   }
   else
   {
      GEN::DIE( ": " . DOT_XQARC_FILE_NAME() . " section \"$section_name\" not found." );
   }
}          

# -- eof PARAM.pm -----------------------------------------------------------
