# ---------------------------------------------------------------------------
# 
# File:    MODE.pm
#
# Copyright (c) Programming Research, 2004.
#
# Written: C.Waddingham 20-Aug-04
#
# Version: 1.8
#
# Purpose: To provide dependency checking controlling QAC/PP tool execution.
#
# History:
#
# 1.8  26-01-12  CW  CR-14544: Write cwd to .opt file header.
# 1.7  21-03-11  CW  REFORMAT_PARAM updated to use SPLIT_COMMAND_LINE_STRING.
#                    - QAC DF nogen effect on -o and -n incorporated.
# 1.6  22-04-09  CW  - CR12636. Improve speed of -mode depend checking.
# 1.5  17-04-08  CW  EXECUTE_FOR_PROG_VERSION parameters updated.
# 1.4  09-02-06  CW  Incorrect handling of parameter paths with spaces
#                    in options like -maseq, -saseq, -exec, -glob - corrected.
# 1.3  11-07-05  CW  Filter added to post_anal_details and glob_anal_details.
# 1.2  25-03-05  CW  EVALUATE_GLOB_ANALYSIS_REQUIREMENT added.
# 1.1  23-02-05  CW  GEN::DATE Update.
#
# NOTE 
# The following are properties of .inc files:
# - Presence of .inc file => analysis (if current) has no syntax errors.
# - Absence of .inc file => analysis is necessary
# - .inc only and always written after successful analysis. => not affected
#   by -mode none.
# - .inc always deleted after failed analysis.
#
# ---------------------------------------------------------------------------

package MODE; #==============================================================

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

use strict;

require GEN;
require PARSE_CL;
require PARAM;

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

sub OPTION_IS_ANALYSIS_OPTION
{
   # Usage: $opt_is_analysis_option_bool = OPTION_IS_ANALYSIS_OPTION( $opt );
   #
   my $opt = shift;

   my $is_analysis_option = exists $PARAM::analysis_options{ $opt };

   if ( ! $is_analysis_option &&
        PARAM::VERSION_IS_LATER_THAN_OR_EQUAL_TO_QAC8_OR_QACPP2_5_2()
      )
   {
      $is_analysis_option = ( $opt eq "-o" ) || ( $opt eq "-n" );
   }

   return $is_analysis_option;
}

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

sub GET_MODE_OPTION
{
   # Usage: $mode_option = GET_MODE_OPTION( $mode_arg );
   #
   my $mode_arg = shift;

   if ( ! defined( $mode_arg ) )
   {
      # Here when no -mode specified
      #
      return "ALL";   # This is the default
   }

   $mode_arg = uc( $mode_arg );

   if ( ( $mode_arg eq "DEP" ) ||
        ( $mode_arg eq "DEPEND" )
      )
   {
      return "DEPEND";
   }
   elsif ( $mode_arg eq "NONE" )
   {
      return "NONE";
   }
   elsif ( $mode_arg eq "ALL" )
   {
      return "ALL";
   }

   GEN::DIE( ": Bad -mode parameter \"$mode_arg\"" );
}

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

sub EVALUATE_GLOB_ANALYSIS_REQUIREMENT
{
   # Usage: ( $perform_global_analysis, $log_msg ) =
   #      	MODE::EVALUATE_GLOB_ANALYSIS_REQUIREMENT
   #      	   ( $mode_option,
   #      	     $glob_analysis_program,
   #                 $glob_analysis_raw_param,
   #      	     $glob_output_file,
   #                 $ar_qa_opts,
   #                 $ar_file_names,
   #                 $ar_file_outputpaths,
   #                 $cmaf_path );
   #
   # If saved glob paramaters file from previous run not same as current 
   # (including %P) - do analysis.
   # If -cmaf specified, then if cmaf.err or cmaf.met is missing - do analysis.
   # If -cmaf specified and any .err or .met is newer than cmaf.err or cmaf.met
   # then do analysis.
   # If -glob doesn't have -file, then do analysis.
   # If -glob does have -file, and %L.err or %L.met is newer than -file arg
   # then do analysis.
   #
   my $mode_option             = shift;
   my $glob_analysis_program   = shift;
   my $glob_analysis_raw_param = shift;
   my $glob_output_file        = shift;
   my $ar_qa_opts              = shift;
   my $ar_file_names           = shift;
   my $ar_file_outputpaths     = shift;
   my $cmaf_path               = shift;

   my $glob_param_file;
   if ( $glob_output_file ne "" )
   {
      # Here when -glob has an output file, use this as
      # a basis for the name of the parameter file name.
      #
      $glob_param_file = PARAM::QA_RESULTS_TEMP() . 
                         GEN::BASE_NAME( $glob_output_file ) .
                         ".glob_opt";
   }
   else
   {
      # Here when -glob doesn't have an output file, so use 
      # parameter file name based on temp\prog.glob_opt.
      #
      $glob_param_file = PARAM::QA_RESULTS_TEMP(). 
                         GEN::BASE_NAME( $glob_analysis_program ) .
                         ".glob_opt";
   }

   # Load existing param file if present.
   #
   my $prev_analysis_parameters = "";

   if ( -e $glob_param_file )
   {
      $prev_analysis_parameters = GEN::LOAD_FILE_AS_STRING( $glob_param_file );

      # Trim off any trailing \n's
      #
      $prev_analysis_parameters =~ s/\n$//gox;
   }

   my $new_glob_analysis_parameters = 
      CALC_GLOB_ANALYSIS_PARAMETERS
         ( $glob_param_file,
           $glob_analysis_program,
           $glob_analysis_raw_param,
           $ar_qa_opts,
           $ar_file_names,
           $ar_file_outputpaths );

   # Write the new parameters
   #
   GEN::WRITE_FILE( $glob_param_file, $new_glob_analysis_parameters );

   if ( $mode_option eq "DEPEND" )
   {
      # Here when calculating dependencies
      #

      # Test whether previous analysis parameters match
      #
      if ( $new_glob_analysis_parameters ne $prev_analysis_parameters )
      {
         return ( 1,  # true - analysis required.
                  "" );
      }

      # Treat cmaf as a special case.
      # If program is pal, then test $ar_file_names timestamps against
      # oldest of: cmaf.err and cmaf.met.
      #

      my $prog = lc( GEN::BASE_NAME( $glob_analysis_program ) );

      if ( ( $prog eq "pal" ) ||
           ( $prog eq "pal.exe" )
         )
      {
         my $path = GEN::REMOVE_FINAL_SEP( $cmaf_path );

         my $cmaf_err = "$path.err";
         my $cmaf_met = "$path.met";

   	 if ( ( ! -e $cmaf_err ) ||
     	      ( ! -e $cmaf_met )
            )
         {
            return ( 1,  # true - analysis required
                     "" );
         }

         my $err_timestamp = GEN::FILE_TIMESTAMP( $cmaf_err );
         my $met_timestamp = GEN::FILE_TIMESTAMP( $cmaf_met );

         my $output_timestamp = GEN::MIN( $err_timestamp, $met_timestamp );

         my $perform_analysis = 
             TEST_FILES_TIMESTAMP
                ( $output_timestamp,
                  $ar_file_names,
                  $ar_file_outputpaths );

         if ( $perform_analysis )
         {
            return ( 1,   # true - analysis required
                     "" );
	 }
         else
         {
            return ( 0,   # false - analysis not required
                     "Analysis already current" );
         }
      }

      # If -file specified, then check timestamp of -file arg against
      # .met and .err files for all files in %L list, and only require
      # analysis if .met / .err newer than -file arg output file.
      #
      elsif ( ( $glob_output_file eq "" ) ||
              ( $glob_output_file eq "STDOUT" )
            )
      {
         # Here when there is no output file hence must always do
         # analysis.
         #
         return ( 1,  # true - analysis required
                  "" );
      }
      else
      {
         # Here with output_file specified (via -file).
         # File may exist if run before, if not then must perform analysis.
         #
         if ( ! -e $glob_output_file )
         {
            # No existing output so must run analysis
            #
            return ( 1,  # true - analysis required
                     "" );
         }

         # Here when previous output file exists.
         #

         # Check all files in $ar_file_names to see if any are newer than
         # $glob_output_file.
         #

         my $output_timestamp = GEN::FILE_TIMESTAMP( $glob_output_file );

         my $perform_analysis = 
             TEST_FILES_TIMESTAMP
                ( $output_timestamp,
                  $ar_file_names,
                  $ar_file_outputpaths );

         if ( $perform_analysis )
         {
            return ( 1,   # true - analysis required
                     "" );
	 }
         else
         {
            return ( 0,   # false - analysis not required
                     "Analysis already current" );
         }
      }
   }
   elsif ( $mode_option eq "NONE" )
   {
      return ( 0,  # False
               "No analysis" );
   }
   elsif ( $mode_option eq "ALL" )
   {
      return ( 1,  # True
               "" );
   }
   else
   {
      GEN::DIE( "::EVALUATE_GLOB_ANALYSIS_REQUIREMENT: Program error - Bad mode \"$mode_option\"." );
   }
}

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

sub CALC_GLOB_ANALYSIS_PARAMETERS
{
   # Usage: $glob_analysis_parameters = 
   #         	CALC_GLOB_ANALYSIS_PARAMETERS
   #         	   ( $filename,
   #                 $glob_analysis_program,
   #         	     $glob_analysis_raw_param,
   #                 $ar_qa_opts,
   #         	     $ar_file_names,
   #         	     $ar_file_outputpaths );
   #
   # Creates string comprising:
   #
   # * THIS FILE : $filename     
   # * GLOBAL ANALYSER : $glob_analysis_program Version $ver
   # * PARAMETERS : $glob_analysis_raw_param
   # * SETTINGS :
   # $ar_qa_opts
   # * FILES :
   # $ar_file_names
   # * OUTPUTPATHS :
   # $ar_file_outputpaths
   # * END : $filename
   #
   my $filename                = shift;
   my $glob_analysis_program   = shift;
   my $glob_analysis_raw_param = shift;
   my $ar_qa_opts              = shift;
   my $ar_file_names           = shift;
   my $ar_file_outputpaths     = shift;

   my $prog_version_string;

   if ( lc( GEN::STEM_NAME( $glob_analysis_program ) ) eq "viewer" )
   {
      $prog_version_string = "unobtainable";
   }
   else
   {
      $prog_version_string = 
         PARAM::EXECUTE_FOR_PROG_VERSION( $glob_analysis_program, "-ver" );
    
      $prog_version_string =~ s/^\s*(.*?)\s*$/$1/o;
   }

   my $glob_analysis_parameters = 
"* THIS FILE : $filename
* GLOBAL ANALYSER : $glob_analysis_program $prog_version_string
* PARAMETERS : $glob_analysis_raw_param
* SETTINGS :\n";

   if ( index( lc( $glob_analysis_raw_param ), "%p" ) != -1 )
   {
      # Don't want to include -exec analysis details as either:
      # - -exec analysis was not repeated because of mode -none
      #   or mode -depend or -sfba, in which case don't need to
      #   consider it now.
      # or
      # - -exec analysis was run in which case .err files will
      #   have been updated by repoeated primary analysi and
      #   hence the issue is academic as will have decided to
      #   repeat -glob analysis already.

      $glob_analysis_parameters .= join( "\n", @$ar_qa_opts ) . "\n";
   }

   $glob_analysis_parameters .= 
"* FILES :\n" .
join( "\n", @$ar_file_names ) . "
* OUTPUTPATHS :\n" .
join( "\n", @$ar_file_outputpaths ) . "
* END : $filename";

   return $glob_analysis_parameters;
}

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

sub TEST_FILES_TIMESTAMP
{
   # Usage: $analysis_required = 
   #          TEST_FILES_TIMESTAMP( $output_timestamp,
   #                                $ar_file_names,
   #                                $ar_file_outputpaths );
   #
   my $output_timestamp    = shift;
   my $ar_file_names       = shift;
   my $ar_file_outputpaths = shift;

   for ( my $i_file = 0; $i_file <= $#$ar_file_names; ++$i_file )
   {
      my $file_name = $$ar_file_names[ $i_file ];
      my $op        = $$ar_file_outputpaths[ $i_file ];
   
      $op                = GEN::APPLY_FINAL_SEP( $op );
      my $base_name      = GEN::BASE_NAME( $file_name );
      my $met_file_name  = $op . $base_name . ".met";
      my $err_file_name  = $op . $base_name . ".err";
   
      if ( $output_timestamp < GEN::FILE_TIMESTAMP( $file_name ) )
      {
         return 1;  # true - analysis required
      }
      elsif ( $output_timestamp < GEN::FILE_TIMESTAMP( $met_file_name ) )
      {
         return 1;  # true - analysis required
      }
      elsif ( $output_timestamp < GEN::FILE_TIMESTAMP( $err_file_name ) )
      {
         return 1;  # true - analysis required
      }
   }

   return 0;  # false - analysis not required.
}

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

sub EVALUATE_ANALYSIS_REQUIREMENT
{
   # Usage: $perform_analysis = 
   #        EVALUATE_ANALYSIS_REQUIREMENT( $mode_option, 
   #                                       $file_name,
   #                                       $file_outputpath,
   #                                       $ar_qa_opts,
   #                                       $ar_post_analysers_spec,
   #                                       $ar_post_analysis_details,
   #                                       $ar_global_analysis_details );
   #
   my $mode_option                = shift;
   my $file_name                  = shift;
   my $file_outputpath            = shift;
   my $ar_qa_opts                 = shift;
   my $ar_post_analysers_spec     = shift;
   my $ar_post_analysis_details   = shift;
   my $ar_global_analysis_details = shift;

   if ( $mode_option eq "DEPEND" )
   {
      # Here when calculating dependencies
      #
      my $op             = GEN::APPLY_FINAL_SEP( $file_outputpath );
      my $base_name      = GEN::BASE_NAME( $file_name );
      my $met_file_name  = $op . $base_name . ".met";
      my $err_file_name  = $op . $base_name . ".err";
      my $opt_file_name  = $op . $base_name . ".opt";
      my $inc_file_name  = $op . $base_name . ".inc";
   
      my $perform_analysis = ! ( ( -e $met_file_name ) &&
      			         ( -e $err_file_name ) &&
   		       	         ( -e $opt_file_name ) &&
                                 ( -e $inc_file_name )
   		               );

      if ( ! $perform_analysis )
      {
         $perform_analysis = ANALYSIS_OPTIONS_DIFFER
            ( $opt_file_name,
              $ar_qa_opts,
              $ar_post_analysers_spec,
              $ar_post_analysis_details,
              $ar_global_analysis_details );

         if ( ! $perform_analysis )
         {
            my $analysis_timestamp = GEN::FILE_TIMESTAMP( $err_file_name );

   	    $perform_analysis = IS_TRANSLATION_UNIT_UPDATED
                                   ( $file_name, 
                                     $inc_file_name,
                                     $analysis_timestamp );
         }
      }

      return $perform_analysis;
   }
   elsif ( $mode_option eq "NONE" )
   {
      return 0;  # False
   }
   elsif ( $mode_option eq "ALL" )
   {
      return 1;  # True
   }
   else
   {
      GEN::DIE( "::EVALUATE_ANALYSIS_REQUIREMENT: Program error - Bad mode \"$mode_option\"." );
   }
}

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

sub ANALYSIS_OPTIONS_DIFFER
{
   # Usage: $perform_analysis = ANALYSIS_OPTIONS_DIFFER
   #                            ( $opt_file_name, 
   #                              $ar_qa_opts,
   #                              $ar_post_analysers_spec,
   #                              $ar_post_analysis_details,
   #                              $ar_global_analysis_details
   #                            );
   #
   my $opt_file_name              = shift;
   my $ar_qa_opts                 = shift;
   my $ar_post_analysers_spec     = shift;
   my $ar_post_analysis_details   = shift;
   my $ar_global_analysis_details = shift;

   # Example .opt file header.  Note this sub relies on the order of lines in header:
   #
   # *--------------------------------------------------------------------------------------
   # * THIS FILE        : C:\Program Files\PRQA\QAC-5.0.1\mcm\bin\test\output\rule_101.c.opt
   # * FILE ANALYSED    : C:\Program Files\PRQA\QAC-5.0.1\mcm\bin\test\src\rule_101.c
   # * CURRECT DIRECTORY: C:\Fred\Blogs
   # * ANALYSIS TIME    : 03-Apr-04 17:21:01
   # * QAW VERSION      : qacw 1.19
   # * PRIMARY ANALYSER : QAC 5.0.1
   # * POST ANALYSER    : qacuser Version 1.0
   # *--------------------------------------------------------------------------------------
   # -ar+
   # ...
   #

   my $qa_program = PARAM::QA_PROGRAM();
   my $qa_version = PARAM::QA_VERSION();

   my @opts_previous = GEN::LOAD_FILE_AS_ARRAY( $opt_file_name );

   # Ensure that file has a header
   #
   if ( scalar( @opts_previous ) < 6 )
   {
      # Here with header without primary analyser spec
      #
      return 1;  # true ie analysis necessary
   }

   # Check the version of qaw
   #
   my $qaw_spec;
   ( $qaw_spec = $opts_previous[ 5 ] ) =~ s/^\* QAW VERSION      : //o;
   chomp $qaw_spec;
   my $this_qaw_spec = uc( PARAM::EXECUTABLE_NAME() ) . " " . PARAM::QAW_VERSION();

   if ( $qaw_spec ne $this_qaw_spec )
   {
      return 1;   # true ie QAW differs, analysis necessary
   }

   # Check the primary analyser spec
   #
   my $primary_analyser_spec;
   ( $primary_analyser_spec = $opts_previous[ 6 ] ) =~ s/^\* PRIMARY ANALYSER : //o;
   chomp $primary_analyser_spec;

   if ( $primary_analyser_spec ne "$qa_program $qa_version" )
   {
      return 1;  # true ie analysis necessary
   }   

   # Check the post analysers spec
   #
   my $cursor = 6;
   foreach my $post_analyser_spec ( @$ar_post_analysers_spec )
   {
      ++$cursor;

      if ( ! defined( $opts_previous[ $cursor ] ) )
      {
         return 1;   # true ie post analysers differ, analysis necessary
      }

      my $prev_post_analyser = $opts_previous[ $cursor ];
      chomp $prev_post_analyser;
      ( $prev_post_analyser = $prev_post_analyser ) =~ s/^.*?: //o;

      if ( $prev_post_analyser ne $post_analyser_spec )
      {
   	 return 1;  # true ie analysis necessary
      }   
   }

   my @opts_prev = ();

   # Remove comments and chomp
   #
   foreach my $opt ( @opts_previous )
   {
      if ( substr( $opt, 0, 1 ) ne "*" )
      {
         chomp $opt;
         push @opts_prev, $opt;
      }
   }

   # Filter out non-analysis options
   #
   my $prev_opts = FILTER_ANALYSIS_ONLY_OPTIONS( \@opts_prev );

   # Merge -exec and -glob options (as these were written to opt_file)
   #
   my @exec_and_glob_options = GET_EXEC_AND_GLOB_OPTIONS
      ( $ar_post_analysis_details,
        $ar_global_analysis_details );  

   # Rely on opts being quoted as done when writing prev opts.
   #
   my @new_opts = ( @$ar_qa_opts, @exec_and_glob_options );

   # Filter out non-analysis options
   #
   my $qa_opts = FILTER_ANALYSIS_ONLY_OPTIONS( \@new_opts );

   return $prev_opts ne $qa_opts;
}            

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

sub FILTER_ANALYSIS_ONLY_OPTIONS
{
   # Usage: $filtered_options = FILTER_ANALYSIS_ONLY_OPTIONS( $ar_options );
   #
   my $ar_options = shift;

   my $filtered_options = "";
   my $i_opt  = 0;

   while ( $i_opt <= $#$ar_options )
   {
      my ( $opt, $param, $dn_opt, $error ) = PARSE_CL::GET_OPT( $i_opt, $ar_options );

      if ( $dn_opt != 1 )
      {
         GEN::DIE( "::FILTER_ANALYSIS_ONLY_OPTIONS: Error $error - Bad option \"$opt\"." );
      }

      if ( OPTION_IS_ANALYSIS_OPTION( $opt ) &&
           ( $opt ne "-glob" )
         )
      {
         $filtered_options .= PARSE_CL::FULL_OPTION( $opt, $param );
      }

      $i_opt += $dn_opt;
   }

   return $filtered_options;
}

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

sub IS_TRANSLATION_UNIT_UPDATED
{
   # Usage: $perform_analysis = IS_TRANSLATION_UNIT_UPDATED
   #                               ( $file_name,
   #                                 $inc_file_name,
   #                                 $analysis_timestamp );
   #
   my $file_name          = shift;
   my $inc_file_name      = shift;
   my $analysis_timestamp = shift;

   # Check main file
   #
   if ( GEN::FILE_TIMESTAMP( $file_name ) > $analysis_timestamp )
   {
      return 1;   # true ie file updated
   }

   # Check included files
   #
   my @inc_file = GEN::LOAD_FILE_AS_ARRAY( $inc_file_name );

   foreach my $line ( @inc_file )
   {
      # Here with include file record.
      #
      my ( $key, $including_file, $included_file, $line_no ) =
         split( "\t", $line );
   
      if ( ! defined( $line_no ) )
      {
         GEN::DIE( "::IS_TRANSLATION_UNIT_UPDATED Bad file record \"$line\" in file \"$inc_file_name\"." );
      }

      if ( GEN::FILE_TIMESTAMP( $included_file ) > $analysis_timestamp )
      {
         return 1;   # true ie file updated
      }
   }
         
   return 0;   # File(s) are not updated.
}

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

sub WRITE_OPT_FILE
{
   # Usage: WRITE_OPT_FILE( $file_name,
   #                        $file_outputpath,
   #                        $ar_qa_opts,
   #                        $ar_post_analysers_spec,
   #                        $ar_post_analysis_details,
   #                        $ar_global_analysis_details );
   #
   my $file_name                  = shift;
   my $file_outputpath            = shift;
   my $ar_qa_opts                 = shift;
   my $ar_post_analysers_spec     = shift;
   my $ar_post_analysis_details   = shift;
   my $ar_global_analysis_details = shift;

   my @qa_opts = @$ar_qa_opts;
   my $qa_program = PARAM::QA_PROGRAM();
   my $qa_version = PARAM::QA_VERSION();

   my $opt_file = GEN::GET_FILE_PATH_ABSOLUTE
                     ( GEN::APPLY_FINAL_SEP( $file_outputpath ) . 
                       GEN::BASE_NAME( $file_name ) .
                       ".opt"
                     );
   $opt_file = GEN::EVALUATE_RELATIVE_PATHS( $opt_file );

   my $cwd = GEN::PWD();

   my @heading = ();
   $heading[ 0 ] = "* THIS FILE        : $opt_file";
   $heading[ 1 ] = "* FILE ANALYSED    : $file_name";
   $heading[ 2 ] = "* CURRENT DIRECTORY: $cwd";
   $heading[ 3 ] = "* ANALYSIS TIME    : " . GEN::DATE_DD_MMM_YY() . " " . GEN::TIME();
   $heading[ 4 ] = "* QAW VERSION      : " . uc( PARAM::EXECUTABLE_NAME() ) .
                                         " " . PARAM::QAW_VERSION();
   $heading[ 5 ] = "* PRIMARY ANALYSER : $qa_program $qa_version";

   foreach my $post_analyser_spec ( @$ar_post_analysers_spec )
   {
      my $rec = "* POST ANALYSER    : $post_analyser_spec";
      push @heading, $rec;
   }

   my $len = 0;

   foreach my $line ( @heading )
   {
      if ( length( $line ) > $len )
      {
         $len = length( $line );
      }
   }

   my $line = "*" . "-" x ( $len - 1 );

   # -rem, -exec and -glob options have been parsed into @$ar_post_analysis_details
   # and @$ar_global_analysis_details as opposed to @qa_opts.
   # Merge them here for the purposes of writing the .opt file.

   my @exec_and_glob_options = GET_EXEC_AND_GLOB_OPTIONS
      ( $ar_post_analysis_details,
        $ar_global_analysis_details );  

   push @qa_opts, @exec_and_glob_options;

   # Rely on any opts with embedded spaces being already surrounded by quotes
   #  
   GEN::WRITE_FILE( $opt_file, ( $line, @heading, $line ), @qa_opts );
}

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

sub GET_EXEC_AND_GLOB_OPTIONS
{
   # Usage: @exec_and_glob_options = GET_EXEC_AND_GLOB_OPTIONS
   #                                    ( $ar_post_analysis_details,
   #                                      $ar_global_analysis_details );  
   #
   my $ar_post_analysis_details   = shift;
   my $ar_global_analysis_details = shift;

   my @exec_and_glob_options = ();

   foreach my $post_analysis_record ( @$ar_post_analysis_details )
   {
      my ( $prog, $params, $error_prog, $filter ) = split( "\n", $post_analysis_record );

      # Convert form of any included quoted parameters in glob_raw_param,
      # since glob_raw_param may include quoted parameters which have
      # been converted from parameters with embedded spaces.
      #
      my $reformatted_params = REFORMAT_PARAM( $params );

      push @exec_and_glob_options, 
           PARSE_CL::FULL_OPTION( "-exec", "$prog##$reformatted_params" );
   }

   foreach my $glob_anal_details ( @$ar_global_analysis_details )
   {
      my ( $glob_analysis_program,
      	   $glob_analysis_params,
      	   $glob_analysis_renderer,
      	   $glob_renderer_param,
           $glob_raw_param,
           $glob_filter ) = split( "\n", $glob_anal_details );
      
      # Convert form of any included quoted parameters in glob_raw_param,
      # since glob_raw_param may include quoted parameters which have
      # been converted from parameters with embedded spaces.
      #
      my $reformatted_glob_raw_param = REFORMAT_PARAM( $glob_raw_param );

      push @exec_and_glob_options,
           PARSE_CL::FULL_OPTION( "-glob", 
                                  "$glob_analysis_program#" .
			          "$reformatted_glob_raw_param#" .
			          "$glob_analysis_renderer" );
   }

   return @exec_and_glob_options;
}

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

sub REFORMAT_PARAM
{
   # Usage: $reformatted_raw_param = REFORMAT_PARAM( $raw_param );
   #
   # Convert form of any included quoted parameters in glob_raw_param,
   # since glob_raw_param may include quoted parameters which have
   # been converted from parameters with embedded spaces.
   #
   # i.e. Option may have been supplied by the user as:
   # -maseq "/bin/space\ /prog %Q -file /space\ /fil.html"
   #
   # Parameters will have been formatted into $glob_raw_param as:
   # $glob_raw_param = '%Q -file "/space /fil.html"'
   #
   # Hence now find all terms, and reformat using '\ ' for spaces.
   #
   # Also have to deal with # chars in each term.  These must also
   # be encoded.
   #
   my $raw_param = shift;

   # Split $raw_param into list of terms
   #
#   my @list_of_terms = GEN::SPLIT_STRING_ALLOWING_QUOTED_TERMS( " ", $raw_param );
   my @list_of_terms = GEN::SPLIT_COMMAND_LINE_STRING( $raw_param );

   # Reconstruct the list of terms with encoded spaces
   #
   my $reformatted_raw_param = "";
   my $separator = "";

   foreach my $term ( @list_of_terms )
   {
      $reformatted_raw_param .= $separator;
      $separator = " ";

      $term =~ s/ /\\ /go;
     
      $term =~ s/#/\\#/go;
 
      $reformatted_raw_param .= $term;
   }

   return $reformatted_raw_param;
}

# -- eof MODE.pm ------------------------------------------------------------
