Advanced-Config

 view release on metacpan or  search on metacpan

lib/Advanced/Config/Reader.pm  view on Meta::CPAN

use vars qw( @ISA @EXPORT @EXPORT_OK $VERSION );
use Exporter;

use Advanced::Config::Options;
use Advanced::Config;

use Fred::Fish::DBUG 2.09 qw / on_if_set  ADVANCED_CONFIG_FISH /;

use File::Basename;

$VERSION = "1.14";
@ISA = qw( Exporter );

@EXPORT = qw( read_config  source_file  make_new_section  parse_line
              expand_variables  apply_modifier  parse_for_variables
              format_section_line  format_tag_value_line format_encrypt_cmt
              encrypt_config_file_details  decrypt_config_file_details );

@EXPORT_OK = qw( );

my $skip_warns_due_to_make_test;
my %global_sections;
my $gUserName;

# ==============================================================
# NOTE: It is extreemly dangerous to reference Advanced::Config
#       internals in this code.  Avoid where possible!!!
#       Ask for copies from the module instead.
# ==============================================================
# Any other module initialization done here ...
# This block references initializations done in my other modules.
BEGIN
{
   DBUG_ENTER_FUNC ();

   # What we call our default section ...
   $global_sections{DEFAULT}  = Advanced::Config::Options::DEFAULT_SECTION_NAME;
   $global_sections{OVERRIDE} = $global_sections{DEFAULT};

   $gUserName = Advanced::Config::Options::_get_user_id ();

   # Is the code being run via "make test" environment ...
   if ( $ENV{PERL_DL_NONLAZY} ||
        $ENV{PERL_USE_UNSAFE_INC} ||
        $ENV{HARNESS_ACTIVE} ) {
      $skip_warns_due_to_make_test = 1;
   }

   DBUG_VOID_RETURN ();
}


# ==============================================================
# No fish please ... (called way too often)
# This method is called in 2 ways:
#  1) By parse_line() to determine if ${ln} is a tag/value pair.
#  2) By everyone else to parse a known tag/value pair in ${ln}.
#
# ${ln} is in one of these 3 formats if it's a tag/value pair.
#     tag = value
#     export tag = value    # Unix shell scripts
#     set tag = value       # Windows Batch files

sub _split_assign
{
   my $rOpts = shift;    # The read options ...
   my $ln    = shift;    # The value to split ...
   my $skip  = shift;    # Skip massaging the tag? 

   my ( $tag, $value );
   if ( is_assign_spaces ( $rOpts ) ) {
      ( $tag, $value ) = split ( " ", $ln, 2 );
      $skip = 1;   # This separator doesn't support the prefixes.
   } else {
      my $assign_str  = convert_to_regexp_string ($rOpts->{assign}, 1);
      ( $tag, $value ) = split ( /\s*${assign_str}\s*/, $ln, 2 );
   }

   my $export_prefix = "";

   unless ( $skip ) {
      # Check if one of the export/set variable prefixes were used!
      if ( $tag =~ m/^(export\s+)(\S.*)$/i ) {
         $tag = $2;           # Remove the leading "export" keyword ...
         $export_prefix = $1;
      } elsif ( $tag =~ m/^(set\s+)(\S.*)$/i ) {
         $tag = $2;           # Remove the leading "set" keyword ...
         $export_prefix = $1;
      }
   }

   # Did we request case insensitive tags ... ?
   my $ci_tag = ( $rOpts->{tag_case} && defined $tag ) ? lc ($tag) : $tag;

   return ( $ci_tag, $value, $export_prefix, $tag );
}


# ==============================================================

=item $sts = read_config ( $file, $config )

This method performs the reading and parsing of the given config file and puts
the results into the L<Advanced::Config> object I<$config>.  This object
provides the necessary parsing rules to use.

If a line was too badly mangled to be parsed, it will be ignored and a warning
will be written to your screen.

It returns B<1> on success and B<0> on failure.

Please note that comments are just thrown away by this process and only
tag/value pairs remain afterwards.  Everything else is just instructions to
the parser or how to group together these tag/value pairs.

If it sees something like:  export tag = value, it will export tag's value
to the %ENV hash for you just like it does in a Unix shell script!

Additional modifiers can be found in the comments after a tag/value pair
as well.

=cut

# ==============================================================
sub read_config
{
   DBUG_ENTER_FUNC ( @_ );
   my $file = shift;     # The filename to read ...
   my $cfg  = shift;     # The Advanced::Config object ...

   my $opts = $cfg->get_cfg_settings ();   # The Read Options ...

   # Locate the parent section of the config file.
   my $pcfg = $cfg->get_section ();

   # Using a variable so that we can be recursive in reading config files.
   my $READ_CONFIG;

   DBUG_PRINT ("INFO", "Opening the config file named: %s", $file);

   unless ( open ($READ_CONFIG, "<", $file) ) {
      return DBUG_RETURN ( croak_helper ($opts,
                                        "Unable to open the config file.", 0) );
   }

   # Misuse of this option makes the config file unreadable ...
   if ( $opts->{use_utf8} ) {
      binmode ($READ_CONFIG, "encoding(UTF-8)");
      $pcfg->_allow_utf8 ();   # Tells get_date() that wide char languages are OK!
   }

   # Some common RegExp strings ... Done here to avoid asking repeatably ...
   my $decrypt_str = convert_to_regexp_string ($opts->{decrypt_lbl});
   my $encrypt_str = convert_to_regexp_string ($opts->{encrypt_lbl});
   my $hide_str    = convert_to_regexp_string ($opts->{hide_lbl});
   my $sect_str    = convert_to_regexp_string ($opts->{source_file_section_lbl});

   my $export_str  = convert_to_regexp_string ($opts->{export_lbl});
   my ($lb, $rb) = ( convert_to_regexp_string ($opts->{section_left}),
                     convert_to_regexp_string ($opts->{section_right}) );
   my $assign_str  = convert_to_regexp_string ($opts->{assign});
   my $src_str     = convert_to_regexp_string ($opts->{source});
   my ($lv, $rv) = ( convert_to_regexp_string ($opts->{variable_left}),
                     convert_to_regexp_string ($opts->{variable_right}) );

   # The label separators used when searching for option labels in a comment ...
   my $lbl_sep = '[\s.,$!()-]';

   # Initialize to the default secion ...
   my $section = make_new_section ( $cfg, "" );

   my %hide_section;

   while ( <$READ_CONFIG> ) {
      chomp;
      my $line = $_;             # Save so can use in fish logging later on.

lib/Advanced/Config/Reader.pm  view on Meta::CPAN

   }

   # ---------------------------------------------------------------------------
   # Corrupted variable definition with variables in the comments ...
   # Boy things are getting difficult to parse.  Reverse the previous variable
   # substitutions until the all variables in the comments are unexpanded again!
   # Does a greedy RegExp to grab the 1st comment string encountered.
   # ---------------------------------------------------------------------------
   if ( $unbalanced_leading_var_anchor_with_comments ) {
      $cmts = "";
      foreach my $l (reverse @data) {
         if ( $l =~ m/\s*${comment}\s*(.*)$/ ) {
            $cmts = $1;
            last  unless ( $cmts =~ m/${has_no_cmt}/ );
            $cmts = "";
         }
      }

      if ( $cmts ne "" ) {
         my $cmt2 = convert_to_regexp_string ($cmts);
         $line =~ s/\s*${comment}\s*${cmt2}$//;
         DBUG_PRINT ("LINE", "Unbalanced var def encountered with var comments ...");
         return DBUG_RETURN ( $tv_pair_flag, $line, $cmts, "", "");
      }

      # If you get here, assume it's not a tag/value pair even if it is!
      # I know I can no longer hope to parse it correctly without a test case.
      # But I really don't think it's possible to get here anymore ...
      warn ("Corrupted variable definition encountered.  Can't split out the comment with variables in it correctly!\n");
      return DBUG_RETURN ( 0, $line, "", "", "");
   }

   # ----------------------------------------------------------------------
   # No variables, no balanced quotes ...
   # But I still think there's a comment to remove!
   # ----------------------------------------------------------------------

   if ( $tv_pair_flag && $value =~ m/(\s*${comment}\s*)(.*)$/ ) {
      $cmts = $2;
      my $cmt2 = convert_to_regexp_string ($1 . $cmts);
      $line =~ s/${cmt2}$//;             # Remove the comment from the line.
      DBUG_PRINT ("LINE", "Last ditch effort to remove the comment from the value ...");
      return DBUG_RETURN ( $tv_pair_flag, $line, $cmts, "", "");
   }

   $cmts = $line;
   $line =~ s/\s*${comment}.*$//;              # Strip off any comments ....
   $cmts = substr ( $cmts, length ($line) );   # Grab the comments ...
   $cmts =~ s/^\s*${comment}\s*//;             # Remove comment symbol ...

   DBUG_PRINT ("LINE", "Last ditch effort to remove the comment from the line ...");
   DBUG_RETURN ( $tv_pair_flag, $line, $cmts, "", "");
}


# ==============================================================

=item ($v[, $h]) = expand_variables ( $config, $string[, $file[, $sensitive[, trim]]] )

This function takes the provided I<$string> and expands any embedded variables
in this string similar to how it's handled by a Unix shell script.

The optional I<$file> tells which file the string was read in from.

The optional I<$sensitive> when set to a non-zero value is used to disable
B<fish> logging when it's turned on because the I<$string> being passed contains
sensitive information.

The optional I<$trim> tells if you may trim the results before it's returned.

It returns the new value $v, once all the variable substitution(s) have
occurred.  And optionally a second return value $h that tells if B<fish> was
paused during the expansion of that value due to something being sensitive.
This 2nd return value $h is meaningless in most situations, so don't ask for it.

All variables are defined as B<${>I<...>B<}>, where I<...> is the variable you
wish to substitute.  If something isn't surrounded by a B<${> + B<}> pair, it's
not a variable.

   A config file exampe:
       tmp1 = /tmp/work-1
       tmp2 = /tmp/work-2
       opt  = 1
       date = 2011-02-03
       logs = ${tmp${opt}}/log-${date}.txt
       date = 2012-12-13

   So when passed "${tmp${opt}}/log-${date}.txt", it would return:
       /tmp/work-1/log-2011-02-03.txt
   And assigned it to B<logs>.

As you can see multiple variable substitutions may be expanded in a single
string as well as nested substitutions.  And when the variable substitution is
done while reading in the config file, all the values used were defined before
the tag was referenced.

Should you call this method after the config file was loaded you get slightly
different results.  In that case the final tag value is used instead and the
2nd date in the above example would have been used in it's place.

See L<Advanced::Config::lookup_one_variable> for more details on how it
evaluates individual variables.

As a final note, if one or more of the referenced variables holds encrypted
values that haven't yet been decrypted, those variables are not resolved.  But
all variables that don't contain encrypted data are resolved.

=cut

# ==============================================================
sub expand_variables
{
   my $config    = shift;           # For the current section of config obj ...
   my $value     = shift;           # The value to parse for variables ...
   my $file      = shift || "";     # The config file the value came from ...
   my $mask_flag = shift || 0;      # Hide/mask sensitive info written to fish?
   my $trim_flag = shift || 0;      # Tells if we should trim the result or not.

   # Only mask ${value} if ${mask_flag} is true ...
   DBUG_MASK_NEXT_FUNC_CALL (1)  if ( $mask_flag );
   DBUG_ENTER_FUNC ( $config, $value, $file, $mask_flag, $trim_flag, @_);



( run in 0.625 second using v1.01-cache-2.11-cpan-df04353d9ac )