#!/usr/bin/perl -w
######################################################################
#
# $Id: webjob-cfg-get-kvps,v 1.13 2012/01/07 08:01:15 mavrik Exp $
#
######################################################################
#
# Copyright 2009-2012 The WebJob Project, All Rights Reserved.
#
######################################################################
#
# Purpose: Get config file key/value pairs.
#
######################################################################

use strict;
use File::Basename;
use FindBin qw($Bin $RealBin); use lib ("$Bin/../lib/perl5/site_perl", "$RealBin/../lib/perl5/site_perl", "/usr/local/webjob/lib/perl5/site_perl", "/opt/local/webjob/lib/perl5/site_perl");
use Getopt::Std;
use WebJob::KvpRoutines 1.029;
use WebJob::Properties;

BEGIN
{
  ####################################################################
  #
  # The Properties hash is essentially private. Those parts of the
  # program that wish to access or modify the data in this hash need
  # to call GetProperties() to obtain a reference.
  #
  ####################################################################

  my (%hProperties);

  $hProperties{'CommonTemplates'} = PropertiesGetGlobalTemplates();

  sub GetProperties
  {
    return \%hProperties;
  }
}

######################################################################
#
# Main Routine
#
######################################################################

  ####################################################################
  #
  # Punch in and go to work.
  #
  ####################################################################

  my ($phProperties);

  $phProperties = GetProperties();

  $$phProperties{'Program'} = basename(__FILE__);

  ####################################################################
  #
  # Get Options.
  #
  ####################################################################

  my (%hOptions);

  if (!getopts('d:f:l:o:r:t:', \%hOptions))
  {
    Usage($$phProperties{'Program'});
  }

  ####################################################################
  #
  # A Delimiter, '-d', optional.
  #
  ####################################################################

  $$phProperties{'Delimiter'} = (exists($hOptions{'d'})) ? $hOptions{'d'} : "=";

  if ($$phProperties{'Delimiter'} !~ /^[:=|]$/)
  {
    print STDERR "$$phProperties{'Program'}: Error='Invalid delimiter ($$phProperties{'Delimiter'}).'\n";
    exit(2);
  }

  ####################################################################
  #
  # A config file, '-f', is required.
  #
  ####################################################################

  $$phProperties{'ConfigFile'} = (exists($hOptions{'f'})) ? $hOptions{'f'} : undef;

  if (!defined($$phProperties{'ConfigFile'}))
  {
    Usage($$phProperties{'Program'});
  }

  ####################################################################
  #
  # A recursion limit, '-l', optional.
  #
  ####################################################################

  $$phProperties{'RecursionLimit'} = (exists($hOptions{'l'})) ? $hOptions{'l'} : 3;

  if
  (
       $$phProperties{'RecursionLimit'} !~ /^\d+$/
    || $$phProperties{'RecursionLimit'} < 1
    || $$phProperties{'RecursionLimit'} > 50
  )
  {
    print STDERR "$$phProperties{'Program'}: Error='Recursion limit ($$phProperties{'RecursionLimit'}) must be a number in the range [1-50].'\n";
    exit(2);
  }

  ####################################################################
  #
  # The option list, '-o', is optional.
  #
  ####################################################################

  $$phProperties{'BeQuiet'} = 0;
  $$phProperties{'KeysOnly'} = 0;
  $$phProperties{'UnquoteValues'} = 0;
  $$phProperties{'ValuesOnly'} = 0;

  $$phProperties{'Options'} = (exists($hOptions{'o'})) ? $hOptions{'o'} : undef;

  if (exists($hOptions{'o'}) && defined($hOptions{'o'}))
  {
    foreach my $sActualOption (split(/,/, $$phProperties{'Options'}))
    {
      foreach my $sTargetOption ('BeQuiet', 'KeysOnly', 'UnquoteValues', 'ValuesOnly')
      {
        if ($sActualOption =~ /^$sTargetOption$/i)
        {
          $$phProperties{$sTargetOption} = 1;
        }
      }
    }
  }

  if ($$phProperties{'KeysOnly'} && $$phProperties{'ValuesOnly'})
  {
    print STDERR "$$phProperties{'Program'}: Error='Conflicting options. Use either 'KeysOnly' or 'ValuesOnly', but not both.'\n";
    exit(2);
  }

  ####################################################################
  #
  # A recursion key, '-r', is optional.
  #
  ####################################################################

  $$phProperties{'RecursionKey'} = (exists($hOptions{'r'})) ? $hOptions{'r'} : undef;

  ####################################################################
  #
  # A type, '-t', is optional.
  #
  ####################################################################

  $$phProperties{'Type'} = (exists($hOptions{'t'})) ? $hOptions{'t'} : "generic";

  if (!exists($$phProperties{'CommonTemplates'}{$$phProperties{'Type'}}))
  {
    print STDERR "$$phProperties{'Program'}: Error='Invalid type ($$phProperties{'Type'}).'\n";
    exit(2);
  }
  $$phProperties{'Template'} = $$phProperties{'CommonTemplates'}{$$phProperties{'Type'}};

  ####################################################################
  #
  # If no keys were specified, get all key/value pairs.
  #
  ####################################################################

  my (@aOfficialKeys, $sGetAllKvps);

  if (scalar(@ARGV) == 0)
  {
    $sGetAllKvps = 1;
  }
  else
  {
    $sGetAllKvps = 0;
    foreach my $sKey (@ARGV)
    {
      my $sOfficialKey = PropertiesGetOfficialKeyName($$phProperties{'Type'}, $sKey);
      if (!defined($sOfficialKey))
      {
        print STDERR "$$phProperties{'Program'}: Warning='Unofficial key ($sKey).'\n" unless ($$phProperties{'BeQuiet'});
      }
      else
      {
        push(@aOfficialKeys, $sOfficialKey);
      }
    }
  }

  ####################################################################
  #
  # Create KVP map.
  #
  ####################################################################

  my (%hKvpMap) = ();

  if (-f $$phProperties{'ConfigFile'} || $$phProperties{'ConfigFile'} eq "-")
  {
    my %hLArgs =
    (
      'Delimiter'      => $$phProperties{'Delimiter'},
      'File'           => $$phProperties{'ConfigFile'},
      'MatchKeyCase'   => 0,
      'Properties'     => \%hKvpMap,
      'Template'       => $$phProperties{'Template'},
      'UnquoteValues'  => $$phProperties{'UnquoteValues'},
      'VerifyValues'   => 1,
    );
    $hLArgs{'RecursionKey'} = $$phProperties{'RecursionKey'} if (defined($$phProperties{'RecursionKey'}));
    $hLArgs{'RecursionLimit'} = $$phProperties{'RecursionLimit'} if (defined($$phProperties{'RecursionLimit'}));
    if (!KvpGetKvps(\%hLArgs))
    {
      print STDERR "$$phProperties{'Program'}: Error='$hLArgs{'Error'}'\n";
      exit(2);
    }
  }
  else
  {
    print STDERR "$$phProperties{'Program'}: Error='File ($$phProperties{'ConfigFile'}) does not exist or is not regular.'\n";
    exit(2);
  }

  ####################################################################
  #
  # Develop the output.
  #
  ####################################################################

  if ($sGetAllKvps)
  {
    foreach my $sKey (sort(keys(%hKvpMap)))
    {
      my $sValue = (defined($hKvpMap{$sKey})) ? $hKvpMap{$sKey} : "";
      if ($$phProperties{'KeysOnly'})
      {
        print STDOUT $sKey, "\n";
      }
      elsif ($$phProperties{'ValuesOnly'})
      {
        print STDOUT $sValue, "\n";
      }
      else
      {
        print STDOUT join($$phProperties{'Delimiter'}, $sKey, $sValue), "\n";
      }
    }
  }
  else
  {
    foreach my $sKey (@aOfficialKeys)
    {
      if (exists($hKvpMap{$sKey}))
      {
        my $sValue = (defined($hKvpMap{$sKey})) ? $hKvpMap{$sKey} : "";
        if ($$phProperties{'KeysOnly'})
        {
          print STDOUT $sKey, "\n";
        }
        elsif ($$phProperties{'ValuesOnly'})
        {
          print STDOUT $sValue, "\n";
        }
        else
        {
          print STDOUT join($$phProperties{'Delimiter'}, $sKey, $sValue), "\n";
        }
      }
      else
      {
        print STDERR "$$phProperties{'Program'}: Warning='Unknown key ($sKey).'\n" unless ($$phProperties{'BeQuiet'});
      }
    }
  }

  ####################################################################
  #
  # Shutdown and go home.
  #
  ####################################################################

  1;


######################################################################
#
# Usage
#
######################################################################

sub Usage
{
  my ($sProgram) = @_;
  print STDERR "\n";
  print STDERR "Usage: $sProgram [-d delimiter] [-l recursion-limit] [-o option[,option[,...]]] [-r recursion-key] [-t type] -f file [key [key ...]]\n";
  print STDERR "\n";
  exit(1);
}


=pod

=head1 NAME

webjob-cfg-get-kvps - Get config file key/value pairs.

=head1 SYNOPSIS

B<webjob-cfg-get-kvps> B<[-d delimiter]> B<[-l recursion-limit]> B<[-o option[,option[,...]]]> B<[-r recursion-key]> B<[-t type]> B<-f file> B<[key [key ...]]>

=head1 DESCRIPTION

This utility gets one or more key/value pairs from the specified
config file.

=head1 OPTIONS

=over 4

=item B<-d delimiter>

Specifies the delimiter.  Valid delimiters include the following
characters: colon ':', equals sign '=', and pipe '|'.  The default
delimiter is an equals sign.

=item B<-f file>

Specifies the name of the config file to query.

=item B<-l recursion-limit>

Specifies the number of recursions allowed (i.e., how deep you are
willing to go) when processing files identified by a recursion key.
This value must be in the range [1-50].  The default value is 3.

=item B<-o option,[option[,...]]>

Specifies the list of options to apply.  Currently, the following
options are supported:

=over 4

=item BeQuiet

Don't print warning messages.

=item KeysOnly

Only print keys rather than key/value pairs.

=item UnquoteValues

Unwrap values that have been quoted.  Note that improperly quoted
values will not be unwrapped or unescaped.  A properly quoted value is
a value that has matching left and right quotes.  Both single and
double quotes are supported.

=item ValuesOnly

Only print values rather than key/value pairs.

=back

=item B<-r recursion-key>

Specifies the name of a key whose value is to be treated as a file
containing additional properties.  Set this value to 'Import' when
processing the B<webjob.execute> and B<webjob.get-url> file types.

=item B<-t type>

Specifies the type of config file being operated on.  Each supported
type has a custom template that is used to validate keys and values.
Currently, the following types are supported:

=over 4

=item B<generic>

=item B<nph-config.custom>

=item B<nph-config.global>

=item B<nph-webjob.custom>

=item B<nph-webjob.global>

=item B<nph-webjob.env> (or B<webjob.env>)

=item B<webjob.execute>

=item B<webjob.get-url>

=item B<webjob.server>

=back

The default type is generic.

=back

=head1 AUTHOR

Klayton Monroe

=head1 SEE ALSO

webjob-cfg-set-kvps(1)

=head1 LICENSE

All documentation and code are distributed under same terms and
conditions as B<WebJob>.

=cut
