package parse_checkfile;
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(parse_checkfile get_confvar);

#------------------------------------------------------------------------------
# modul parse_checkfile.pm
#
# Copyright (c) 2002-2004  eisfair webconf team
#
# Creation:     2004/07/10
# Last Update:  $Id$
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#------------------------------------------------------------------------------

use strict;
use CGI::Carp qw(fatalsToBrowser);
use Data::Dumper;
use Fcntl ':flock';

my $tag_emptyline = '^\s*$';
my $tag_comment_emptyline = '^[ \t]*#.*$|^\s*$';
my $tag_comment_separator  = '^####.*$|^#---.*$|^#===.*$';

# release settings
my $configpath  = "/etc/config.d";
my $checkpath   = "/etc/check.d";
my $defaultpath = "/etc/default.d";
my $layoutpath  = "/etc/layout.d";
my $helppath    = "/var/install/help";

# for testing only
#my $configpath  = "./config";
#my $checkpath   = "./check";
#my $defaultpath = "./default";
#my $layoutpath  = "./layout";
#my $helppath    = "./help";


# get_confvar description {{{
##########################################################
# sub get_confvar($cf)
# - reads the configuration variables from $cf
# IN:  $cf - name of configfile relative to /etc/config.d
#            or absolute path
# OUT: %hash (var-name => var-value)
##########################################################
# }}}
# sub get_confvar($cf) {{{
sub get_confvar {
   my $cf = shift;
   my ($name, $val);
   my %opt_hash;

   $cf = "$configpath/$cf" unless ($cf =~ /^\/.*/);

   # read the content of the config-file
   open(CONF, "<$cf");
   flock(CONF, LOCK_EX);

   # get configuration variables
   while(<CONF>) {
      next if (!/^[ \t]*([A-Z].*)=('[^']*'|"[^"]*")[ \t]*(#.*)?\s*$/);
      $opt_hash{"$1"} = substr($2,1,length($2)-2);
   }

   flock(CONF, LOCK_UN);
   close(CONF);

   return %opt_hash;
}
# }}}



# sub get_checkfile($package) {{{
sub get_checkfile {
   my %check;
   my ($name, $mastername, $masterprefix, $optional);
   my $package = shift;
   
   open(CHECKFILE, "<$checkpath/$package") || die("Could not open $checkpath/$package, reason: $!\n");
   while(<CHECKFILE>)
   {
      next if (/$tag_comment_emptyline/);
      s/\r?\n$//;                            # 
      s/[ \t]+/ /g;                          # replace tabs and multi-spaces

      # handling of optional variables
      $optional =  $_;
      $optional =~ s/^(\++).*/\1/;
      s/^\++//;

      my %helement;
      my @fields = split(/ /, $_, 4);
      $fields[3] =~ s/[[:space:]]$//g;

      $name               = $fields[0];
      $helement{"name"}   = $fields[0];
      if ($fields[2] && $fields[2] ne "-")
      {
         $helement{"master"}   = $fields[2];
         $name                 =~ s/%/[0-9]+/g;
         $mastername           = $fields[2];
         $mastername           =~ s/%/[0-9]+/g;
         $masterprefix         = $fields[0];
         $masterprefix         =~ s/[^%]*$//;
         $check{$mastername}->{"ismaster"} = $masterprefix;

         $helement{"optional"} = "yes"    if ($optional eq "++");
      }
      else
      {
         $helement{"optional"} = "yes"    if ($optional eq "+" || $optional eq "++");
      }
      $helement{"match"}  = $fields[3];

      $check{$name} = \%helement;
   }
   close(CHECKFILE);

   return \%check;
}
# }}}

# sub get_defaultfile($package) {{{
sub get_defaultfile {
   my @default;
   my ($comment, $value);
   my $package = shift;
   
   open(DEFAULTFILE, "<$defaultpath/$package") || die("Could not open $defaultpath/$package, reason: $!\n");
   while(<DEFAULTFILE>)
   {
      next if (/$tag_emptyline/);
      s/\r?\n$//;
      s/[ \t]+/ /g;

      my %helement;

      if ($_ =~ /^#/)
      {  ### full comment
         $helement{"fullcomment"} = $_;
      }
      elsif ($_ =~ /^[ \t]+#/)
      {  ### comment
         $comment = $_;
         $comment =~ s/^[ \t]+# //;
         if ($comment)
         {
            if ($default[-1]->{"comment"}) { $default[-1]->{"comment"} .= " "; }
            $default[-1]->{"comment"} .= $comment;
         }
         next;
      }
      else
      {  ### value
         my @fields = split(/=/, $_, 2);
         $helement{"name"} = $fields[0];
         $value = $fields[1];
         $value =~ s/^'//;
         $value =~ s/'.*//;
         $helement{"default"} = $value;
         $comment  = $fields[1];
         $comment  =~ s/^'[^']*'//;
         $comment  = "" unless ($comment =~ /^[ \t]+#/);
         $comment  =~ s/^[ \t]+# //;
         $helement{"comment"} = $comment;
      }

      push(@default, \%helement);
   }
   close(DEFAULTFILE);

   return \@default;
}
# }}}

# sub get_layoutfile($package) {{{
sub get_layoutfile
{
   my $package = shift;
   my %layout;
   my ($lastname, $splitcnt) = ("", 3);
   
   if (! -f "$layoutpath/$package")
   {
      return 0;
   }
   
   open(LAYOUTFILE, "<$layoutpath/$package") || die("Could not open $layoutpath/$package, reason: $!\n");
   while(<LAYOUTFILE>)
   {
      next if (/$tag_comment_emptyline/);
      s/\r?\n$//;
      s/[ \t]+/ /g;
      if (/^ /)
      {   # variable name is omitted, take the last used
          s/^ //;
          $splitcnt = 2;
      } 
      else
      {
          $splitcnt = 3;
      }

      my ($name, $option, $value);
      my @fields = split(/ /, $_, $splitcnt);

      # splitcnt = 2 -> use last variable name
      if ($splitcnt == 2)
      {
          $name   = $lastname;
          $option = lc($fields[0]);
          $value  = $fields[1];
      }
      else
      {
          $name     = uc($fields[0]);
          $lastname = $name;
          $option   = lc($fields[1]);
          $value    = $fields[2];
      }

      # fill in layout infos
      if ($option =~ /^(style|value)$/)
      {
          $value = lc($value) if ($option eq "style");
          push(@{ $layout{"$name"}{"$option"} }, $value);
      }
      else
      {
          $layout{"$name"}{"$option"} = $value;
      }
   }

   return \%layout;
}
# }}}

# sub get_helpfile($package)
# {{{
sub get_helpfile
{
    my $package   = shift;
    my $key       = "";

    my $tag_help_on  = '^[ \t]*<help name="(.*?)">';
    my $tag_help_off = '^[ \t]*<\/help>[ \t]*(#.*?)?\s*$';

    my ($inhelp, $text) = (0, "");

    my %help;

    if (! -f "$helppath/$package")
    {
        return 0;
    }
    
    open(HELPFILE, "<$helppath/$package") || die("Could not open $helppath/$package, reason: $!\n");
    while (<HELPFILE>)
    {
        if ($key && /$tag_help_off/)
        {
            $help{$key} = $text;
            $key = "";
        }
        elsif ($key)
        {
            $text .= $_;
        }
        elsif (/$tag_help_on/)
        {
            $key  = $1;
            $key  =~ s/#+/%/g;
            $text = "";
        }
    }
    close(HELPFILE);

    return \%help;
}
# }}}

# sub parse_checkfile($package) {{{
sub parse_checkfile
{
   my $package = shift;

   my $temp;
   my ($check, $default, $layout, $help);

   ### read and parse check-file
   $check = get_checkfile($package);

   ### read and parse default-file
   $default = get_defaultfile($package);

   ### read and parse layout-file
   $layout = get_layoutfile($package);

   ### read helpfile
   $help = get_helpfile($package);
   
   ### prepare structure
   my (@astruct, %hformat_struct);
   my (@aform,   %hformat_form);
   my (@adefaultgroup,  %hformat_defaultgroup);

   $hformat_struct{"prepare_script"} = "";
   $hformat_struct{"finish_script"}  = "";

   $astruct[0] = \%hformat_struct;
   $astruct[1] = \@aform;

   $hformat_form{"apply_script"} = "";
   if ($layout && $layout->{"-"})
   {
       $temp = $layout->{"-"}{"user-vars"} || "";
   }
   else
   {
       $temp = "";
   }
   if ($temp =~ /^(no|hidden|show|edit)$/)
   {
       $hformat_form{"user-vars"} = $temp;
   }
   else
   {
       $hformat_form{"user-vars"} = "no";
   }

   $aform[0]   = \%hformat_form;

   $hformat_defaultgroup{"name"} = "[default group]";
   $adefaultgroup[0]  = \%hformat_defaultgroup;


   ### fill structure
   my ($line, $group, $element, $fullcomment, $status, $checkline);
   $status = 0;
   $group  = "";
   $fullcomment = "";
   my $current_group = \@adefaultgroup;
   foreach $line ( @{ $default } )
   {
      if ($line->{"fullcomment"})
      {
         if ($line->{"fullcomment"} =~ /$tag_comment_separator/)
         {
            $status = 0;
         }
         elsif ($status == 0)
         {
            $group = $line->{"fullcomment"};
            $group =~ s/#\s*//;
            $fullcomment = "";
            $status = 1;
         }
         else
         {
            $temp = $line->{"fullcomment"};
            $temp =~ s/^#+ {0,1}//;
            if ($fullcomment) { $fullcomment .= "\n" . $temp; }
            else { $fullcomment = $temp; }
         }
      }
      else
      {
         $status = 2;
         if ($group)
         {
            my (@agroup,  %hformat_group);
            $hformat_group{"name"} = $group;
            $fullcomment =~ s/( \t\n)*//g;
            $hformat_group{"comment"} = $fullcomment if ($fullcomment);
            $agroup[0]  = \%hformat_group;
            push(@aform, \@agroup);
            $current_group = \@agroup;

            $group  = "";
            $fullcomment = "";
         }

         my (%helement, $name, $found);
         $name = $line->{"name"};
         foreach $checkline ( keys %{ $check } )
         {
            if ($name =~ /^$checkline$/)
            {
               $found = "yes";
               if (!$check->{$checkline}->{"mark"})
               {
                  $helement{"name"}     = $check->{$checkline}->{"name"};
                  $helement{"default"}  = $line->{"default"};
                  if ($check->{$checkline}->{"match"} =~ /^YESNO$/) { $helement{"type"} = "yesno"; }
                  else                                              { $helement{"type"} = "edit"; }
                  $helement{"match"}    = $check->{$checkline}->{"match"};
                  $helement{"comment"}  = $line->{"comment"} if $line->{"comment"};
                  $helement{"ismaster"} = $check->{$checkline}->{"ismaster"} if $check->{$checkline}->{"ismaster"};
                  $helement{"master"}   = $check->{$checkline}->{"master"}   if $check->{$checkline}->{"master"};
                  $helement{"optional"} = $check->{$checkline}->{"optional"} if $check->{$checkline}->{"optional"};

                  $check->{$checkline}->{"mark"} = "yes";
                  push(@$current_group, \%helement);
               }
               last;
            }
         }
         if (!$found)
         {
            $helement{"name"}    = $name;
            $helement{"type"}    = "edit";
            $helement{"default"} = $line->{"default"};
            $helement{"comment"} = $line->{"comment"} if $line->{"comment"};

            push(@$current_group, \%helement);
         }

      }
   }
   
   ### process all unmarked variables
   foreach $checkline ( keys %{ $check } )
   {
      if (!$check->{$checkline}->{"mark"})
      {
         my %helement;
         $helement{"name"} = $check->{$checkline}->{"name"};
         if ($check->{$checkline}->{"match"} =~ /^YESNO$/) { $helement{"type"} = "yesno"; }
         else                                              { $helement{"type"} = "edit"; }
         $helement{"match"}    = $check->{$checkline}->{"match"};
         $helement{"ismaster"} = $check->{$checkline}->{"ismaster"} if $check->{$checkline}->{"ismaster"};
         $helement{"master"}   = $check->{$checkline}->{"master"}   if $check->{$checkline}->{"master"};
         $helement{"optional"} = $check->{$checkline}->{"optional"} if $check->{$checkline}->{"optional"};

         push(@adefaultgroup, \%helement);
      }
   }
   
   if ($#{@adefaultgroup} > 0)
   {
      push(@aform, \@adefaultgroup);
   }
   
   my ($name, $option, $style, $file);
   ### now add infos from %layout, %help and missing defaults
   foreach $group ( 1 .. $#{@aform} )
   {
      foreach $element ( 1 .. $#{$aform[$group]} )
      {
         $name = $aform[$group]->[$element]->{"name"};
         if ($help && $help->{"$name"})         # are there helptexts?
         {
             $aform[$group]->[$element]->{"help"} = $help->{"$name"};
         }
         if ($layout && $layout->{"$name"})     # are there layout infos?
         {
            foreach $option (keys(%{ $layout->{"$name"} }))
            {
               if ($option eq "style")    # check style options
               {
                  foreach $style (@{ $layout->{"$name"}{"$option"} })
                  {
                     if ($style eq "readonly")
                     {
                        $aform[$group]->[$element]->{"readonly"} = "yes" ;
                     }
                     elsif ($style eq "hidden")
                     {
                        $aform[$group]->[$element]->{"hidden"} = "yes" ;
                     }
                     elsif ($aform[$group]->[$element]->{"style"})
                     {
                        $aform[$group]->[$element]->{"style"} .= ",$style";
                     }
                     else
                     {
                        $aform[$group]->[$element]->{"style"} = "$style";
                     }
                  }
               }
               elsif ($option eq "file")
               {
                  $file = $layout->{"$name"}{"file"};
                  if (-f $file)
                  {
                     open(VALUEFILE, "<$file") || die("Could not open $file, reason: $!\n");
                     while (<VALUEFILE>)
                     {
                        s/\r?\n$//;
                        push(@{ $aform[$group]->[$element]->{"values"} }, $_);
                     }
                     close(VALUEFILE);
                  }
               }
               elsif ($option eq "value")
               {
                  $aform[$group]->[$element]->{"values"} = $layout->{"$name"}{"$option"} if $layout->{"$name"}{"$option"};
               }
               else
               {
                  $aform[$group]->[$element]->{"$option"} = $layout->{"$name"}{"$option"} if $layout->{"$name"}{"$option"};
               }
            }
         }
         # now add remaining defaults
         if ($aform[$group]->[$element]->{"type"} eq "yesno")
         {
            my @values = ("yes", "no");
            $aform[$group]->[$element]->{"type"}   = "select";
            $aform[$group]->[$element]->{"style"}  = "singleselect,dropdown";
            $aform[$group]->[$element]->{"values"} = \@values;
         }
         if ($aform[$group]->[$element]->{"values"} && $aform[$group]->[$element]->{"type"} eq "edit")
         {
            $aform[$group]->[$element]->{"type"} = "select";
            if (!$aform[$group]->[$element]->{"style"})
            {
               $aform[$group]->[$element]->{"style"} = "singleselect,dropdown";
            }
         }
      }
   }

   return \@astruct;
}
# }}}

1;
