# Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved.
#
#    NAME
#      asmcmdanlz
#
#    DESCRIPTION
#       This module implements the analyze command.
#
#
#    MODIFIED  (MM/DD/YY)
#    manuegar  08/19/13  - Backport manuegar_bug-13951456 from main
#    apfwkr    01/04/12  - Backport moreddy_bug-12818129 from main
#    moreddy   01/13/11  - Backport moreddy_bug-8667038 from main
#    canuprem  05/13/10  - .
#    canuprem  12/11/09  - creation

  ######################## FUNCTIONS LIST #########################
#   asmcmdanlz_process_help 
#   asmcmdanlz_process_cmd 
#   asmcmdanlz_is_cmd 
#   asmcmdanlz_is_wildcard_cmd 
#   asmcmdanlz_process_analyze()
#   asmcmdanlz_wanted_trc
#   asmcmdanlz_get_file_attrs()
#   asmcmdanlz_mine()
#   asmcmdanlz_srch_ioerror()
#   asmcmdanlz_srch_dskoffline()
#   asmcmdanlz_srch_dgdismnt()
#   asmcmdanlz_srch_blkrp
#   asmcmdanlz_get_disk_attr()
#   asmcmdanlz_get_dg_attr()
#   asmcmdanlz_prepare()
#   asmcmdanlz_init_col_width()
#   asmcmdanlz_init_row_info()
#   asmcmdanlz_add_file_record()
#   asmcmdanlz_fetch_file_db()
#   asmcmdanlz_fetch_filedb_filename()
#   asmcmdanlz_add_event()
#   asmcmdanlz_fetch_events()
#   asmcmdanlz_fetch_event_by_indx()
#   asmcmdanlz_compare_time()
#   asmcmdanlz_normalize()
#   asmcmd_mon_to_num()
#   asmcmdanlz_is_no_instance_cmd 
#   asmcmdanlz_parse_int_args 
#   asmcmdanlz_syntax_error 
#   asmcmdanlz_get_cmd_syntax 
#   asmcmdanlz_get_asmcmd_cmds 
#####################################################################

package asmcmdanlz;
require Exporter;
our @ISA    = qw(Exporter);
our @EXPORT = qw(asmcmdanlz_init
                 %asmcmdanlz_cmds
                 );
 
use strict;
use Getopt::Long qw(:config no_ignore_case bundling);
use asmcmdglobal;
use asmcmdshare;

use List::Util qw[min max];
use File::Find;
use File::stat;
use Sys::Hostname;
use Time::Local;
use Cwd 'realpath';

       ######################### HEADERS #############################

our ($ASMCMDANLZ_HOSTNAME)   = "HOSTNAME";
our ($ASMCMDANLZ_INSTANCE)   = "INSTANCE";
our ($ASMCMDANLZ_DG_NAME)    = "DG_NAME";
our ($ASMCMDANLZ_PID)        = "PID";
our ($ASMCMDANLZ_EVENT)      = "EVENT";
our ($ASMCMDANLZ_RESOLUTION) = "EFFECT";
our ($ASMCMDANLZ_DISK_PATH)  = "DISKPATH";
our ($ASMCMDANLZ_TRACEFILE)  = "TRACEFILE";

my @files;
my (@found_events);

my (@files_db);                 # database for logs and traces 
my (@events);                   # database for found events 
my ($files_iterator) = 0;         
my ($iterator) = 0;
my (%filenames);

my %diags;
my %locs;

my ($st_time, $end_time);

my $trace_file;                 # trace file location

my (@dgnames);
my (@eventtypes);

# various patterns of time stamps
 
#YYYY-MM-DD-HH:MI:SS  - covered
#YYYY-MM-DD HH24:MI:SS.FF3  - covered
#YYYY-MM-DD HH24:MI:SS  - covered
my $datepattern1 = '(\d\d\d\d-\d\d-\d\d[\s-]\d\d:\d\d:\d\d(\s|\.\d+))';

#DD-MON-YYYY HH24:MI:SS:FF3 - covered
#DD-MON-YY HH24:MI:SS - covered
#DD-MON-YYYY HH24:MI:SS - covered
my $datepattern2 = '(\d\d-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)' .                     '-(\d\d\d\d|\d\d)\s\d\d:\d\d:\d\d(\s|:\d+|\.\d+))';

# Mon DD HH24:MI:SS TZD YYYY
# Mon DD HH24:MI:SS YYYY
# MON DD HH24:MI:SS YYYY
my $datepattern3 = '((Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)' . 
                   '\s\d\d\s\d\d:\d\d:\d\d(\s|\s.{3}\s)\d\d\d\d)';

# DaY Mon DD HH24:MI:SS YYYY
# DaY Mon DD HH24:MI:SS TDZ YYYY
my $datepattern4 = '((Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s' . 
                   '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)' . 
                   '\s+\d+\s\d\d:\d\d:\d\d(\s|\s.{3}\s)\d\d\d\d)';

# MM-DD-YYYY HH24:MI:SS.FF3
# MM-DD-YYYY HH24:MI:SS
my $datepattern5 = '(\d?\d-\d?\d-\d\d\d\d\s\d\d:\d\d:\d\d(\s|\.\d+))';

my $datepattern = "($datepattern1|$datepattern2|$datepattern3|" . 
                  "$datepattern4|$datepattern5)";

our (%asmcmdanlz_cmds) = ( analyze   => {flags      =>  {'G=s' => 'diskGroup',
                                                         'node=s' => 'nodeName',
                                                           'events=s' =>
                                                           'eventsToCollect',
                                                           'starttime=s' =>
                                                           'startTime',
                                                           'endtime=s' =>
                                                           'endTime=s',
                                                           'tracedirectory=s' =>
                                                           'traceDir',
                                                           'logfile=s' =>
                                                           'outputFile',
                                                           'columns=s' =>
                                                           'columnsNames'
                                                         } 
                                       } 
                        );

# Events registration

my (%eventhash) = (  IOError => { srch_func  => \&asmcmdanlz_srch_ioerror,
                                  entity     => "Disk"},
                     Offline => { srch_func  => \&asmcmdanlz_srch_dskoffline,
                                  entity     => "Disk"},
                     Dismount => {srch_func => \&asmcmdanlz_srch_dgdismnt,
                                  entity     => "Diskgroup"},
                      BlockRepair => {srch_func => \&asmcmdanlz_srch_blkrp,
                                     entity     => "Disk"}
                  );

# Entities Registration

my (%entityhash) = ( Disk => \&asmcmdanlz_get_disk_attr,
                     Diskgroup => \&asmcmdanlz_get_dg_attr);


#dependencies

my (%dependencies) = ( IOError => ["Dismount","Offline","BlockRepair"]);


sub is_asmcmd
{
  return 1;
}

########
# NAME
#   asmcmdanlz_init
#
# DESCRIPTION
#   This function initializes the asmcmdanlz module.  For now it simply 
#   registers its callbacks with the asmcmdglobal module.
#
# PARAMETERS
#   None
#
# RETURNS
#   Null
#
# NOTES
#   Only asmcmdcore_main() calls this routine.
########
sub init
{
  # All of the arrays defined in the asmcmdglobal module must be 
  # initialized here.  Otherwise, an internal error will result.
  push (@asmcmdglobal_command_callbacks, \&asmcmdanlz_process_cmd);
  push (@asmcmdglobal_help_callbacks, \&asmcmdanlz_process_help);
  push (@asmcmdglobal_command_list_callbacks, \&asmcmdanlz_get_asmcmd_cmds);
  push (@asmcmdglobal_is_command_callbacks, \&asmcmdanlz_is_cmd);
  push (@asmcmdglobal_is_wildcard_callbacks, \&asmcmdanlz_is_wildcard_cmd);
  push (@asmcmdglobal_syntax_error_callbacks, \&asmcmdanlz_syntax_error);
  push (@asmcmdglobal_no_instance_callbacks, \&asmcmdanlz_is_no_instance_cmd);
  %asmcmdglobal_cmds = (%asmcmdglobal_cmds, %asmcmdanlz_cmds);

  #Perform ASMCMD consistency check if enabled
  if($asmcmdglobal_hash{'consistchk'} eq 'y')
    {
      if(!asmcmdshare_check_option_consistency(%asmcmdanlz_cmds))
        {
          exit 1;
        }
    }
}

########
# NAME
#   asmcmdanlz_is_help
#
# DESCRIPTION
#   This function is the help function for the ASMCMDANLZ module.
#
# PARAMETERS
#   command     (IN) - display the help message for this command.
#
# RETURNS
#   1 if command found; 0 otherwise.
########
sub asmcmdanlz_process_help 
{
  my ($command) = shift;       # User-specified argument; show help on $cmd. #
  my ($syntax);                                   # Command syntax for $cmd. #
  my ($desc);                                # Command description for $cmd. #
  my ($succ) = 0;                         # 1 if command found, 0 otherwise. #

  if (asmcmdanlz_is_cmd ($command)) 
    {                              # User specified a command name to look up. #
      $syntax = asmcmdanlz_get_cmd_syntax($command);
      $desc = asmcmdshare_get_help_desc($command);
      print "        $syntax\n" .
        "$desc\n";
      $succ = 1;
    }

  return $succ;
}


########
# NAME
#   asmcmdanlz_process_cmd
#
# DESCRIPTION
#   This routine calls the appropriate routine to process the command 
#   specified by $asmcmdglobal_hash{'cmd'}.
#
# PARAMETERS
#   dbh       (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   1 if command is found in the asmcmdanlz module; 0 if not.
#
# NOTES
#   Only asmcmdcore_shell() calls this routine.
########
sub asmcmdanlz_process_cmd 
{
  my ($dbh) = @_;
  my ($succ) = 0;

  # Get current command from global value, which is set by 
  # asmcmdanlz_parse_asmcmd_args()and by asmcmdcore_shell().
  my ($cmd) = $asmcmdglobal_hash{'cmd'};

  # Declare and initialize hash of function pointers, each designating a 
  # routine that processes an ASMCMDANLZ command.
  my (%cmdhash) = ( analyze    => \&asmcmdanlz_process_analyze);


  if (defined ( $cmdhash{ $cmd } ))
    {
      # If user specifies a known command, then call routine to process it. #
      $cmdhash{ $cmd }->($dbh);
      $succ = 1;
    }

  return $succ;
}

########
# NAME
#   asmcmdanlz_is_cmd
#
# DESCRIPTION
#   This routine checks if a user-entered command is one of the known
#   ASMCMD internal commands that belong to the ASMCMDANLZ module.
#
# PARAMETERS
#   arg   (IN) - user-entered command name string.
#
# RETURNS
#   True if $arg is one of the known commands, false otherwise.
########
sub asmcmdanlz_is_cmd 
{
  my ($arg) = shift;

  return defined ( $asmcmdanlz_cmds{ $arg } );
}


########
# NAME
#   asmcmdanlz_is_wildcard_cmd
#
# DESCRIPTION
#   This routine determines if an ASMCMDANLZ command allows the use 
#   of wild cards.
#
# PARAMETERS
#   arg   (IN) - user-entered command name string.
#
# RETURNS
#   True if $arg is a command that can take wildcards as part of its argument, 
#   false otherwise.
########
sub asmcmdanlz_is_wildcard_cmd 
{
  my ($arg) = shift;

  return defined ($asmcmdanlz_cmds{ $arg }) &&
    defined ($asmcmdanlz_cmds{ $arg }{ wildcard });
}

########
# NAME
#   asmcmdanlz_process_analyze
#
# DESCRIPTION
#   This routine processes the asmcmd analyze command
#
# PARAMETERS
#   dbh   (IN) - database handle
#
# RETURNS
#   NONE
# 
# NOTES
#   This function finds the log and trace files and calls the other 
#   functions that act upon the found files.
########
sub asmcmdanlz_process_analyze()
{
  my ($dbh) = @_;
  my ($verbose) = 0;
  my (@what, @from, @where);
  my ($test) = 0 ;
  my ($path, $path1);
  my (%bases, %homes);
  my (%args);
  my (@bases_arr, @homes_arr, @log_arr);
  my (@tokens);
  my ($num);
  my ($cmd);
  my ($line);
  my ($home);
  my ($ret);
  my ($buf);
  my ($ndname);
  my ($tracedir);
  my ($logfile);
  my (%columns);
  my ($qry);
  my ($val);
  my ($row);
  my ($kfod);
  my (@buf);
  my ($sth);
  my ($t);

  my @time = localtime(time);
  #print "@time\n";

  $ret =  asmcmdanlz_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
  return unless defined ($ret);

  if(defined($args{'G'}))
  {
    @dgnames = split("," , $args{'G'});
    
    for(my $i = 0; $i < @dgnames; $i++)
    {
      $dgnames[$i] =~ s,$ASMCMDGLOBAL_WCARD_CHARS,\.\*,g;
    }
  }

  if(defined($args{'node'}))
  {
    $ndname = $args{'node'};
  } 

  if(defined($args{'events'}))
  {
    @eventtypes = split("," , $args{'events'});

    for(my $i = 0; $i < @eventtypes; $i++)
    {
      $eventtypes[$i] =~ s,$ASMCMDGLOBAL_WCARD_CHARS,\.\*,g;
    }    
  }

  if(defined($args{'starttime'}))
  {
    $st_time = asmcmdanlz_normalize($args{'starttime'});
    
    if(!defined($st_time) || $st_time eq "ERROR")
    {
      asmcmdshare_error_msg(8602, undef);
      return;
    }
  }
  
  if(defined($args{'endtime'}))
  {
    $end_time = asmcmdanlz_normalize($args{'endtime'});

    if(!defined($end_time) || $end_time eq "ERROR")
    {
      asmcmdshare_error_msg(8602, undef);
      return;
    }
  }
  
  if(defined($args{'tracedirectory'}))
  {
    $tracedir = $args{'tracedirectory'};

    if(!(-d $tracedir))
    {
      asmcmdshare_error_msg(8603, undef);
      return;
    }

    $tracedir = $tracedir . "/";

    find({wanted => \&asmcmdanlz_wanted_trc, untaint => 1}, $tracedir);
  }  

  if(defined($args{'logfile'}))
  {
    $logfile = $args{'logfile'};
  }

  if(defined($args{'columns'}))
  {
    %columns = split("," ,$args{'columns'});
  }

  #ORACLE_BASE is set 
  if(defined($ENV{'ORACLE_BASE'}))
  { 
    $path = $ENV{'ORACLE_BASE'};
    $path =~ /([^\n^\r^\t]+)/;
    $path = $1;
    $bases{$path} = 1;
    $path .= "/diag/";
    $locs{$path} = 1;
  }

  if(defined($ENV{'ORACLE_HOME'}))
  {
    $path = $ENV{'ORACLE_HOME'};
    $path =~ /([^\n^\r^\t]+)/;
    $path = $1;

    $homes{$path} = 1;
    
    $path1 = $path . "/log/";
    $locs{$path1} = 1;
    
    $path .= "/rdbms/log/";
    $locs{$path} = 1;
  }

  # read ORACLE_HOME from /etc/oratab
  if(-f "/etc/oratab")
  {
     if(open(F, "/etc/oratab"))
     {
        while($line = <F>) 
        {
           # filter out the comments
           if(!($line =~ /^\#/))  
           {
             #$line = grep(/:/, $line);
             push(@buf, $line);
           }
        }
        close(F);
     }
      
     @buf = grep(/:/, @buf);
     $num = scalar @buf;

     foreach $line (@buf)
     {
       # get the ORACLE_HOMEs from the oratab
       if($line =~ /([^\/]+)([^:]+)/)
       {
         $path = $2;
         $homes{$path} = 1;
         
         $path1 = $path . "/log/";
         $locs{$path1} = 1;
         
         $path .= "/rdbms/log/";
         $locs{$path} = 1;
      }
    }
  }

  # read from /etc/oraInst.loc
  if(-f "/etc/oraInst.loc")
  {
    if(open (FILE, "/etc/oraInst.loc"))
    {
      while(<FILE>)
      {
        if($_ =~ /^inventory_loc/)
        {
          push(@buf, $_);
        }
      }

      close(FILE);
    }
      
    foreach $line (@buf)
    {
      if($line =~ /([^\/]+)([^\n]+)/)
      {
        $path = $2;
            
        if(-e "$path/ContentsXML/inventory.xml")
        {
          my @buff_arr;
          my $temp;
  
          if(-e "$path/ContentsXML/inventory.xml")
          {
            if(open (FILE, "$path/ContentsXML/inventory.xml"))
            {
              while(<FILE>)
              {
                if($_ =~ /LOC/)
                {
                  push(@buff_arr, $_);
                }
              }

              close(FILE);
            }
            
            @buff_arr = grep(!/CRS=\"true\"/, @buff_arr);

            @buff_arr = grep(!/REMOVED=\"T\"/, @buff_arr);

            # equivalent grep command for the above two greps is : 

            #$cmd = "/bin/cat $path/ContentsXML/inventory.xml | $grep LOC | 
            #$cmd = "/bin/cat /home/canuprem/inventory.xml | $grep LOC |"
            #      . '/bin/grep -v CRS=\"true\" | /bin/grep -v REMOVED=\"T\" ';
            
          }
 
          foreach $temp (@buff_arr)
          {
            $temp =~ /(.*LOC=\")([^\"]+)/;
            $path =$2;

            $path1 = $path . "/log/";
            $locs{$path1} = 1;

            $path .= "/rdbms/log/";
            $locs{$path} = 1;
          }
        }
      }
    }
  }

  # read from kfod
  if(defined($ENV{'ORACLE_HOME'}))
  {
    $kfod = "$ENV{'ORACLE_HOME'}/bin/kfod";

    # Make sure the kfed binary exists and can be executed. 
    if (! -x $kfod)
    {
      # If not, try at a second locaction.
      $kfod = "$ENV{'ORACLE_HOME'}/rdbms/bin/kfod";
    }

  }
  
  if($^O =~ /win/i)
  {
    $kfod .= ".exe";
  }

  if( -x $kfod)
  {
    my @buf1;
    $cmd = "$kfod op=insts";
      
    # untaint
    $cmd =~ /([^\n^\r^\t]+)/;
    $cmd = $1;
      
    eval
    {
      @buf1 = `$cmd`;
    };    

    if($@ eq '')
    {
      # knock off the top three lines as they contain the header
      # not exaclty required, as they will not match the regexp anyway
      shift(@buf1);
      shift(@buf1);
      shift(@buf1);

      foreach $buf (@buf1)
      {
        if($buf =~ /([^\/]+)([^\s]+)/)
        {
          $path = $2;
          $homes{$path} = 1;

          $path1 = $path . "/log/";
          $locs{$path1} = 1;

          $path .= "/rdbms/log/";
          $locs{$path} = 1;
        }
      }
    }
  }

  if(defined($dbh))
  {

      push(@what, 'v$parameter.value as value');
      push(@from, 'v$parameter');
      push(@where, 'v$parameter.name=\'core_dump_dest\' OR v$parameter.name=
              \'backgroup_dump_dest\' OR v$parameter.name=\'user_dump_dest\'');

      $sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where);
      
      while (defined($row = asmcmdshare_fetch($sth)))
      {
        $val = $row->{VALUE};
        $val = $val . "/";
        # search for trace files, no need to search for diag dir.
        find({wanted => \&asmcmdanlz_wanted_trc, untaint => 1}, $val);
      }
      asmcmdshare_finish($sth);
  }
  # -- find only if they are not null.
  @bases_arr = keys(%locs);
  
  find({wanted => \&asmcmdanlz_wanted_trc, untaint => 1}, @bases_arr);

  asmcmdanlz_get_file_attrs();
   
  asmcmdanlz_mine();

  asmcmdanlz_prepare();

}

########
# NAME
#   asmcmdanlz_wanted_trc
#
# DESCRIPTIN
#   This routine is the callback registered for the perl 'find' api
#
# PARAMETERS
#    NONE
#
# RETURNS
#    NONE
########
sub asmcmdanlz_wanted_trc
{
  my ($OP);
  my $file = "$File::Find::name";

  # store if the file is either a trace file or a log file
  if(/\.trc$/ or /\.log$/)
  {
    my $buf;
    my $ls = "/bin/ls -l $file";
 
    push(@files,"$File::Find::name");
  }

}

########
# NAME
#   asmcmdanlz_get_file_attrs
#
# DESCRIPTION
#   This routine scans the traces and logs, that are found, for their 
#   attributes and stores them in the files database.
#
# PARAMETERS
#    NONE
#
# RETURNS
#    NONE
########
sub asmcmdanlz_get_file_attrs()
{
  # varous patterns of the timestamps

  #my ($datepattern1) = '(\d\d\d\d-\d\d-\d\d[\s-]\d\d:\d\d:\d\d(\s|\.\d+))';

  #my ($datepattern2) = '(\d\d-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)' .                     '-(\d\d\d\d|\d\d)\s\d\d:\d\d:\d\d(\s|:\d+|\.\d+))';

  #my ($datepattern3) = '((Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)' . 
  #                     '\s\d\d\s\d\d:\d\d:\d\d(\s|\s.{3}\s)\d\d\d\d)';

  #my ($datepattern4) = '((Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s' . 
  #                     '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)' . 
  #                     '\s+\d+\s\d\d:\d\d:\d\d(\s|\s.{3}\s)\d\d\d\d)';

  #my ($datepattern5) = '(\d\d-\d\d-\d\d\d\d\s\d\d:\d\d:\d\d(\s|\.\d+))';

  #my ($datepattern)  = "($datepattern1|$datepattern2|$datepattern3|" . 
  #                     "$datepattern4|$datepattern5)";

  my ($inputfile);
  my ($newtime) = '';
  my ($endtime) = '';
  my ($time)    = '';
  my ($command, @buf, $temp);

  # scan through the files for the attributes
  LINE: foreach $inputfile (@files)
  {
    my ($opid);
    my ($pid);
    my ($host);
    my ($inst);
    my (@row);
    my (%rec);
    my ($crttime, $endtime);     #create time and last updated time of a file

    if(open (INFH, $inputfile))   
    {
       while(<INFH>)
       {
         chomp;

         if($_ ne '')
         {
           if(/$datepattern/i  && !defined($crttime))
           {
             $time = $1;
             
             $newtime = asmcmdanlz_normalize($time);

             if(defined($st_time))
             {
               # starttime option is given by the user
               my $new_st_time = asmcmdanlz_normalize($st_time);

               if(asmcmdanlz_compare_time($st_time, $time) == 1) 
               {
                 next LINE;
               }
             }

             # Write to the trc file if format is unknown

             if(defined($newtime))
             {
               # The line should not begin with "Version"
               if($_ !~ /Version:/)  
               {
                 # Get the last updated time of the file
                 my $date = stat($inputfile)->mtime;

                 # This shows only upto seconds, hence increment by one 
                 $endtime = scalar localtime($date + 1);
                   
                 $endtime = asmcmdanlz_normalize($endtime);

                 if(defined($end_time))
                 {
                   # End time option is specified.
                   if(asmcmdanlz_compare_time($end_time, $endtime) == -1)  
                   {
                     next LINE;
                   }
                 }
                 # If it comes here, $newtime is file creation time.
                 $crttime = $newtime;

               }
             }
           }
           elsif(/^Oracle process number:\s([\d]+)/)
           {
             #get the opid
             $opid = $1;
           }
           elsif(/process pid:\s([\d]+)/)
           {
             #Get the os pid
             $pid = $1;
           }
           elsif(/^Instance name:\s(.*)/)
           {
             # Get the isntance name
             $inst = $1;
           }
           elsif(/^Node name:([\s]+)(.*)/)
           {
             # Get the host name
             $host = $2;
           }
        }

        if($inputfile =~ /\.trc$/) 
        {
          # If the current file is the trace file, all of these atributes can
          # be found. Scan the file until all the attribtues are found.
            
          if(defined($crttime) && defined($endtime) && defined($opid) &&
             defined($pid)     && defined($host)    && defined($inputfile) &&
             defined($inst))
            {

             $rec{'crttime'}   = $crttime;
             $rec{'endtime'}   = $endtime;
             $rec{'opid'}      = $opid;
             $rec{'pid'}       = $pid;
             $rec{'host'}      = $host;
             $rec{'inst'}      = $inst;

             if($inputfile =~ /^(.*\/|.*\\)(.*)$/)
             {  
               my $dir = realpath($1);
               my $fn  = $2;
              
               $rec{'inputfile'} = $dir . "/" . $fn;
             }

             @row = ($newtime, $endtime, $opid, $pid, $host,$inst,$inputfile);
                   
             # add record to the fiels db
             asmcmdanlz_add_file_record(\%rec);
       
             close(INFH);
             next LINE;
           }
         }
         elsif($inputfile =~ /\.log$/)
         {
           # Not all the attribtues can be found from the log files
           # log files should not be scanned until the end

           if(defined($newtime) && defined($endtime))
           {
             $rec{'newtime'}   = $newtime;
             $rec{'endtime'}   = $endtime;

             if($inputfile =~ /^(.*\/|.*\\)(.*)/)
             #if($inputfile =~ /^(.*\/)(.*)$/)
             {
               my $dir = realpath($1);
               my $fn  = $2;
                 
               $rec{'inputfile'} = $dir . "/" . $fn;
             }
             
             #$rec{'inputfile'} = realpath($inputfile);

             @row = ($newtime, $endtime,'','','','',$inputfile);
                 
             # add record to the files db
             asmcmdanlz_add_file_record(\%rec);
                  
             close(INFH);
             next LINE;            
           }
        }
      }
      close(INFH);
    }
  }
}

########
# NAME
#   asmcmdanlz_mine
#
# DESCRIPTION
#   This routine checks searches for the events and corresponding entities
#   in the log files
#
# PARAMETERS
#   NONE
#
# RETURNS
#   NONE
########
sub asmcmdanlz_mine()
{
  my ($file,$line,@attr);
  my (%file_record, $filename);
  my ($key, $value);
  my $time;
  my $entity;
  my $found;
  my $pattern;

  my $len = scalar @files_db;

  while(%file_record = asmcmdanlz_fetch_file_db())  
  {
    $file = $file_record{'inputfile'};                 # get the file name
    $file =~ m/([^\/]*)$/;
    $filename = $1;

    # Start with alert logs
    if($filename =~ /^alert/)
    {
      if(open(FILE , $file))
      {
        #chomp;

        LOOP: while(<FILE>)
        {
          my (%row);                             # context for the events

          # The %row hash filled in the current function , the event callback
          # and the and the coresponding entity callback. It is passed as an 
          # arguement to both the callbacks.
 
          $line = $_;

          if($filename =~ /^alert_(.*)\.log$/)
          {
            $row{'inst'} = $1;
          }
          
          if(/$datepattern/i)
          {
            $time = $1;
            $time = asmcmdanlz_normalize($1);
          }
          
          # search for all the events
          foreach $key (keys(%eventhash))
          {
            $row{'time'} = $time;
            $row{'file'} = $file;
            $row{'type'} = $key;

            #if search function is define, call it.
            if(defined($eventhash{$key}{srch_func}))
            {
             $found = $eventhash{$key}{srch_func}->($line, \%row);
            }
            #else match with the reg exp for the event.
            elsif(defined($eventhash{$key}{regexp}))
            {
              $pattern = $eventhash{$key}{regexp};
              if(/$pattern/)
              {
                #record event.
                $found = 1;
              }
            }
           
            # Event is found. call the entity function
            if($found && defined($eventhash{$key}{entity}))
            {
              $entity = $eventhash{$key}{entity};
              # get the entity and call the appropriate callback
              $entityhash{$entity}->(\%row);
              $row{'trcfile'} = $trace_file;

              if(%row)
              {
                # add the record to the event db
                asmcmdanlz_add_event(\%row);
              }
              
              next LOOP;
            }
          } 
        }
        close(FILE);
      }
      
    }
  }
}

################ EVENT AND ENTITY SEARCH FUNCTIONS ####################

########
# NAME
#   asmcmdanlz_srch_ioerror
#
# DESCRIPTION
#   This routine searches for the occurence of I/O error
#   in the log files
#
# PARAMETERS
#   $attr -  partially filled row context
#
# RETURNS
#   1 if an i/o error is found. 0 otherwise
########
sub asmcmdanlz_srch_ioerror()
{
  my ($str, $attr) = @_;
  my ($found) = 0;
  my ($time, $currtime, $file, $line);

  $currtime = $attr->{'time'};
  $file     = $attr->{'file'};

  # store the trace file in the global var to avoid one more file 
  # traversal
  if($str =~ /^Errors in file (.*):$/)
  {
    $trace_file = $1;
  }

  if($_ =~ /ERROR: cache failed to .* group=\d+\(([^\s]*)\) .*: (\d*)\((.*)\)/)
  {
    $attr->{'dgname'}   = $1;
    $attr->{'dsknum'}   = $2;
    #dg is the entity here
    $attr->{'ent_name'} = $1;
    $found = 1;
  }
  # need a test case for this loop
  elsif($str =~ /^ORA-15025: could not open disk \"(.*)\"$/)
  {
    $attr->{'dskpath'} = $1;

    if(open(FL, $file))
    {
      while(<FL>)
      {
        if(/$datepattern/i)
        {
          $time = $1;
          $time = asmcmdanlz_normalize($1);
        }

        if(defined($time) && 
           (asmcmdanlz_compare_time($time, $currtime) == 0))
        {
          $line = $_;

          $found = 1;
    
        }
      }
    }
  }
  # Warning message for block repair 
  # WARNING: failed to read mirror side 1 of virtual extent 141 logical 
  # extent 0 of file 257 in group [1.2579871670] from disk RADEK_0000 
  else
  {
    my $pattern = "WARNING: failed to .* mirror side .* virtual extent " .
                  "([\\d]+) .* of file ([\\d]+) in group \\[([\\d]+).* " .
                  "disk ([^\\s]+)";
 
    if($str =~ /$pattern/)
    {
      $attr->{'dgnum'} = $3;
      asmcmdanlz_get_dg_attr($attr);

      $attr->{'dskname'} = $4;
      # The file number and extent have to be stored
      # as the entity to match 
      $attr->{'ent_name'} = $1 . "-" . $2;
      $found = 1;
    }
  }
  return $found;
}

########
# NAME
#   asmcmdanlz_srch_dskoffline
#
# DESCRIPTION
#   This routine searches for the occurence of disk offline event
#   in the log files
#
# PARAMETERS
#   $attr -  partially filled row context
#
# RETURNS
#   1 if an disk offlien event is found. 0 otherwise
########
sub asmcmdanlz_srch_dskoffline()
{
  my ($str, $attr) = @_;
  my ($found) = 0;
  my ($pattern);

  $pattern = "SUCCESS: disk number ([\\d]+) force dropped offline";
  # SUCCESS: disk number 2 force dropped offline

  if($str =~ /$pattern/)
  {
    $attr->{'dsknum'} = $1;
    $found = 1;
  }
  
  return $found;
}

########
# NAME
#   asmcmdanlz_srch_dgdismnt
#
# DESCRIPTION
#   This routine searches for the occurence of diskgroup dismount event
#   in the log files
#
# PARAMETERS
#   $attr -  partially filled row context
#
# RETURNS
#   1 if an dg dismount event is found. 0 otherwise
########
sub asmcmdanlz_srch_dgdismnt()
{
  my ($str, $attr) = @_;
  my ($found) = 0;

  if($str =~ /^SUCCESS: alter diskgroup (.*) dismount/)
  {
    $attr->{'dgname'} = $1;
    $attr->{'ent_name'} = $1;

    if($str =~ /ASM SERVER \*/)
    {
      $attr->{'server_drop'} = 1;
    }

    $found = 1;
  }  
  return $found;
}

########
# NAME
#   asmcmdanlz_srch_blkrp
#
# DESCRIPTION
#   This routine searches for the occurence of block repair
#   in the log files
#
# PARAMETERS
#   $attr -  partially filled row context
#
# RETURNS
#   1 if an block repair event is found. 0 otherwise
########

sub asmcmdanlz_srch_blkrp
{
  my ($str, $attr) = @_;

  if($str =~ /^SUCCESS: extent ([\d]+) of file ([\d]+) group ([\d]+) repaired/)
  {
    $attr->{'extnum'} = $1;
    $attr->{'filenum'} = $2;
    $attr->{'dgnum'} = $3;
    $attr->{'ent_name'} = $1 . "-" . $2;
  }
}

########
# NAME
#   asmcmdanlz_get_disk_attr
#
# DESCRIPTION
#   This routine obtains the attribtue of the disk on which 
#   an event is noticed
#
# PARAMETERS
#   $attr -  partially filled row context
#
# RETURNS
#   NONE
########
sub asmcmdanlz_get_disk_attr()
{
  my ($attr) = @_; 
  my ($pattern);
  my ($dsknum, $dskpath,$dgnum);
  my ($currfile);

  $dsknum = $attr->{'dsknum'};
  #dgnum  = $attr->{'dgnum'};

  if(defined($attr->{'dsknum'}) && !defined($attr->{'dskname'}))
  {
    $pattern = "cache opening disk $attr->{'dsknum'} of grp .*: (.*) path:(.*)";
    
    if(open(TFILE , $attr->{'file'}))
    {
      while(<TFILE>)
      {
        if(/$pattern/)
        {
          $attr->{'dskname'} = $1;
          $attr->{'dskpath'} = $2;
        }
      }
      close(TFILE);
    }
  }
  
  if(defined($attr->{'dskpath'}) && !defined($attr->{'dskname'}))
  {
    $pattern = "cache opening disk ([\\d]+) of grp .*: (.*) path:" . 
               "$attr->{'dskpath'}";
    
    if(open(TFILE , $attr->{'file'}))
    {
      while(<TFILE>)
      {
        if(/$pattern/)
        {
          $attr->{'dsknum'}  = $1;
          $attr->{'dskname'} = $2;
          $attr->{'ent_name'} = $2;
        }
      }
      close(TFILE);
    }
  }
  elsif(defined($attr->{'dskname'}) && !defined($attr->{'dskpath'}))
  {
    my $pattern = "cache opening disk ([\\d]+) of grp .*: $attr->{'dskname'}".
                  " path:(.*)" ;

    if(open(TFILE , $attr->{'file'}))
    {
      while(<TFILE>)
      {
        if(/$pattern/)
        {
          $attr->{'dskpath'} = $2;
        }
      }
    }
    close(TFILE);
  }

}

########
# NAME
#   asmcmdanlz_get_dg_attr
#
# DESCRIPTION
#   This routine obtains the attribtue of the diskgroup on which 
#   an event is noticed
#
# PARAMETERS
#   $attr -  partially filled row context
#
# RETURNS
#   NONE
########
sub asmcmdanlz_get_dg_attr()
{
  my ($attr) = @_;
  my ($pattern);

  if((!defined($attr->{'dgname'}) && !defined($attr->{'dgnum'})) ||
     (defined($attr->{'dgname'}) && defined($attr->{'dgnum'})))
  {
    return;
  }

  if(open(TFILE, $attr->{'file'})) 
  {
    while(<TFILE>)
    {
      if(defined($attr->{'dgnum'}) && !defined($attr->{'dgname'}))
      {
        $pattern = "NOTE: cache began mount .* group (.*) " .
                   "number=$attr->{'dgnum'}";
        
        if($_ =~ /$pattern/)
        {
          $attr->{'dgname'} = $1;

          close(TFILE);

          return;
        }
      }
      elsif(!defined($attr->{'dgnum'}) && defined($attr->{'dgname'}))
      {
        $pattern = "NOTE: cache began mount .* group $attr->{'dgname'}" .
                   "number=([\\d]+)";
        if($_ =~ /$pattern/)
        {
          $attr->{'dgnum'} = $1;

          close(TFILE);

          return;
        }
      }
    }
    close(TFILE);
  }
}


########
# NAME
#   asmcmdanlz_prepare
#
# DESCRIPTION
#   This routine goes thruogh all the events that are found to 
#   establish relationship among them.
#
# PARAMETERS
#   NONE
#
# RETURNS
#   NONE
########
sub asmcmdanlz_prepare()
{
  my ($key, @value);
  my (%min_col_wid);
  my ($tmp, $i, @keys, @res, @rows);
  my (%cur_row, $row,  $pattern, $type);
 
  asmcmdanlz_init_col_width(\%min_col_wid);

  while(%cur_row = asmcmdanlz_fetch_events())
  {
    my (%causes);
   
    if(@dgnames)
    {
      my $dg_matched = 0;

      if(defined($cur_row{'dgname'}))
      {
        for(my $i = 0 ; $i < @dgnames; $i++)
        {
          if($cur_row{'dgname'} =~ m/$dgnames[$i]/i)
          {
            $dg_matched = 1;
          }
        }
        
        if($dg_matched == 0)
        {
          #if the current DG is not given as a value for -G option, skip
          next;
        }
      }
    }

    if(defined($st_time))
    {
      if(asmcmdanlz_compare_time($cur_row{'time'}, $st_time) == -1)
      {
        next;
      }
    }

    if(defined($end_time))
    {
      if(asmcmdanlz_compare_time($cur_row{'time'}, $end_time) == 1)
      {
        next;
      }
    }

    if(@eventtypes)
    {
      if(defined($cur_row{'type'}))
      {
        my $evnt_matched = 0;

        for(my $i =0 ; $i < @eventtypes; $i++)
        {
          if($cur_row{'type'} =~ m/$eventtypes[$i]/i)
          {
            $evnt_matched = 1;
          }
       }
            
        if($evnt_matched == 0)
        {
          # if the current event is not specified skip it
          next;
        }
      }
    }

    $type = $cur_row{'type'};

    my $entity = $cur_row{'ent_name'};
    next unless defined($entity);

    #find out if the current event can be the effect of some other events
    foreach $tmp (keys(%dependencies))
    {
      @value = @{$dependencies{$tmp}};
      $pattern = "^" . $cur_row{'type'} . "\$";

      @res = grep(/$pattern/, @value);

      if(@res)
      {
        $causes{$tmp} = 1;
      }
    }

    #foreach $row (@found_events)
    for($i = 0; $i < @found_events; $i++)
    {
      $row = $found_events[$i];
      my %filedb_rec;

      if(($row->[0] eq $type) && ($row->[1] eq $cur_row{'ent_name'}))
      {
        #same event on same entity is reported again
      }
      
      #cause of this event is already recorded
      if(defined($causes{$row->[0]}) && ($row->[1] eq $cur_row{'ent_name'}))
      {
        my (%entry);
        
        asmcmdanlz_init_row_info(\%entry);
        
        my %causal_event = asmcmdanlz_fetch_event_by_indx($row->[2]);
        
        # Get the corresponding record from the files database
        if(defined($causal_event{'trcfile'}))
        {
          %filedb_rec = asmcmdanlz_fetch_filedb_filename($causal_event{'trcfile'});
        }
 
        if(defined($cur_row{'host'}))
        {
          $entry{'HOSTNAME'} = $cur_row{'host'};
        }
        elsif(defined($filedb_rec{'host'}))
        {
          $entry{'HOSTNAME'} = $filedb_rec{'host'};
        }
      
        if(defined($cur_row{'inst'}))
        {
          $entry{'INSTANCE'} = $cur_row{'inst'}
        }
        elsif(defined($filedb_rec{'inst'}))
        {
          $entry{'INSTANCE'} = $filedb_rec{'inst'};
        }

        
        if(defined($cur_row{'dgname'}))
        {
          $entry{'DG_NAME'} = $cur_row{'dgname'}
        }
        elsif(defined($causal_event{'dgname'}))
        {
          $entry{'DG_NAME'} = $causal_event{'dgname'};
        }

        $entry{'PID'} = $filedb_rec{'pid'}
              if(defined($filedb_rec{'pid'}));

        $entry{'EVENT'} = $row->[0]
              if(defined($row->[0]));

        $entry{'RESOLUTION'} = $cur_row{'type'}
              if(defined($cur_row{'type'}));

        $entry{'DISK_PATH'} = $causal_event{'dskpath'}
              if(defined($causal_event{'dskpath'}));

        if(defined($causal_event{'trcfile'}))
        {
          my $trc = $causal_event{'trcfile'};
          
          if($trc =~ m/([^\/]*)$/)
          {
            $trc = $1;
          }

          $entry{'TRACEFILE'} = $trc
               if(defined($trc));
        }

        push(@rows, \%entry);
        splice(@found_events, $i , 1);

      }
     # splice(@found_events, $i , 1);
    }


    # Record the current event in found event as it can be a cause events
    # in future
    my (@tmp_arr) ;     
    push(@tmp_arr, $type);
    push(@tmp_arr, $entity);
    push(@tmp_arr, $iterator-1);
    push(@found_events, \@tmp_arr);
 
  }

  if(@rows)
  {
    asmcmdshare_ls_calc_min_col_wid(\@rows, \%min_col_wid);

    my ($printf_format) = "\"" . 
      "%-$min_col_wid{'HOSTNAME'}" . "s " .
      "%-$min_col_wid{'INSTANCE'}" . "s " .
      "%-$min_col_wid{'DG_NAME'}" . "s " .
      "%-$min_col_wid{'PID'}" . "s " .
      "%-$min_col_wid{'EVENT'}" . "s " .
      "%-$min_col_wid{'RESOLUTION'}" . "s " .
      "%-$min_col_wid{'DISK_PATH'}" . "s " .
      "%-$min_col_wid{'TRACEFILE'}" ."s\n" .
      "\"";
  
     my ($print_stmt) = "printf $printf_format, (";
   
     $print_stmt .= '$ASMCMDANLZ_HOSTNAME,
                     $ASMCMDANLZ_INSTANCE,
                     $ASMCMDANLZ_DG_NAME,
                     $ASMCMDANLZ_PID,
                     $ASMCMDANLZ_EVENT,
                     $ASMCMDANLZ_RESOLUTION,
                     $ASMCMDANLZ_DISK_PATH,
                     $ASMCMDANLZ_TRACEFILE)';
 
    eval($print_stmt);
    warn $@ if $@;

    for($i = 0; $i < @rows; $i++)
    {
      $print_stmt = "printf $printf_format, ";
      
      $print_stmt .= q|($rows[$i]->{'HOSTNAME'},
                        $rows[$i]->{'INSTANCE'},
                        $rows[$i]->{'DG_NAME'},
                        $rows[$i]->{'PID'},
                        $rows[$i]->{'EVENT'},
                        $rows[$i]->{'RESOLUTION'},
                        $rows[$i]->{'DISK_PATH'},
                        $rows[$i]->{'TRACEFILE'})|;
      
      eval($print_stmt);
      warn $@ if $@; 
    }
  }
}

########
# NAME
#   asmcmdanlz_init_col_width
#
# DESCRIPTION
#   This routine initializes the width of the coloumns 
#    to be printed.
#
# PARAMETERS
#   min_col_wid_ref (IN) - Reference to the header hash.
#
# RETURNS
#   NONE
########
sub asmcmdanlz_init_col_width()
{
  my ($min_col_wid_ref) = shift;
  
  $min_col_wid_ref->{'HOSTNAME'}   = length($ASMCMDANLZ_HOSTNAME);
  $min_col_wid_ref->{'INSTANCE'}   = length($ASMCMDANLZ_INSTANCE);
  $min_col_wid_ref->{'DG_NAME'}    = length($ASMCMDANLZ_DG_NAME);
  $min_col_wid_ref->{'PID'}        = length($ASMCMDANLZ_PID);
  $min_col_wid_ref->{'EVENT'}      = length($ASMCMDANLZ_EVENT);
  $min_col_wid_ref->{'RESOLUTION'} = length($ASMCMDANLZ_RESOLUTION);
  $min_col_wid_ref->{'DISK_PATH'}  = length($ASMCMDANLZ_DISK_PATH);
  $min_col_wid_ref->{'TRACEFILE'}  = length($ASMCMDANLZ_TRACEFILE); 
}

########
# NAME
#  asmcmdanlz_init_row_info
#
# DESCRIPTION
#   This routine initializes the row structure
#
# PARAMETERS
#  row_info (IN/OUT) - reference to the row hash 
#
# RETURNS
#   The initialization is to prevent the undefined variables
#   warnings.
########
sub asmcmdanlz_init_row_info()
{
  my ($row_info) = shift;

  $row_info->{'HOSTNAME'}   = '';
  $row_info->{'INSTANCE'}   = '';
  $row_info->{'DG_NAME'}    = '';
  $row_info->{'PID'}        = '';
  $row_info->{'EVENT'}      = '';
  $row_info->{'RESOLUTION'} = '';
  $row_info->{'DISK_PATH'}  = '';
  $row_info->{'TRACEFILE'}  = '';
  
}

########################## DATABASE FUNCTIONS ##############################

########
# NAME
#   asmcmdanlz_add_file_record
#
# DESCRIPTION
#   This routine adds a record to the files db
#
# PARAMETERS
#   row -  record to be added
#
# RETURNS
#   NONE
# NOTES
#   The records are stored in the memory.
########
sub asmcmdanlz_add_file_record()
{
  my ($row) = shift;
  my ($name, $dir);
  my ($filesdb) = "$ENV{'ORACLE_HOME'}/dbs/anlz/filesdb";

  $filesdb =~ /([^\n^\r^\t]+)/;
  $filesdb = $1;

  if($row->{'inputfile'} =~ m/([^\/]*)$/)
  {
    $name = $1;
  }

  #Create anlz directory in dbs, if it does not exis
  if(!(-d "$ENV{'ORACLE_HOME'}/dbs/anlz"))
  {
    $dir = "$ENV{'ORACLE_HOME'}/dbs/anlz" ;

    $dir =~ /([^\n^\r^\t]+)/;
    $dir = $1;

    if(!mkdir($dir, 0755))
    {
      asmcmdshare_error_msg(8601, undef);
      asmcmdshare_trace(1, "$!", 'y', 'y');
    }
  }

  # don't duplicate the file entry for trace files 
  # filenames can be repeated for alert logs
  # Remove some alert logs.Need a better way

  my $file_present = 0;

  if(defined($filenames{$name}))
  {
    my @tmp_file_arr = @{$filenames{$name}};

    foreach (@tmp_file_arr)
    {
      my $path1 = $_;
  
      my $path2 = $row->{'inputfile'};
  
      if( $path1 =~ /\Q$path2/ || $path2 =~ /\Q$path1/)
      {
        $file_present = 1;

        last;
      }
    }

  }

  if($file_present == 0)
  {

    push(@files_db, $row);

    if(open(FLFILE,  ">> $filesdb")) #remember to make the file path generic
    {
      while(my($key, $value) = each(%$row))
      {
         print FLFILE "$key=$value ";
      }
      print FLFILE "\n";

      close(FLFILE);
    }

    push(@{$filenames{$name}} , $row->{'inputfile'});
  }
}

########
# NAME
#   asmcmdanlz_fetch_file_db
#
# DESCRIPTION
#   This routine fetches a row from the files database
#   and increments the iterator
#
# PARAMETERS
#   NONE 
#
# RETURNS
#   An event database row
########
sub asmcmdanlz_fetch_file_db()
{
  my ($row, %ret);
  my ($buf);
  my ($temp_iter) = 0;
  my (@fields, $val);
  my ($filesdb) = "$ENV{'ORACLE_HOME'}/dbs/anlz/filesdb";

  $filesdb =~ /([^\n^\r^\t]+)/;
  $filesdb = $1;

  my $len = scalar @files_db;

  if($files_iterator < $len)
  {
    #$row = $events[$iterator];
      
    if(open(FILE, $filesdb))
    {
      # seek to the $iterator position and read the line
      while(<FILE>)
      {
        last if($temp_iter > $files_iterator);

        if($temp_iter == $files_iterator)
        {
          @fields = split(" ", $_);
                  
          foreach $val (@fields)
          {
            if($val =~ /(.*)=(.*)/)
            {
              $ret{$1} = $2;
            }
          }
        }

        $temp_iter++;
      }
      close(FILE);
    }

    $files_iterator++;
  }

  return %ret; 
}

########
# NAME
#   asmcmdanlz_fetch_filedb_filename
#
# DESCRIPTION
#   Given a file name, the routine retrieves the corresponding record from 
#   from filedb.
#
# PARAMETERS
#   file (IN)  - file name
#
# RETURNS
#    A file db record
########
sub asmcmdanlz_fetch_filedb_filename()
{
  my ($file) = shift;
  my (%ret , $i, $row);
  my (@fields, $val);
  my ($filesdb) = "$ENV{'ORACLE_HOME'}/dbs/anlz/filesdb"; 

  $filesdb =~ /([^\n^\r^\t]+)/;
  $filesdb = $1;

  if(open(FLFILE, $filesdb))
  {
    while(<FLFILE>)
    {
      my (%tmp);
      @fields = split(" ", $_);

      foreach $val (@fields)
      {
        if($val =~ /(.*)=(.*)/)
        {
          $tmp{$1} = $2;
        }
      } 
     
      if($tmp{'inputfile'} eq $file)
      {
        %ret = %tmp;
      }
    }

    close(FLFILE);
  }

  #for($i = 0; $i < @files_db; $i++)
  #{
  #  $row = $files_db[$i];
  #  
  #  if($row->{'inputfile'} eq $file)
  #  {
  ##   $ret = $row;
  # }
  #}
  
  return(%ret);
}

########
# NAME
#   asmcmdanlz_add_event
#
# DESCRIPTION
#   This routine adds a found event to the event db
#
# PARAMETERS
#   $attr -  event context
#
# RETURNS
#   Stored in memory as of now
########
sub asmcmdanlz_add_event()
{
  my ($row) = shift;
  my (%hrow, $tmp1, $key, $i);
  my ($line, $dir);
  my (@fields, $val);
  my ($eventdb) = "$ENV{'ORACLE_HOME'}/dbs/anlz/eventdb";

  $eventdb =~ /([^\n^\r^\t]+)/;
  $eventdb = $1;

  #Create anlz directory in dbs, if it does not exis
  if(!(-d "$ENV{'ORACLE_HOME'}/dbs/anlz"))
  {
    $dir = "$ENV{'ORACLE_HOME'}/dbs/anlz" ;

    $dir =~ /([^\n^\r^\t]+)/;
    $dir = $1;

    if(!mkdir($dir, 0755))
    {
      asmcmdshare_error_msg(8601, undef);
    }
  }

  # check whether the same record exists in db already.
  if(open(EVFILE, $eventdb))
  {
    while(<EVFILE>)
    {
      my (%tmp);
      chomp;

      @fields = split("  ", $_);
      
      foreach $val (@fields)
      {
        if($val =~ /(.*)=(.*)/)
        {
          my ($k, $v) = ($1, $2);

          # trim the leading and trailing spaces
          $k =~ s/^\s+//;
          $k =~ s/\s+$//;

          $v =~ s/^\s+//;
          $v =~  s/\s+$//;

          $tmp{$k} = $v;
        }
      }
      
      # compare the two hashes to find out if the 
      # record already exists in the db.

      my $areident = 1;

      if(keys %$row == keys %tmp)
      {
        foreach $key (keys  %$row)
        {
          if(defined($tmp{$key}) && defined($row->{$key}))
          {
            #trim the leading and trailing spaces.
            my $str1 = $row->{$key};
            my $str2 = $tmp{$key};

            $str1 =~ s/^\s+//;
            $str1 =~ s/\s+$//;

            if(!($str1 eq $str2))
            {
              $areident = 0;
              last;
            }
          }
          else
          {
            $areident = 0;
            last;
          }
        }
      }
      else
      {
        $areident = 0;
      }

      if($areident == 1)
      {
        #found the same record in the db
        return;
      }
    }
  }

  close(EVFILE);

  if(open(EVFILE,  ">> $eventdb"))
  {
    while(my($key, $value) = each(%$row))
    {
      #avoid the undef variable warnings
      if(defined($key) && defined($value))
      {
        print EVFILE "$key=$value  ";
      }
    }
    print EVFILE "\n";

    close(EVFILE);
  }

  push(@events, $row);
}

########
# NAME
#   asmcmdanlz_fetch_events
#
# DESCRIPTION
#   This routine fetches a record from the events database
#   and increments the iterator
#
# PARAMETERS
#   NONE 
#
# RETURNS
#   An event database record
########
sub asmcmdanlz_fetch_events()
{
  my ($row, %ret);
  my ($key, $value);
  my ($temp_iter) = 0;
  my (@fields, $val);
  my ($eventdb) = "$ENV{'ORACLE_HOME'}/dbs/anlz/eventdb";
 
  $eventdb =~ /([^\n^\r^\t]+)/;
  $eventdb = $1;

  my $len = scalar @events;

  $row = $events[$iterator];
           
   if(open(FILE, $eventdb))
   {
     # seek to the $iterator position and read the line
     while(<FILE>)
     {
       last if($temp_iter > $iterator);

       if($temp_iter == $iterator)
       {
         @fields = split("  ", $_);
        
         foreach $val (@fields)
         {
           if($val =~ /(.*)=(.*)/)
           {
             my ($k, $v) = ($1, $2);

             # trim the leading and trailing spaces
             $k =~ s/^\s+//;
             $k =~ s/\s+$//;

             $v =~ s/^\s+//;
             $v =~  s/\s+$//;

             $ret{$k} = $v;
           }
         }
       }

       $temp_iter++;
     }
     close(FILE);
   }

   $iterator++;

  return %ret; 
}

########
# NAME
#   asmcmdanlz_fetch_event_by_indx
#
# DESCRIPTION
#   This routine fetches a record from the events database
#   at the location specified by index
#
# PARAMETERS
#   index(IN)   -    locaton of the record in events db
#
# RETURNS
#   An event database record
########

sub asmcmdanlz_fetch_event_by_indx()
{
  my ($indx) = shift;
  my ($temp_iter) = 0;
  my (%ret, @fields, $val);
  my ($eventdb) = "$ENV{'ORACLE_HOME'}/dbs/anlz/eventdb";
 
  $eventdb =~ /([^\n^\r^\t]+)/;
  $eventdb = $1;

  if(open(FILE, $eventdb))
  {
    # seek to the $iterator position and read the line
    while(<FILE>)
    {
      last if($temp_iter > $indx);

      if($temp_iter == $indx)
      {
        @fields = split(" ", $_);
              
        foreach $val (@fields)
        {
          if($val =~ /(.*)=(.*)/)
          {
            $ret{$1} = $2;
          }
        }
      }

      $temp_iter++;
    }
    close(FILE);
  }
  
  return %ret;
}

############################ TIME RELATED FUNCTIONS ##########################
  
########
# NAME
#   asmcmdanlz_compare_time
#
# DESCRIPTION
#   This routine adds a found event to the event db
#
# PARAMETERS
#   time1  (IN) - timestamp1 
#   time2  (IN) - timestamp2 
#
# RETURNS
#   0  if time1 == time2 
#   1  if time1 > time2
#  -1  if time1 < time2
########
sub asmcmdanlz_compare_time()
{
  my ($time1 , $time2 ) = @_;
  my ($etime1, $etime2);
  my ($ms1   , $ms2   );
  my ($ret);

  $time1 = asmcmdanlz_normalize($time1);
  $time2 = asmcmdanlz_normalize($time2);

  if($time1 =~ /([\d]+)-([\d]+)-([\d]+)\s([\d]+):([\d]+):([\d]+)/) 
  {
    $etime1 = timelocal($6,$5,$4,$1,$2-1,$3);
  }

  if($time2 =~ /([\d]+)-([\d]+)-([\d]+)\s([\d]+):([\d]+):([\d]+)/) 
  {
    $etime2 = timelocal($6,$5,$4,$1,$2-1,$3);
  }

  if($etime1 > $etime2)
  {
    $ret = 1;
  }
  elsif($etime2 > $etime1)
  {
    $ret = -1;
  }
  elsif($etime1 == $etime2)
  {
    # if any of the timestamps has milliseconds part, process accordingly
    if($time1 =~ /([\d]+)-([\d]+)-([\d]+)\s([\d]+):([\d]+):([\d]+)\.([\d]+)/)
    {
      $ms1 = $7;  
    }

    if($time2 =~ /([\d]+)-([\d]+)-([\d]+)\s([\d]+):([\d]+):([\d]+)\.([\d]+)/)
    {
      $ms2 = $7;
    }
      
    # If only of the two stamps has milliseconds, its greater than the other
    if(!defined($ms1) && defined($ms2))
    {
      $ret = -1;
    }
    elsif(defined($ms1) && !defined($ms2))
    {
      $ret = 1;
    }
    elsif(defined($ms1) && defined($ms2))
    {
      if($ms1 == $ms2)
      {
        $ret  = 0;
      }
      else
      {
        ($ms1 > $ms2) ? ($ret = 1) : ($ret = -1);
      }
    }
    else
    {
      # Both the stamps do not have  milliseconds part
      $ret = 0;
    }
  }
  
  return $ret;
}

########
# NAME
#   asmcmdanlz_normalize
#
# DESCRIPTION
#   This routine normalizes the passed time stamp to a standard time stamp 
#
# PARAMETERS
#   time (IN) - timestamp to be normalized
#
# RETURNS
#   Normalized timestamp
########
sub asmcmdanlz_normalize()
{
  my ($time)       = @_;
  my ($newtime)    = '';
  my ($mon);

  my $datepattern1 = '(\d\d\d\d)(-\d\d-)(\d\d)([\s-]\d\d:\d\d:\d\d(\s|\.\d+)?)';

  my $datepattern2 = '(\d\d-)(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)'.            
                     '(-\d\d\d\d|\d\d)(\s\d\d:)(\d\d:)(\d\d)(\s|:\d+|\.\d+)';

  my $datepattern3 = '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)' . 
                     '\s(\d\d)\s(\d\d:\d\d:\d\d(\s|\s.{3}\s))(\d\d\d\d)';

  my $datepattern4 = '(Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s' . 
                     '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)' . 
                     '\s+(\d+)\s(\d\d:\d\d:\d\d(\s|\s.{3}\s))(\d\d\d\d)';

  my $datepattern5 = '(\d?\d)-(\d?\d)-(\d\d\d\d)\s(\d\d):(\d\d):(\d\d)(\s|\.\d+)?';
  
  if($time =~ /$datepattern5/)
  {
    $newtime = $time;
  }
  elsif($time =~ /$datepattern2/)
  {
    $mon = asmcmd_mon_to_num($2);
    $newtime = $1 . $mon . $3 . $4 . $5 . $6;
  }

  elsif($time =~ /$datepattern1/)
  {
    $newtime = $3 . $2 . $1 . $4;
  }

  elsif($time =~ /$datepattern4/)
  {
    $mon = asmcmd_mon_to_num($2);

    my $dt = $4;
    my $day = $3;
    my $yr = $6;

    $dt =~ s/\s+$//;

    $newtime = $day . "-" . $mon . "-" . $yr . " " . $dt;
  }

  elsif($time =~ /$datepattern3/)
  {
    $mon = asmcmd_mon_to_num($1);
    $newtime = $2 . "-" . $mon . "-" . $4 . " " . $3;
  }



  else
  {
    $newtime = "ERROR";
  }    

  return $newtime;
}

########
# NAME
#   asmcmd_mon_to_num
#
# DESCRIPTION
#   This routine converts a month name to number
#
# PARAMETERS
#   text (IN) - month name
#
# RETURNS
#   The number of the month
########
sub asmcmd_mon_to_num()
{
  my ($text) = @_;
  my ($num) = "NULL";

  #print "mon_to_num for $text in progress..\n";
  if($text =~ m/^jan$/i)
  {
    $num = 1;
  }
  elsif($text =~ m/^feb$/i)
  {
    $num = 2;
  }
  elsif($text =~ m/^mar$/i)
  {
    $num = 3;
  }
  elsif($text =~ m/^apr$/i)
  {
    $num = 4;
  }
  elsif($text =~ m/^may$/i)
  { 
    $num = 5;
  }
  elsif($text =~ m/^jun$/i)
  {
    $num = 6;
  }
  elsif($text =~ m/^jul$/i)
  {
    $num = 7;
  }
  elsif($text =~ m/^aug$/i)
  {
    $num = 8;
  }
  elsif($text =~ m/^sep$/i)
  {
    $num = 9;
  }
  elsif($text =~ m/^oct$/i)
  {
    $num = 10;
  }
  elsif($text =~ m/^nov$/i)
  {
    $num = 11;
  }
  elsif($text =~ m/^dec$/i)
  {
    $num = 12;
  }
  else
  {
    $num = 0;
  }
  
  return $num;
}

########
# NAME
#   asmcmdanlz_is_no_instance_cmd
#
# DESCRIPTION
#   This routine determines if a command can run without an ASM instance.
#
# PARAMETERS
#   arg   (IN) - user-entered command name string.
#
# RETURNS
#   True if $arg is a command that can run without an ASM instance 
#   or does not exist, false otherwise.
#
# NOTES
#   NONE
########
sub asmcmdanlz_is_no_instance_cmd 
{
  my ($arg) = shift;

  return !defined ($asmcmdanlz_cmds{ $arg }) ||
    !defined ($asmcmdanlz_cmds{ $arg }{ no_instance });
}


########
# NAME
#   asmcmdanlz_parse_int_args
#
# DESCRIPTION
#   This routine parses the arguments for flag options for ASMCMDANLZ
#   internal commands.
#
# PARAMETERS
#   cmd      (IN)  - user-entered command name string.
#   args_ref (OUT) - hash of user-specified flag options for a command,
#                    populated by getopts().
#
# RETURNS
#   Zero on success; undefined on error.
#
# NOTES
#   $cmd must already be verified as a valid ASMCMDANLZ internal command.
########
sub asmcmdanlz_parse_int_args 
{
  my ($cmd, $args_ref) = @_;
  my ($key);
  my (@string);

  #build the list of options to parse using GetOptions
  if($asmcmdanlz_cmds{ $cmd }{ flags })
  {
    foreach $key(keys %{$asmcmdanlz_cmds{ $cmd }{ flags }})
    {
      push(@string, $key);
    }
  }

  # Use GetOptions() from the Getopt::Long package to parse arguments for
  # internal commands.  These arguments are stored in @ARGV.
  if (!GetOptions($args_ref,@string))
  {
    # Print correct command format if syntax error. #
    asmcmdshare_syntax_error($cmd);
    return undef;
  }
  return 0;
}


########
# NAME
#   asmcmdanlz_syntax_error
#
# DESCRIPTION
#   This function prints the correct syntax for a command to STDERR, used 
#   when there is a syntax error.  This function is responsible for 
#   only ASMCMDANLZ commands.
#
# PARAMETERS
#   cmd   (IN) - user-entered command name string.
#
# RETURNS
#   1 if the command belongs to this module; 0 if command not found.
#
# NOTES
#   These errors are user-errors and not internal errors.  They are of type
#   record, not signal.  
# 
#   N.B. Functions in this module can call this function directly, without
#   calling the asmcmdshare::asmcmdshare_syntax_error equivalent.  The
#   latter is used only by the asmcmdcore module.
########
sub asmcmdanlz_syntax_error 
{
  my ($cmd) = shift;
  my ($cmd_syntax);                               # Correct syntax for $cmd. #
  my ($succ) = 0;

  $cmd_syntax = asmcmdanlz_get_cmd_syntax($cmd);  # Get syntax for $cmd. #

  if (defined ($cmd_syntax))
    {
      print STDERR 'usage: ' . $cmd_syntax . "\n";
      print STDERR 'help:  help ' . $cmd . "\n";
      $succ = 1;
    }

  return $succ;
}


########
# NAME
#   asmcmdanlz_get_cmd_syntax
#
# DESCRIPTION
#   This routine returns the help syntax of the command specified by $cmd.
#
# PARAMETERS
#   cmd   (IN) - the name of the command of which we're looking up the 
#                syntax.
#
# RETURNS
#   The syntax for command $cmd; undefined if $cmd does not exist.
########
sub asmcmdanlz_get_cmd_syntax 
{
  my ($cmd) = shift;
  my (%cmd_syntax);     # Hash storing the syntax for each internal command. #

  $cmd_syntax{'analyze'} = 'analyze [-G diskgroup] [--node nodename] ' .
                           '[--events event] [--starttime time] '.
                           '[--endtime time] [--tracedirectory directory]'; 

  return $cmd_syntax{$cmd};
}

########
# NAME
#   asmcmdanlz_get_asmcmd_cmds
#
# DESCRIPTION
#   This routine constructs a string that contains a list of the names of all 
#   ASMCMD internal commands and returns this string.
#
# PARAMETERS
#   None.
#
# RETURNS
#   A string contain a list of the names of all ASMCMD internal commands.
#
# NOTES
#   Used by the help command and by the error command when the user enters
#   an invalid internal command.
#
#   IMPORTANT: the commands names must be preceded by eight (8) spaces of
#              indention!  This formatting is mandatory.
########
sub asmcmdanlz_get_asmcmd_cmds 
{
  return asmcmdshare_print_cmds(sort(keys %asmcmdanlz_cmds));
}
1;
