# ---------------------------------------------------------------------------
# 
# File:    GEN.pm
#
# Copyright (c) Programming Research, 2004.
#
# Version: 1.21
#
# Written: C.Waddingham 22-June-03
#
# Purpose: To provide utility functions for qaw.pl, qa_user, etc.
#
# History  : 
#   Date      Version By   Comment
#   --------  ------- ---- ----------------------------------------------
#   21-03-11  1.21    CW   SPLIT_COMMAND_LINE_STRING added.
#                          SPLIT_COMMAND_LINE_STRING_WINDOWS added.
#   31-01-11  1.20    CW   GET_GLOB_FILE_LIST_CW added.
#   23-07-08  1.19    CW   APPEND_FILE improved.
#   24-10-06  1.18    CW   CR11593. EXECUTE_ARGS & EXECUTE_ARGS_WITH_RESULT added.
#   05-12-05  1.17    CW   TRUNCATE_FILE added.
#                          UNIX_2_DOS added.
#   24-09-05  1.16    CW   EXECUTE_PROGRAM_FOR_STDOUT added.
#   11-05-05  1.15    CW   FLUSH_STDERR, FLUSH_STDOUT added.
#   25-03-05  1.14    CW   DIEX added.
#   23-01-05  1.13    CW   Restructured splitting into sub packages and
#                          merging with functionality from CMS UTIL.
#   12-01-05  1.12    CW   - OS tested as MSWin.
#                          - Handling added for Cygwin.
#   23-12-04  1.11    CW   SPLIT_STRING_ALLOWING_QUOTED_TERMS implemented 
#                          in terms of:
#                          SPLIT_STRING_ALLOWING_SPECIFIC_QUOTED_TERMS
#                          which allows quote char to be supplied.
#   05-11-04  1.10    CW   Added:
#                            EXIT, DELETE_FILE_OR_DIR,
#                            CREATE_NEW_DIRECTORY, SEARCH, 
#                            MIXED_TYPE_GT, WHICH,
#                            EXECUTABLE_FULL_NAME
#   09-09-04  1.9     CW   Restructured for QAW.
#   10-05-04  1.8     CW   GET_GLOB_FILE_LIST handling of filename with
#                          embedded spaces improved.
#   03-05-04  1.7     CW   TRACE interface updated. QUOTE_ARG updated.
#                          COUNT_STRING_ALLOWING_ESCAPES added.
#                          GET_GLOB_FILE_LIST added.
#   01-04-04  1.6     CW   EVALUATE_SYMBOLIC_LINK added.
#   02-02-04  1.5     CW   TRIM corrected.
#   01-02-04  1.4     CW   UNQUOTE added
#   11-11-03  1.3     CW   EXECUTE_SYSTEM_COMMAND_WITH_RESULT corrected.
#   08-11-03  1.2     CW   "." removed in EVALUATE_RELATIVE_PATHS
#   02-10-03  1.1     CW   Added IS_QUOTED and 
#                          SPLIT_STRING_ALLOWING_QUOTED_TERMS
#   26-06-03  1.0     CW   Initial version.
# ---------------------------------------------------------------------------

package GEN; #===============================================================

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

BEGIN
{
   use integer;
   use strict;

   require GEN_SYS;
   require GEN_STRING;
   require GEN_FILE;
   require GEN_PATH;
   require GEN_SORT;
   require GEN_TIME;

   our $path_separator = GEN_SYS::GET_PATH_SEPARATOR();
   our $path_delimiter = GEN_SYS::GET_PATH_DELIMITER();

   our $platform_is_cygwin  = GEN_SYS::PLATFORM_IS_CYGWIN();
   our $platform_is_unix    = GEN_SYS::PLATFORM_IS_UNIX();
   our $platform_is_windows = GEN_SYS::PLATFORM_IS_WINDOWS();
}

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

sub APPEND_FILE
{
   # Usage: APPEND_FILE( $file_name, @text );
   #
   my $file_name = shift;
   my @text = @_;

   GEN_FILE::APPEND_FILE( UNQUOTE( $file_name ), @text );
}

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

sub APPLY_FINAL_SEP
{
   # Usage: $qabin = APPLY_FINAL_SEP( $QACBIN );
   #
   return GEN_PATH::APPLY_FINAL_SEP( shift );
}

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

sub BASE_NAME
{
   # Usage $stem_and_extension = BASE_NAME( $path );
   #
   return GEN_PATH::BASE_NAME( shift );
}

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

sub CAPITALIZE_WORD
{
   # Usage: $string_with_first_letter_upper_case = CAPITALIZE_WORD( $str );
   #
   return GEN_STRING::CAPITALIZE_WORD( shift );
}

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

sub CLOSE_TRACE
{
   # Usage: CLOSE_TRACE();
   #
   GEN_SYS::CLOSE_TRACE();
}

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

sub COPY_DIRECTORY
{
   # Usage: COPY_DIRECTORY( $source_dir, $target_dir );
   #
   # Copies every file in source_dir to target_dir.
   # Assumes that there are no nested directories, hence does not recurse.
   #
   GEN_FILE::COPY_DIRECTORY( @_ );
}

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

sub COPY_FILE
{
   # Usage COPY_FILE( $source_file_name, $target_file_name );
   #
   GEN_FILE::COPY_FILE( @_ );
}

#------------------------------------------------- COPY_FILE_TO_DIRECTORY --

sub COPY_FILE_TO_DIRECTORY
{
   # Usage COPY_FILE_TO_DIRECTORY( $source_file_name, $target_dir_name );
   #
   GEN_FILE::COPY_FILE_TO_DIRECTORY( @_ );
}

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

sub COUNT_STRING
{
   # Usage: $count = COUNT_STRING( $str, $s );
   #
   # Counts the number of $s strings in $str
   #
   return GEN_STRING::COUNT_STRING( @_ );
}
   
#---------------------------------------------------------------------------

sub COUNT_STRING_ALLOWING_ESCAPES
{
   # Usage: $hash_count = GEN::COUNT_STRING_ALLOWING_ESCAPES( $str, $char );
   #
   # NB Cannot handle $char eq '\n'
   #
   return GEN_STRING::COUNT_STRING_ALLOWING_ESCAPES( @_ );
}

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

sub CREATE_NEW_DIRECTORY
{
   # Usage: $dir_name = CREATE_NEW_DIRECTORY
   #            ( $name_template,
   #              $parent_directory );
   #
   # Creates a directory with name based on $name_template within
   # directory $parent_directory.
   # Returns the path of the created directory.
   #
   return GEN_SYS::CREATE_NEW_DIRECTORY( @_ );
}

#--------------------------------------------------------- DATE_DD_MMM_YY --

sub DATE_DD_MMM_YY
{
   # Usage: $date_dd_mmm_yy = DATE_DD_MMM_YY();
   #
   # Note this sub is based on localtime.  Other possibility is gmtime.
   # Others?
   #
   # NB sub DATE() deleted from GEN at 23-01-05 as it is identical to this
   # sub except that DATE() was based on gmtime.
   #
   return GEN_TIME::DATE_DD_MMM_YY();
}

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

sub DATE_ISO_8601
{
   # Usage: $$date_yyyy_mm_dd = DATE_ISO_8601()
   #
   # Note this sub is based on localtime.  Other possibility is gmtime.
   # Others?
   #
   return GEN_TIME::DATE_ISO_8601();
}

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

sub DELETE_DIRECTORY_FILE_CONTENT
{
   # Usage: DELETE_DIRECTORY_FILE_CONTENT( $directory_name );
   #
   # Tidy up the supplied directory.
   # i.e. delete all files but no subdirectories nor directory itself.
   #
   GEN_FILE::DELETE_DIRECTORY_FILE_CONTENT( shift );
}

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

sub DELETE_DIRECTORY_RECURSIVE
{
   # Usage: DELETE_DIRECTORY_RECURSIVE( $subject_dir );
   #
   GEN_FILE::DELETE_DIRECTORY_RECURSIVE( shift );
}

#----------------------------------------------------- DELETE_FILE --

sub DELETE_FILE
{
   # Usage: DELETE_FILE( $file_path );
   #
   GEN_FILE::DELETE_FILE( shift );
}

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

sub DELETE_FILE_OR_DIRECTORY
{
   # Usage: DELETE_FILE_OR_DIRECTORY( $file_or_dir_name );
   #
   GEN_FILE::DELETE_FILE_OR_DIRECTORY( shift );
}

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

sub DIE
{
   # Usage: DIE( $str );
   #
   GEN_SYS::DIE( shift );
}

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

sub DIEX
{
   # Usage: DIEX( $str, $return_code );
   #
   GEN_SYS::DIEX( @_ );
}

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

sub DIRECTORY_EQUALITY
{
   # Usage: DIRECTORY_EQUALITY( $dir_a, $dir_b );
   #
   # Lists to standard output all files in $dir_a and $dir_b that are
   # different.  Is recursive.
   #
   GEN_FILE::DIRECTORY_EQUALITY( @_ );
}
 
#------------------------------------------------------------- DIR_EXISTS --

sub DIR_EXISTS 
{
   # 
   # Usage: $true_false = DIR_EXISTS( $dir_name );
   #
   return GEN_PATH::DIR_EXISTS( shift );
}

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

sub DIR_NAME
{
   # Usage: $directory_of_file_path = DIR_NAME( $path );
   #
   return GEN_PATH::DIR_NAME( shift );
}

# ------------------------------------------------------------ DOS_2_UNIX --

sub DOS_2_UNIX
{
   # Usage: DOS_2_UNIX( $full_file_name );
   #
   GEN_FILE::DOS_2_UNIX( shift );
}

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

sub EVALUATE_RELATIVE_PATHS
{
   # Usage: $simple_path = EVALUATE_RELATIVE_PATHS( $path );
   #
   # Removes embedded .. (ie up one level) specifiers from $path, and
   # . (i.e. current directory)
   #
   return GEN_PATH::EVALUATE_RELATIVE_PATHS( shift );
}

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

sub EVALUATE_SYMBOLIC_LINK
{
   # Usage: $filename = EVALUATE_SYMBOLIC_LINK( $filename );
   #
   # If supplied $filename is a symbolic link, this sub returns the path
   # of the file that is linked.
   # If supplied $filename is a plain file, it is returned unchanged.
   #
   return GEN_PATH::EVALUATE_SYMBOLIC_LINK( shift );
}

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

sub EXECUTABLE_EXISTS
{
   # Usage: $boolean_exists = EXECUTABLE_EXISTS( $name );
   #
   # Simply use return WHICH( $name ) ne "";
   # as WHICH() will find any matching executable on Unix,
   # or any program or non-executable matching $name in PATH.
   #
   return GEN_PATH::EXECUTABLE_EXISTS( shift );
}

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

sub EXECUTABLE_FULL_NAME
{
   # Usage: $full_program_name = EXECUTABLE_FULL_NAME( $program_name );
   #
   return GEN_PATH::EXECUTABLE_FULL_NAME( shift );
}

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

sub EXECUTE_ARGS
{
   # Usage: EXECUTE_ARGS( @args );
   #
   GEN_SYS::EXECUTE_ARGS( @_ );
}

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

sub EXECUTE_ARGS_WITH_RESULT
{
   # Usage: $result_code = EXECUTE_ARGS_WITH_RESULT( @args );
   #
   return GEN_SYS::EXECUTE_ARGS_WITH_RESULT( @_ );
}

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

sub EXECUTE_PROGRAM
{
   # Usage: EXECUTE_PROGRAM( $prog_name, $args );
   #
   GEN_SYS::EXECUTE_PROGRAM( @_ );
}
   
# ---------------------------------------------------------------------------

sub EXECUTE_PROGRAM_FOR_STDOUT
{
   # Usage: $stdout = EXECUTE_PROGRAM_FOR_STDOUT( $prog_name, $args );
   #
   return GEN_SYS::EXECUTE_PROGRAM_FOR_STDOUT( @_ );
}
   
# ---------------------------------------------------------------------------

sub EXECUTE_PROGRAM_WITH_RESULT
{
   # Usage: $result_code = EXECUTE_PROGRAM_WITH_RESULT( $prog_name, $args );
   #
   return GEN_SYS::EXECUTE_PROGRAM_WITH_RESULT( @_ );
}
   
# ---------------------------------------------------------------------------

sub EXECUTE_SYSTEM_COMMAND
{
   # Usage: EXECUTE_SYSTEM_COMMAND( $command );
   #
   GEN_SYS::EXECUTE_SYSTEM_COMMAND( shift );
}

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

sub EXECUTE_SYSTEM_COMMAND_WITH_RESULT
{
   # Usage $return_code = EXECUTE_SYSTEM_COMMAND_WITH_RESULT( $command );
   #
   # Execute the command and divide the result by 256 as the lower byte
   # has the signal number that killed the process (if any) and the eighth
   # bit indicates whether the process dumped core.
   #
   return GEN_SYS::EXECUTE_SYSTEM_COMMAND_WITH_RESULT( shift );
}

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

sub EXIT
{
   # Usage: EXIT( $code );
   #
   GEN_SYS::EXIT( shift );
}

#------------------------------------------------------------ EXPAND_TABS --

sub EXPAND_TABS
{
   # Usage: $tab_expanded_line = EXPAND_TABS( $line, $interval );
   #
   return GEN_STRING::EXPAND_TABS( @_ );
}

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

sub FILES_ARE_SAME
{
   # Usage: $are_same_bool = FILES_ARE_SAME( $filename_a, $filename_b );
   #
   # Uses 'stat' to get inode of each file, where files are specified by
   # their path.  Files are the same file if the inode for each is the same
   #
   return GEN_FILE::FILES_ARE_SAME( @_ );
}

# --------------------------------------------------------- FILE_EQUALITY --

sub FILE_EQUALITY
{
   # Usage: $true_if_equal = FILE_EQUALITY( $file_a, $file_b );
   #
   return GEN_FILE::FILE_EQUALITY( @_ );
}

#------------------------------------------------------------ FILE_EXISTS --

sub FILE_EXISTS 
{
   # Usage: $true_false = FILE_EXISTS( $file_name );
   #
   return GEN_PATH::FILE_EXISTS( shift );
}

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

sub FILE_EXTENSION
{
   # Usage $extn = FILE_EXTENSION( $path );
   #
   # Given (say) "\user\local\stem.extn", returns ".extn"
   #
   return GEN_PATH::FILE_EXTENSION( shift );
}

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

sub FILE_REF
{
   # Usage: $file_ref = FILE_REF( $filename );
   #
   # Returns SHORT_PATH_NAME for Windows files, or inode for Unix files.
   #
   return GEN_PATH::FILE_REF( shift );
}

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

sub FILE_TIMESTAMP
{
   # Usage: $file_timestamp = FILE_TIMESTAMP( $file_name );
   #
   return GEN_FILE::FILE_TIMESTAMP( shift );
}

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

sub FILL
{
   # Usage: $str = FILL( $char, $nch );
   #
   return GEN_STRING::FILL( @_ );
}

# --------------------------------------------------------- FILTER_STRING --

sub FILTER_STRING
{
   # Usage: $filtered_string = FILTER_STRING( $str, $filter );
   #
   # Returns all characters in str that are in filter
   #
   return GEN_STRING::FILTER_STRING( @_ );
}

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

sub FLUSH_STDERR
{
   # Usage: FLUSH_STDERR();
   #
   GEN_SYS::FLUSH_STDERR();
}

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

sub FLUSH_STDOUT
{
   # Usage: FLUSH_STDOUT();
   #
   GEN_SYS::FLUSH_STDOUT();
}

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

sub FORCE_SEPARATORS
{
   # e.g. $qabin = FORCE_SEPARATORS( $QACBIN, "/" );
   #
   return GEN_PATH::FORCE_SEPARATORS( @_ );
}

#------------------------------------------------------------ FORMAT_TIME --

sub FORMAT_TIME
{
   # Usage: $formatted_time = FORMAT_TIME( $time );
   #
   # Returns the supplied time formatted.
   #
   return GEN_TIME::FORMAT_TIME( shift );
}

# ---------------------------------------------- GET_ALL_SUB_DIR_PATHS_IN --

sub GET_ALL_SUB_DIR_PATHS_IN
{
   # Usage @list_of_sub_paths_of_each_sub_dir = 
   #           GET_ALL_SUB_DIR_PATHS_IN( $dir_path );
   #
   return GEN_PATH::GET_ALL_SUB_DIR_PATHS_IN( @_ );
}

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

sub GET_DIRECTORY_ENTRIES
{
   # Usage @entries = GET_DIRECTORY_ENTRIES( $dir_path );
   #
   return GEN_PATH::GET_DIRECTORY_ENTRIES( shift );
}

# ------------------------------------------------------ GET_DIR_NAMES_IN --

sub GET_DIR_NAMES_IN
{
   # Usage @list_of_names_of_each_sub_dir = GET_DIR_NAMES_IN( $dir_path );
   #
   return GEN_PATH::GET_DIR_NAMES_IN( shift );
}

# ----------------------------------------------------- GET_FILE_NAMES_IN --

sub GET_FILE_NAMES_IN
{
   # Usage @list_of_paths_to_each_file = GET_FILE_NAMES_IN( $dir_path );
   #
   return GEN_PATH::GET_FILE_NAMES_IN( shift );
}

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

sub GET_FILE_PATH_ABSOLUTE
{
   # Usage $path_adjusted = GET_FILE_PATH_ABSOLUTE( $path );
   #
   # If $path is relative, returns as absolute. 
   # If absolute returns unchanged.
   #
   return GEN_PATH::GET_FILE_PATH_ABSOLUTE( shift );
}

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

sub GET_GLOB_FILE_LIST
{
   # Usage: ( $ar_file_list, $error_string ) = GET_GLOB_FILE_LIST( $filespec );
   #
   # If $filespec does not contain spaces, performs glob() on $filespec.
   #
   # NB glob() cannot handle filespec with spaces.
   #
   # Checks files exist and returns file_list and error status
   # error is returned true (1) if any errors are reported.
   #
   return GEN_PATH::GET_GLOB_FILE_LIST( shift );
}

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

sub GET_GLOB_FILE_LIST_CW
{
  # Usage: $ar_filepath_list = GET_GLOB_FILE_LIST_CW( $filepath_spec );
  #
  # Similar to Perl glob but only returns 'file' entries.
  #
  # NB glob() cannot handle filespec with spaces.
  # GET_GLOB_FILE_LIST_CW can handle filespec with spaces.
  #
  return GEN_PATH::GET_GLOB_FILE_LIST_CW( shift );
}

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

sub GET_PATH_DELIMITER
{
   # Usage $path_delimiter = GET_PATH_DELIMITER();
   #
   return $GEN::path_delimiter;
}


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

sub GET_PATH_SEPARATOR
{
   # Usage $path_separator = GET_PATH_SEPARATOR();
   #
   return $GEN::path_separator;
}

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

sub GET_DIE_PREFIX
{
   # Usage: $prefix = GET_DIE_PREFIX();
   #
   # Return string ($prefix) currently prepended to DIE messages.
   #
   return GEN_SYS::GET_DIE_PREFIX();
}

# ------------------------------------------------------ GET_QUOTED_ITEMS --

sub GET_QUOTED_ITEMS
{
   # Usage: @items = GET_QUOTED_ITEMS( $str, "<[", "]>" );
   #
   # Returns a list of items in $str that are quoted with 
   # $left_delimiter and $right_delimiter
   #
   return GEN_STRING::GET_QUOTED_ITEMS( @_ );
}

# ----------------------------------------------- GET_SUB_DIRECTORY_PATHS --

sub GET_SUB_DIRECTORY_PATHS
{
   # Usage @list_of_sub_dir_paths = GET_SUB_DIRECTORY_PATHS( $dir_path );
   #
   return GEN_PATH::GET_SUB_DIRECTORY_PATHS( shift );
}

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

sub GET_TRACEFILE
{
   # Usage: $trace_filename = GET_TRACEFILE();
   #
   return GEN_SYS::GET_TRACEFILE();
}

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

sub HASH_IS_EMPTY
{
   # Usage: if ( ! HASH_IS_EMPTY( $hr_hash ) );
   #
   # Makes pass through all value elements in %$hr_hash, and returns true if
   # they are all "".
   #
   my $hr_hash = shift;

   foreach my $key ( keys %$hr_hash )
   {
      if ( $$hr_hash{ $key } ne "" )
      {
         return 0;  # false, ie hash is not empty
      }
   }

   # Here when empty.
   #
   return 1;  # true, hash is empty.
}

#---------------------------------------- INDEX_SORT_ARRAY_OF_MIXED_TYPES --

sub INDEX_SORT_ARRAY_OF_MIXED_TYPES
{
   # Usage: @idx = INDEX_SORT_ARRAY_OF_MIXED_TYPES( \@idx_list, \@mixed_type );
   #
   return GEN_SORT::INDEX_SORT_ARRAY_OF_MIXED_TYPES( @_ );
}

#----------------------------------------------- INDEX_SORT_NUMERIC_ARRAY --

sub INDEX_SORT_NUMERIC_ARRAY
{
   # Usage: @idx = INDEX_SORT_NUMERIC_ARRAY( \@idx, \@num );
   #
   return GEN_SORT::INDEX_SORT_NUMERIC_ARRAY( @_ );
}

#------------------------------------------------ INDEX_SORT_STRING_ARRAY --

sub INDEX_SORT_STRING_ARRAY
{
   # Usage: @idx = INDEX_SORT_STRING_ARRAY( \@idx, \@str );
   #
   return GEN_SORT::INDEX_SORT_STRING_ARRAY( @_ );
}

#------------------------------------------------------------------ INPUT --

sub INPUT
{
   # Usage: $continue = INPUT( $prompt );
   #
   return GEN_SYS::INPUT( shift );
}

#---------------------------------------------------------- INPUT_CHOMPED --

sub INPUT_CHOMPED
{
   # Usage: $result = INPUT_CHOMPED( $prompt );
   #
   return GEN_SYS::INPUT_CHOMPED( shift );
}

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

sub IS_FILE_EXECUTABLE
{
   # Usage: $boolean_executable= IS_FILE_EXECUTABLE( $filename );
   #
   return GEN_SYS::IS_FILE_EXECUTABLE( shift );
}

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

sub IS_FILE_PATH_ABSOLUTE
{
   # Usage $is_absolute_bool = IS_FILE_PATH_ABSOLUTE( $path );
   #
   # Returns true if path starts with \ or / or c:\
   #
   return GEN_PATH::IS_FILE_PATH_ABSOLUTE( shift );
}

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

sub IS_QUOTED
{
   # $bool = IS_QUOTED( $item, "\'\"" );
   #
   return GEN_STRING::IS_QUOTED( @_ );
}

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

sub LEFT
{
   # Usage $left_hand_str = LEFT( $str, $nch );
   #
   return GEN_STRING::LEFT( @_ );
}

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

sub LOAD_FILE_AS_ARRAY
{
   # Usage @array_of_lines_of_file = LOAD_FILE_AS_ARRAY( $file_name );
   #
   return GEN_FILE::LOAD_FILE_AS_ARRAY( shift );
}

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

sub LOAD_FILE_AS_STRING
{
   # Usage $string_image_of_file = LOAD_FILE_AS_STRING( $file_name );
   #
   return GEN_FILE::LOAD_FILE_AS_STRING( shift );
}

# ------------------------------------------------------------------ LPAD --

sub LPAD
{
   # Usage: $str = LPAD( $str, $nch );
   # Returns Str with leading space characters of overall length ncg
   #
   return GEN_STRING::LPAD( @_ );
}

# --------------------------------------------------------------- LPAD_CH --

sub LPAD_CH
{
   # Usage: $str = LPAD_CH( $str, $nch, $ch );
   # Returns Str with leading ch characters of overall length nch
   #
   return GEN_STRING::LPAD_CH( @_ );
}

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

sub MAX
{
   # Usage: $greatest = MAX( $n1, $n2 );
   #
   my $n1 = shift;
   my $n2 = shift;

   if ( $n1 > $n2 )
   {
      return $n1;
   }
   else
   {
      return $n2;
   }
}

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

sub MID
{
   # Usage: $sub_str = MID( $str, $from, $nch );
   #
   my $str  = shift;
   my $from = shift;
   my $nch  = shift;

   return GEN_STRING::MID( $str, $from, $nch );
}

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

sub MIN
{
   # Usage: $least = MIN( $n1, $n2 );
   #
   my $n1 = shift;
   my $n2 = shift;

   if ( $n1 < $n2 )
   {
      return $n1;
   }
   else
   {
      return $n2;
   }
}

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

sub MIXED_TYPE_GT
{
   # Usage: if ( MIXED_TYPE_GT( $mixed_type_lhs, $mixed_type_rhs ) ) 
   #
   return GEN_SORT::MIXED_TYPE_GT( @_ );
}

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

sub MK_PATH
{
   # Usage MK_PATH( $path_to_ensure_created );
   #
   GEN_PATH::MK_PATH( shift );
}

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

sub NORMALISE_CYGWIN_PATH
{
   # Usage $path_normalised = NORMALISE_CYGWIN_PATH( $path );
   #
   # If on CYGWIN, then need to ensure sepatators are normalised and
   # that the drive prefix is ok.
   #
   return GEN_PATH::NORMALISE_CYGWIN_PATH( shift );
}

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

sub NORMALISE_PATH_SEPARATORS
{
   # Usage $path_normalised = NORMALISE_PATH_SEPARATORS( $path );
   #
   # Swaps all occurences of \ or / for true path separator.
   #
   return GEN_PATH::NORMALISE_PATH_SEPARATORS( shift );
}

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

sub OPEN_TRACE
{
   # Usage: OPEN_TRACE( $trace_file_name_stem );
   #
   GEN_SYS::OPEN_TRACE( shift );
}

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

sub PATH_DELIMITER
{
   # Usage: $delimiter = PATH_DELIMITER();
   #
   # Returns either ; for dos, or : for unix.
   #
   return $GEN::path_delimiter;
}

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

sub PLATFORM_IS_CYGWIN
{
   # Usage: $bool = PLATFORM_IS_CYGWIN();
   #
   return $GEN::platform_is_cygwin;
}

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

sub PLATFORM_IS_UNIX
{
   # Usage: $bool = PLATFORM_IS_UNIX();
   #
   return $GEN::platform_is_unix;
}

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

sub PLATFORM_IS_WINDOWS
{
   # Usage: $bool = PLATFORM_IS_WINDOWS();
   #
   return $GEN::platform_is_windows;
}

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

sub PRINT_FILE
{
   # Usage: PRINT_FILE( $file_name );
   #
   GEN_FILE::PRINT_FILE( shift );
}

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

sub PRINT_STDERR
{
   # Usage: PRINT_STDERR( $str );
   #
   GEN_SYS::PRINT_STDERR( shift );
}

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

sub PRINT_STDOUT
{
   # Usage: PRINT_STDOUT( $str );
   #
   GEN_SYS::PRINT_STDOUT( shift );
}

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

sub PWD
{
   # Usage: $current_directory_path = GEN::PWD();
   #
   return GEN_PATH::PWD();
}

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

sub QUOTE_ARG
{
   # Usage: $quoted_arg = QUOTE_ARG( $arg );
   #
   # Escapes any embedded quotes.
   # If $arg has embedded spaces, and $arg not already quoted,
   # quotes $arg.
   #
   return GEN_STRING::QUOTE_ARG( shift );
}

# ------------------------------------------ REMOVE_ANY_DUPLICATE_SPACES --

sub REMOVE_ANY_DUPLICATE_SPACES
{
   # Usage: $text = REMOVE_ANY_DUPLICATE_SPACES( $text );
   #
   return GEN_STRING::REMOVE_ANY_DUPLICATE_SPACES( shift );
}

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

sub REMOVE_DUPLICATES
{
   # Usage: @unique_array = REMOVE_DUPLICATES( @array );
   #
   return GEN_SORT::REMOVE_DUPLICATES( @_ );
}
    
# --------------------------------------------------------------------------

sub REMOVE_FINAL_SEP
{
   # Usage: $path = REMOVE_FINAL_SEP( $file_path_str );
   #
   return GEN_PATH::REMOVE_FINAL_SEP( shift );
}

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

sub RENDER_ARG
{
   # Usage: $arg = RENDER_ARG( $arg );
   #
   # Used to avoid passing eoln to errwrt 
   #
   return GEN_STRING::RENDER_ARG( shift );
}

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

sub RIGHT
{
   # Usage $right_hand_str = RIGHT( $str, $nch );
   #
   return GEN_STRING::RIGHT( @_ );
}

# ---------------------------------------------------------------- RIGHT2 --

sub RIGHT2
{
   # Usage $right_hand_str = RIGHT2( $str, $pos );
   #
   return GEN_STRING::RIGHT2( @_ );
}

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

sub RIGHT_FILL
{
   # Usage $string = RIGHT_FILL( $template_string, $string_to_apply );
   # 
   return GEN_STRING::RIGHT_FILL( @_ );
}

#---------------------------------------------------------- RM_DIR --

sub RM_DIR
{
   # Usage: RM_DIR( $dir_path );
   #
   GEN_FILE::RM_DIR( shift );
}

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

sub RPAD
{
   # Usage: $padded_string = RPAD( $str, $nch );
   #
   # Returns Str with trailing spaces to length of nch
   #
   return GEN_STRING::RPAD( @_ );
}

# --------------------------------------------------------------- RPAD_CH --

sub RPAD_CH
{
   # Usage: $str = RPAD_CH( $str, $nch, $ch );
   # Returns Str with trailing ch characters of overall length nch
   #
   return GEN_STRING::RPAD_CH( @_ );
}

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

sub R_TRIM
{
   # Usage: $trimmed_string = r_trim( $string, $character_to_trim );
   # Removes trailing characters that match $character_to_trim
   #
   return GEN_STRING::R_TRIM( @_ );
}

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

sub SEARCH
{
   # Usage: $index = SEARCH( $ar_list, $value );
   #
   # Search array $ar_list for first element that matches $value
   # by string equality comparison and return the index of that element.
   # If not found return -1.
   #
   return GEN_SORT::SEARCH( @_ );
}

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

sub SET_DIE_PREFIX
{
   # Usage: SET_DIE_PREFIX( $prefix );
   #
   # Set string ($prefix) to prepend to DIE messages.
   #
   GEN_SYS::SET_DIE_PREFIX( shift );
}

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

sub SET_DIE_STDERR
{
   # Usage: SET_DIE_STDERR( $stderr );
   #
   # If $stderr == true, => DIE uses stderr.
   # If $stderr == false, => DIE does not write to stderr.
   #
   GEN_SYS::SET_DIE_STDERR( shift );
}

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

sub SET_DIE_STDOUT
{
   # Usage: SET_DIE_STDOUT( $stdout );
   #
   # If $stdout == true, => DIE uses stdout.
   # If $stdout == false, => DIE does not write to stdout.
   #
   GEN_SYS::SET_DIE_STDOUT( shift );
}

#----------------------------------------------- SHELL_SORT_NUMERIC_ARRAY --

sub SHELL_SORT_NUMERIC_ARRAY
{
   # Usage: @sorted = SHELL_SORT_NUMERIC_ARRAY( @array );
   #
   return GEN_SORT::SHELL_SORT_NUMERIC_ARRAY( @_ );
}

#------------------------------------ SHELL_SORT_NUMERIC_ARRAY_DESCENDING --

sub SHELL_SORT_NUMERIC_ARRAY_DESCENDING
{
   # Usage: @sorted = SHELL_SORT_NUMERIC_ARRAY_DESCENDING( @array );
   #
   return GEN_SORT::SHELL_SORT_NUMERIC_ARRAY_DESCENDING( @_ );
}

#--------------------------------- CASE_SENSITIVE_SHELL_SORT_STRING_ARRAY --

sub CASE_SENSITIVE_SHELL_SORT_STRING_ARRAY
{
   # Usage: @sorted = CASE_SENSITIVE_SHELL_SORT_STRING_ARRAY( @array );
   #
   return GEN_SORT::CASE_SENSITIVE_SHELL_SORT_STRING_ARRAY( @_ );
}

#------------------------------- CASE_INSENSITIVE_SHELL_SORT_STRING_ARRAY --

sub CASE_INSENSITIVE_SHELL_SORT_STRING_ARRAY
{
   # Usage: @sorted = CASE_INSENSITIVE_SHELL_SORT_STRING_ARRAY( @array );
   #
   return GEN_SORT::CASE_INSENSITIVE_SHELL_SORT_STRING_ARRAY( @_ );
}

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

sub SHORT_PATH_NAME
{
   # Usage: $windows_short_path = SHORT_PATH_NAME( $path );
   #
   return GEN_SYS::SHORT_PATH_NAME( shift );
}

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

sub SORT_ARRAY_OF_MIXED_TYPES
{
   # Usage: @sorted_mixed_types = SORT_ARRAY_OF_MIXED_TYPES( @mixed_types );
   #
   return GEN_SORT::SORT_ARRAY_OF_MIXED_TYPES( @_ );
}

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

sub SPLIT_COMMAND_LINE_STRING
{
   # Subroutine to split up a string of terms separated by space or tab 
   # characters, where some of the terms may be quoted strings that include
   # spaces, according to OS command line semantics.
   # Note that escaped spaces may be embedded.  To do this the space 
   # is prefixed with \ (backslash).
   #
   # Usage:
   #  @list_of_terms = SPLIT_COMMAND_LINE_STRING( $str );
   #
   # Handling of escape char \ (backslash) is OS specific.
   # For the following 4 cases Unix and Windows produce 2 different sets of 
   # results.   (NB For the purposes of this sub Cygwin is treated as the same
   # as Unix as Cygwin uses the Unix version of Perl.  Our approach to Cygwin
   # is to use the wrapper.exe file which uses Windows Perl conventions.):
   #
   # 1. "abc\"def\\ghi\ jk\'l\x\y\z"
   # 2. 'abc\"def\\ghi\ jk\'l\x\y\z'
   # 3. 'abc\"def\\ghi\ jkl\x\y\z'  
   # 4. abc\"def\\ghi\ jk\'l\x\y\z  
   #
   # On Unix:
   # 1. "abc\"def\\ghi\ jk\'l\x\y\z" becomes -> abc"def\ghi\ jk\'l\x\y\z
   # 2. 'abc\"def\\ghi\ jk\'l\x\y\z' is illegal (unterminated)
   # 3. 'abc\"def\\ghi\ jkl\x\y\z'   becomes -> abc\"def\\ghi\ jkl\x\y\z
   # 4. abc\"def\\ghi\ jk\'l\x\y\z   becomes -> abc"def\ghi jk'lxyz
   #
   # On Windows:
   # 1. "abc\"def\\ghi\ jk\'l\x\y\z" becomes -> abc"def\\ghi\ jk\'l\x\y\z
   # 2. 'abc\"def\\ghi\ jk\'l\x\y\z' becomes -> 'abc"def\\ghi\
   #                                            jk\'l\x\y\z
   # 3. 'abc\"def\\ghi\ jkl\x\y\z'   becomes -> 'abc"def\\ghi\
   #                                            jkl\x\y\z
   # 4. abc\"def\\ghi\ jk\'l\x\y\z   becomes -> abc"def\\ghi\
   #                                            jk\'l\x\y\z
   #
   # Therefore rules are:
   # On Unix act on \\ and \" inside "
   # On Unix act on nothing inside '
   # On Unix act on \ all outside " and '
   #
   # On Windows act on \" inside "
   # On Windows act on \" inside '
   # On Windows act on \" outside " and '
   #
   my $str = shift;

   return GEN_STRING::SPLIT_COMMAND_LINE_STRING( $str );
}

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

sub SPLIT_COMMAND_LINE_STRING_WINDOWS
{
   # Same as GEN::SPLIT_COMMAND_LINE_STRING except always uses Windows
   # semantics
   #
   my $str = shift;

   my $keep = $GEN_STRING::platform_is_windows;

   $GEN_STRING::platform_is_windows = 1;

   my @terms = GEN_STRING::SPLIT_COMMAND_LINE_STRING( $str );

   $GEN_STRING::platform_is_windows = $keep;

   return @terms;
}

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

sub SPLIT_ON_QUOTED_ITEMS
{
   # Usage: ( $ar_items, $ar_kind ) = SPLIT_ON_QUOTED_ITEMS( $str, "<[", "]>" );
   #
   # Returns a list of items in $str that are quoted with 
   # $left_delimiter and $right_delimiter, plus the intermediate
   # non-quoted items.
   #
   # Also returns a 'parallel' array of 'kind' information which is one of:
   #  ( RAW_DATA 
   #  	LEFT_DELIMITER
   #  	QUOTED_DATA
   #  	RIGHT_DELIMITER
   #  	INCOMPLETE_QUOTED_DATA
   #  );
   #
   # e.g. Given:
   # $str             = "abcd efgh <![CDATA[klmno pqr ]]> s t <![CDATA[ uvw]]> x y z"
   # $left_delimiter  = "<![CDATA["
   # $right_delimiter = "]]>"
   #
   # Returns $ar_items,            $ar_kind:
   # $items[ 0 ] = "abcd efgh "    "RAW_DATA"
   # $items[ 1 ] = "<![CDATA["     "LEFT_DELIMITER"
   # $items[ 2 ] = "klmno pqr "    "QUOTED"
   # $items[ 3 ] = "]]>"           "RIGHT_DELIMITER"
   # $items[ 4 ] = " s t "         "RAW_DATA"
   # $items[ 5 ] = "<![CDATA["     "LEFT_DELIMITER"
   # $items[ 6 ] = " uvw"          "QUOTED"
   # $items[ 7 ] = "]]>"           "RIGHT_DELIMITER"
   # $items[ 8 ] = " x y z"        "RAW_DATA"
   #
   # Sets last $ar_kind element to "INCOMPLETE_QUOTED_DATA" if
   # $right_delimiter is not found after a $left_delimiter.
   #
   return GEN_STRING::SPLIT_ON_QUOTED_ITEMS( @_ );
}

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

sub SPLIT_MIXED_TYPE
{
   # Usage: @lhs = SPLIT_MIXED_TYPE( $mixed_type );
   #
   # Separates $mixed_type into array of numeric and non-numeric tokens.
   #
   return GEN_SORT::SPLIT_MIXED_TYPE( shift );
}

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

sub SPLIT_PATH
{
   # Usage @path_elements = SPLIT_PATH( $path );
   #
   # Separates all elements (directory names and filename) into a list.
   #
   return GEN_PATH::SPLIT_PATH( shift );
}

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

sub SPLIT_STRING_ALLOWING_QUOTED_TERMS
{
   # Subroutine to split up a string of terms separated by characters in 
   # the supplied $separator list.  Where some of the terms may be quoted
   # strings that include separators.
   # Note that escaped separators may be embedded.  To do this the 
   # separator is prefixed with \ (backslash).
   #
   # Usage:
   #  @list_of_terms = SPLIT_STRING_ALLOWING_QUOTED_TERMS( $separators, $str );
   #
   return GEN_STRING::SPLIT_STRING_ALLOWING_QUOTED_TERMS( @_ );
}

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

sub SPLIT_STRING_ALLOWING_SPECIFIC_QUOTED_TERMS
{
   # Subroutine to split up a string of terms separated by characters in 
   # the supplied $separator list.  Where some of the terms may be quoted
   # strings that include separators.
   # Note that escaped separators may be embedded.  To do this the 
   # separator is prefixed with \ (backslash).
   #
   # Usage:
   #  @list_of_terms = SPLIT_STRING_ALLOWING_SPECIFIC_QUOTED_TERMS
   #                     ( $separators, $str, $quote_char );
   #
   return GEN_STRING::SPLIT_STRING_ALLOWING_SPECIFIC_QUOTED_TERMS( @_ );
}

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

sub STEM_NAME
{
   # Usage $stem = STEM_NAME( $path );
   #
   return GEN_PATH::STEM_NAME( shift );
}

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

sub STRING_FORM
{
   # Usage: $str = STRING_FORM( @arg_list );
   #
   # Takes supplied array and returns as a string with single space separator
   # between elements.  If an element of the array contains embedded space
   # characters, that element is surrounded by quotes.
   #
   return GEN_STRING::STRING_FORM( @_ );
}

# ---------------------------------------------------------- STRIP_NUMBER --

sub STRIP_NUMBER
{
   # Usage: $extracted_number = STRIP_NUMBER( $str );
   #
   # Returns the first string of numeric (integer) digits in str, ignoring
   # any preceding characters.  Last digit read is last consecutive digit
   # character that is numeric.  Returns the integer value of the sub-string
   # that is found.
   #
   return GEN_STRING::STRIP_NUMBER( shift );
}

# --------------------------------------------------------- TEMP_DIR_PATH --

sub TEMP_DIR_PATH
{
   # Usage: $path = TEMP_DIR_PATH();
   #
   return GEN_PATH::TEMP_DIR_PATH();
}

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

sub TIME
{
   # Usage: $formatted_time = TIME();
   #
   # Returns the current time formatted as HH:MM:SS
   #
   return GEN_TIME::TIME();
}

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

sub TRACE
{
   # Usage: e.g. TRACE( "Assumed platform $Config{'osname'}\n" );
   #
   GEN_SYS::TRACE( shift );
}

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

sub TRIM
{
   # Usage: $trimmed_string = TRIM( string );
   # Removes leading and trailing whitespace from string
   #
   return GEN_STRING::TRIM( shift );
}

# ---------------------------------------------------- TRIM_TRAILING_CHAR --

sub TRIM_TRAILING_CHAR
{
   # Usage $trimmed_string = TRIM_TRAILING_CHAR( $string, $char_to_trim );
   #
   return GEN_STRING::TRIM_TRAILING_CHAR( @_ );
}

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

sub TRUNCATE_FILE
{
   # usage: TRUNCATE_FILE( $file_path );
   #
   return GEN_FILE::TRUNCATE_FILE( shift );
}

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

sub UNIX_2_DOS
{
   # Usage: UNIX_2_DOS( $full_file_name );
   #
   GEN_FILE::UNIX_2_DOS( shift );
}

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

sub UNIX_FILE_INODE_VALUE
{
   # $inode = UNIX_FILE_INODE_VALUE( $filename );
   #
   return GEN_SYS::UNIX_FILE_INODE_VALUE( shift );
}

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

sub UNQUOTE
{
   # Usage: $unquoted_string = UNQUOTE( $arg );
   #
   return GEN_STRING::UNQUOTE( shift );
}

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

sub WHICH
{
   # Usage: $full_filename = WHICH( $program_name );
   #
   # Returns absolute path to $program_name or "" if not found.
   #
   # NB WHICH() has the limitation that for Windows, if the
   # supplied file is not an executable file, e.g. fred.txt,
   # then 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 WHICH() to ensure that a truely executable
   # filename is supplied.
   #
   # For Unix simply uses the 'which' utility.
   # For Windows:
   #    Gets all paths on PATH and for each path:
   #       Looks for $program_name . ( '.exe' | '.com' | '' ).
   #
   # NB For Windows, this sub will return the first file path in PATH
   # that matches $program_name . ( '.exe' | '.com' | '' )
   # This may be a non-executable file.
   #
   return GEN_PATH::WHICH( shift );
}

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

sub WRITE_FILE
{
   # Usage: WRITE_FILE( $file_name, @text );
   #
   GEN_FILE::WRITE_FILE( @_ );
}

#------------------------------------------------------------------- YEAR --

sub YEAR
{
   # Usage: $year = YEAR();
   #
   # Returns the current year as dddd.
   #
   return GEN_TIME::YEAR();
}

# -- eof GEN.pm -------------------------------------------------------------
