#!/usr/bin/perl
################################################################################
#
# sahpi_parser: Parse OpenHPI header file, SaHpi.h to print enum
#				information for an entity (format of SaHpi.h entries).
#
#
# Usage:
#	sahpi_parser [-dryrun] [-v] [-entity] entity_type
#		entity_type is the entity tag to look up.
#					entity_type may or may not have the
#					prepended "SAHPI_ENT".
#		-v		=> verbose (debug) output
#		-dryrun => Use a local copy of the header file
#	
#
#      -*- OpenSAF  -*-
#
# (C) Copyright 2008 The OpenSAF Foundation
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed
# under the GNU Lesser General Public License Version 2.1, February 1999.
# The complete license can be accessed from the following location:
# http://opensource.org/licenses/lgpl-license.php
# See the Copying file included with the OpenSAF distribution for full
# licensing terms.
#
# Author(s):
#           Hewlett-Packard Company
#
################################################################################


use strict "vars";
use vars qw($VERBOSE $DRYRUN %ENV $EntityInfo);

################################################################################
#                                  Libraries
################################################################################
use Data::Dumper;
use Getopt::Long;

################################################################################
#                            Global/Default Values
################################################################################
$VERBOSE = 0;
if (defined($ENV{DRYRUN})) {
	$DRYRUN=$ENV{DRYRUN};
} else {
	$DRYRUN = 0;
}
$EntityInfo = undef;
my $MyName = $0;
	$MyName =~ s:.*/::;
#my $Usage = "\nUsage:\t$MyName [-v] [-dryrun] <-entity entity_name | -path entity_path>
my $Usage = "\nUsage:\t$MyName [-v] [-dryrun] <-entity entity_name>
	Get the SaHpiEntityTypeT enum value for the specified entity.\n";

my $HpiHeaderFile = "/usr/include/openhpi/SaHpi.h";

################################################################################
#                                 Subroutines
################################################################################

#********************************************************************************
#	ParseArgs: Parse command line options
#	Input: @ARGV
#	Output: Data-hash pointer of the format
#			$input->{$param1} = param1_value;
#			$input->{$param2} = param2_value;
#				...
#			$input->{$paramN} = paramN_value;
#
#********************************************************************************
sub ParseArgs {

	my $input = {};
	my $help;
	  # Let unknown arguments pass through so that the
	  # user can call this w/o flags, i.e.,
	  #		 $MyName <entity>
	Getopt::Long::Configure( qw(pass_through) );	
	GetOptions(
		"help|?"	=> \$help,
		"verbose"	=> \$VERBOSE,	
		"dryrun"	=> \$DRYRUN,	
		"entity=s"	=> \$input->{entity},	
#		"path=s"	=> \$input->{path},	
#		"type=i"	=> \$input->{pathType},	
	);

	$input->{entity} = shift(@ARGV) if (@ARGV);
	

	if ($help) {
		print "$Usage\n";
		exit 0;
	}

	if ($DRYRUN) {
		$HpiHeaderFile = "SaHpi.h" if ( -s "SaHpi.h" );
	}


#	if ( !defined($input->{entity}) && ! defined($input->{path}) ) {
	if ( !defined($input->{entity}) ) {
		print STDERR "\nERROR: $MyName requires entity argument\n\n";
		exit(1);
	}
	
	# Now patch up entity information in case the user reqested
	#  information with input like, "SYSTEM_BLADE"
	if (defined($input->{entity})) {
		# Suppose the entity was input as "FPGA"
		my $ent = uc($input->{entity});
		if ($ent !~ /SAHPI_ENT_/) {
			$ent = "ENT_" . $ent if ($ent !~ /ENT_/);
			$ent = "SAHPI_" . $ent if ($ent !~ /SAHPI_/);
		}
		$input->{entity} = $ent;
	}

	print STDERR "\nLooking for entity mapping for $input->{entity}\n" if $VERBOSE;
	return $input;
}

#********************************************************************************
#	ReadHeaderInfo: Read the HPI header file to get entity information
#	Input: $headerFile = HPI header file to read
#	Output: Data-hash pointer of the format
#			$input->{$param1} = param1_value;
#			$input->{$param2} = param2_value;
#				...
#			$input->{$paramN} = paramN_value;
#
#********************************************************************************
sub ReadHeaderInfo
{
	my ($headerFile) = @_;
	  # Flags for parsing the file
	my $inCmtFlag = 0;
	my $enumVal;
	my $inEnum = 0;
	my $enumName;
	my $inEntitySection = 0;
	my $enumCounter = 0;
	my $lbDefine = {};

	my $targetEnum = "SaHpiEntityTypeT";
	print STDERR "\nReadHeaderInfo: Searching $headerFile for '$targetEnum'\n" if $VERBOSE;

	my $printed = 0;
	my $lineNo = 0;
	open(HEADER, "<$headerFile") or die "Couldn't open $headerFile";

    READ: while (<HEADER>) {
		$lineNo++;
		chomp;	
		s/\/\*[^*]+\*\///;  # This eliminates comments from a line
		s/\/\/.*$//;		# This removes //-style comments
		next if (/^\s*$/);	# Skip blank lines
		# Skip everything until we get to the section that describes
		#  entities.
		$inEntitySection = 1 if ( /Entity Types/i );
		next if (!$inEntitySection);
		if ($VERBOSE && !$printed) {
			print STDERR "\nMust be in entity section, line $lineNo\n";
			$printed++;
		}
		next if ($inCmtFlag && ($_ !~ /\*\//));		# Skip if in a comment and don't see the
													# end of the comment in the line.
		CASE: {
			/^\s*\/\*/ && do {
				# For lines that start a comment, like /****************
				print STDERR "\tFound C-style whole-line comment on $lineNo\n" if ($VERBOSE);
				# This is a comment line
				next CASE if (/\*\//);
				$inCmtFlag = 1;
				next CASE;
			};	

			/\*\// && do {
				# For lines that end a comment, like ****************/
				$inCmtFlag = 0;
				my $cleaned = $_;
				$cleaned =~ s:.*\*\/::;
				next if ($cleaned =~ /^\s*$/);
				print "\nWARNING: Info on line $lineNo after eliminating comment\n";
				next CASE;
			};	
				
			/#\s*define\s+(\S+)\s+\(.+\)\s*(\S+)/ && do {
				# line like  #define SAHPI_NO_MORE_ENTRIES (SaHpiEventLogEntryIdT)0xFFFFFFFE
				print STDERR "- Case '#define param (cast)val' on line $lineNo\n" if ($VERBOSE);
				my $param = $1;
				my $defn = $2;
				$param = hex($param) if ($param =~ /0x/i);
				$defn = hex($defn) if ($defn =~ /^0x/i);
				if ($defn =~ /\((\S+)\s*([-+])([0-9xA-Fa-f])\)/) {
					# line like,  #define SA_ERR_HPI_UNSUPPORTED_API	(SaErrorT)(SA_HPI_ERR_BASE - 2)
					my $param1 = $1;
					my $action = $2;
					my $param2 = $3;
					$param1 = hex($param1) if ($param1 =~ /0x/i);
					if (($param2 =~ /^\d+$/) || ($param2 =~ /^0x[0-9A-F]+/i)) {
						# param 2 is a number
						$param2 =~ s/LL//;
						$param2 = hex($param2) if ($param2 =~ /0x/i);
					}
					if ($lbDefine->{$param1}) {
						if ($action =~ /\+/) {
							$lbDefine->{$param} = $lbDefine->{$param1} + $param2;
						}
						elsif ($action =~ /-/) {
							$lbDefine->{$param} = $lbDefine->{$param1} - $param2;
						}
						print STDERR \
						  "Set lbDefine->{$param} = $lbDefine->{$param1} ($param1 + $param2) on line $lineNo\n"
						  if ($VERBOSE);
					}
					else {
						print "\nWARNING: Got a computed #define at line $lineNo, but didn't know what to do with it!\n";
						print "line: '$_'\n";
					}
				}
				else {
					# line is like #define SAHPI_EC_LIMIT	(SaHpiEventCategoryT)0x05
					$lbDefine->{$param} = $defn;
				}
				next CASE;
			};	
	
			/#\s*define\s+(\S+)\s+(\S+)$/ && do {
				# line like  #define SAHPI_ENT_SAFHPI_GROUP 0x10000
				print STDERR "- Case '#define param val' on line $lineNo\n" if ($VERBOSE);
				my $param = $1;
				my $defn = $2;
				$defn =~ s/LL//;
				$param = hex($param) if ($param =~ /0x/i);
				$defn = hex($defn) if ($defn =~ /^0x/i);
					# Consider lines like #define SAHPI_ENTRY_UNSPECIFIED SAHPI_FIRST_ENTRY
				$lbDefine->{$param} = (defined($lbDefine->{$defn})) ?
					$lbDefine->{$param} = $lbDefine->{$defn} : $lbDefine->{$param} = $defn;
				print STDERR "Set lbDefine->$param = $lbDefine->{$param} ($defn) on line $lineNo\n"
				  if ($VERBOSE);
				next CASE;
			};	
	
			/typedef enum/ && do {	
				# OK Now we're in an enum -- read it until we get to the end
				# and see whether or not it's the right one.
				print STDERR "\n--> Found start of enum on line $lineNo\n" if ($VERBOSE);
				$inEnum = 1;
				$enumVal = undef;
				next CASE;
			};	

			/\s*(SAHPI_ENT_\S+)\s*=\s*([^,\s]+)\s*([-+])\s*([0-9xXA-Fa-f]+),/ && do {
				# Line like SAHPI_ENT_OEM_SYSINT_SPECIFIC = SAHPI_ENT_IPMI_GROUP + 0xD0, 
				next if (!$inEnum);
				my $param = $1;
				my $val = $2;
				my $action = $3;
				my $offset = $4;
				$val = hex($val) if ($val =~ /0x/i);
				$offset = hex($offset) if ($offset =~ /0x/i);
				print STDERR "\tFound definition of $param with offset on line $lineNo\n" if ($VERBOSE);
				if (defined($lbDefine->{$val})) {
					if ($action =~ /\+/) {
						$enumVal->{$param} = $lbDefine->{$val} + $offset;
					}
					elsif ($action =~ /\-/) {
						$enumVal->{$param} = $lbDefine->{$val} - $offset;
					}
					print STDERR "   1. \$enumVal->{$param} = $enumVal->{$param}\n" if ($VERBOSE);
					next CASE;
				}
				elsif (defined($enumVal->{$val})) {
					if ($action =~ /\+/) {
						$enumVal->{$param} = $enumVal->{$val} + $offset;
					}
					elsif ($action =~ /\-/) {
						$enumVal->{$param} = $enumVal->{$val} - $offset;
					}
					print STDERR "   2. \$enumVal->{$param} = $enumVal->{$param}\n" if ($VERBOSE);
					next CASE;
				}
				else {
					$enumVal->{$param} = $val + $offset;
					if ($action =~ /\+/) {
						$enumVal->{$param} = $enumVal->{$val} + $offset;
					}
					elsif ($action =~ /\-/) {
						$enumVal->{$param} = $enumVal->{$val} - $offset;
					}
					print STDERR "   3. \$enumVal->{$param} = $enumVal->{$param}\n" if ($VERBOSE);
					next CASE;
				}
				next CASE;
			};

			/\s*(SAHPI_ENT_\S+)\s*=\s*([^,\s]+),/ && do {
				# Line like, SAHPI_ENT_RACK = SAHPI_ENT_SAFHPI_GROUP,
				next if (!$inEnum);
				my $param = $1;
				my $val = $2;
				$val = hex($val) if ($val =~ /^0x/i);
				print STDERR "\tFound definition of $param on line $lineNo\n" if ($VERBOSE);
				print STDERR "    defined vars are:\n" . Dumper($lbDefine) . "\n" if ($VERBOSE);
				if (defined($lbDefine->{$val})) {
					$enumVal->{$param} = $lbDefine->{$val};
					$enumCounter = $enumVal->{$param};
					print STDERR "   1. \$enumVal->{$param} = $enumVal->{$param}\n" if ($VERBOSE);
					next CASE;
				}
				elsif (defined($enumVal->{$val})) {
					$enumVal->{$param} = $lbDefine->{$val};
					$enumCounter = $enumVal->{$param};
					print STDERR "   2. \$enumVal->{$param} = $enumVal->{$param}\n" if ($VERBOSE);
					next CASE;
				}
				else {
					$enumVal->{$param} = $val;
					$enumCounter = $enumVal->{$param};
					print STDERR "   3. \$enumVal->{$param} = $enumVal->{$param}\n" if ($VERBOSE);
					next CASE;
				}
				next CASE;
			};

			/\s*(SAHPI_ENT_[^,\s]+)\s*,*/ && do {
				# The most obivous case: line like SAHPI_ENT_FAN,
				next if (!$inEnum);
				my $param = $1;
				print STDERR "\tFound $param in list on line $lineNo\n" if ($VERBOSE);
				$enumVal->{$param} = ++$enumCounter;
				print STDERR "   \$enumVal->{$param} = $enumVal->{$param}\n" if ($VERBOSE);
				next CASE;
			};

			# Syntax matching {
			/^\s*}\s+(\S+)\s*;/ && do {
				# End of the enum, line like } SaHpiEntityTypeT;
				next if (!$inEnum);
				print STDERR "Found closing brace on line $lineNo\n" if ($VERBOSE);
				$enumName = $1 if ($inEnum);
				$inEnum = 0;
				print STDERR "\n--> enum name is '$enumName'\n" if ($inEnum && $VERBOSE);
				last READ if ($enumName =~ /$targetEnum/);
				next CASE;
			};	

		} # End CASE
			
	}  # End reading file handle
	close(HEADER);

	return ($enumVal);
}	# End ReadHeaderInfo()

###
###  MAIN
###

# The exit value
my $exitVal = 0;

# Parse command-line options
my $info = ParseArgs();

# Get the entity information from the HPI header file
$EntityInfo = ReadHeaderInfo($HpiHeaderFile);
print Dumper($EntityInfo) if $VERBOSE;

my $entity = $info->{entity};
if (defined($EntityInfo->{$entity})) {
	print "$info->{entity} -> " if $VERBOSE;
	print "$EntityInfo->{$entity}\n";
}
else {
	print "ERROR: No info for entity '$entity'\n";
	$exitVal = 1;
}

exit ($exitVal)

# vim: tabstop=4
