#!/usr/bin/perl -w
######################################################################
#
# $Id: webjob-cfg-set-kvps,v 1.14 2012/01/07 08:01:16 mavrik Exp $
#
######################################################################
#
# Copyright 2009-2012 The WebJob Project, All Rights Reserved.
#
######################################################################
#
# Purpose: Set 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:o:q: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'});
  }

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

  $$phProperties{'BeQuiet'} = 0;
  $$phProperties{'QuoteValues'} = 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', 'QuoteValues')
      {
        if ($sActualOption =~ /^$sTargetOption$/i)
        {
          $$phProperties{$sTargetOption} = 1;
        }
      }
    }
  }

  ####################################################################
  #
  # A quote symbol, '-q', optional.
  #
  ####################################################################

  $$phProperties{'QuoteSymbol'} = (exists($hOptions{'q'})) ? $hOptions{'q'} : undef;

  if (defined($$phProperties{'QuoteSymbol'}) && $$phProperties{'QuoteSymbol'} !~ /^["']$/)
  {
    print STDERR "$$phProperties{'Program'}: Error='Invalid quote symbol.'\n";
    exit(2);
  }

  ####################################################################
  #
  # 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 there isn't at least one argument left, it's an error.
  #
  ####################################################################

  if (scalar(@ARGV) < 1)
  {
    Usage($$phProperties{'Program'});
  }

  ####################################################################
  #
  # Conditionally create a lock file. This is an exclusive lock.
  #
  ####################################################################

  my (%hLockArgs);

  if ($$phProperties{'ConfigFile'} ne "-")
  {
    %hLockArgs =
    (
      'LockFile'   => $$phProperties{'ConfigFile'} . ".lock",
      'LockRemove' => 1,
    );
    if (!KvpLockFile(\%hLockArgs))
    {
      print STDERR "$$phProperties{'Program'}: Error='$hLockArgs{'Error'}'\n";
      exit(2);
    }
  }

  ####################################################################
  #
  # 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'},
      'VerifyValues'   => 1,
    );
    if (!KvpGetKvps(\%hLArgs))
    {
      print STDERR "$$phProperties{'Program'}: Error='$hLArgs{'Error'}'\n";
      exit(2);
    }
  }

  ####################################################################
  #
  # Set the specified key/value pairs.
  #
  ####################################################################

  my $sDelimiter = $$phProperties{'Delimiter'};

  foreach my $sKeyValuePair (@ARGV)
  {
    my ($sKey, $sValue) = ($sKeyValuePair =~ /^([^$sDelimiter]+)$sDelimiter(.*)$/);
    if (!defined($sKey) || !defined($sValue))
    {
      print STDERR "$$phProperties{'Program'}: Error='Unable to parse the specified key/value pair ($sKeyValuePair). Check the delimiter.'\n";
      exit(2);
    }
    $hKvpMap{$sKey} = $sValue;
  }

  ####################################################################
  #
  # Write the new config file.
  #
  ####################################################################

  my %hLArgs =
  (
    'Delimiter'      => $$phProperties{'Delimiter'},
    'File'           => $$phProperties{'ConfigFile'},
    'Properties'     => \%hKvpMap,
    'Template'       => $$phProperties{'Template'},
    'QuoteValues'    => $$phProperties{'QuoteValues'},
    'VerifyValues'   => 0,
  );
  $hLArgs{'QuoteSymbol'} = $$phProperties{'QuoteSymbol'} if (defined($$phProperties{'QuoteSymbol'}));
  if (!KvpSetKvps(\%hLArgs))
  {
    print STDERR "$$phProperties{'Program'}: Error='$hLArgs{'Error'}'\n";
    exit(2);
  }

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

  if ($$phProperties{'ConfigFile'} ne "-")
  {
    KvpUnlockFile(\%hLockArgs);
  }

  1;


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

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


=pod

=head1 NAME

webjob-cfg-set-kvps - Set config file key/value pairs.

=head1 SYNOPSIS

B<webjob-cfg-set-kvps> B<[-d delimiter]> B<[-o option[,option[,...]]]> B<[-q quote]> B<[-t type]> B<-f file> B<key=value [key=value ...]>

=head1 DESCRIPTION

This utility sets one or more key/value pairs in the specified config
file.  Values for existing keys will be overwritten, and any comments
and/or blank lines will be lost as a result of updating the file.
Finally, all keys will be sorted lexically.

=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.

Note: The delimiter for key/value pairs specified on the command line
is affected by this option.  Also, make sure key/value pair arguments
are properly quoted -- especially when the specified delimiter is a
pipe.

=item B<-f file>

Specifies the name of the config file to create or alter.  If the file
does not exist, it will be created.

=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 QuoteValues

Enable the quoting of all values.  When enabled, any quotes (of the
same kind) embedded in the value are escaped with a backslash '\'.

=back

=item B<-q quote>

Specifies the quote symbol (either single or double).  The default
quote symbol is a double quote.  The quoting of values is disabled by
default.

=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<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-get-kvps(1)

=head1 LICENSE

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

=cut
