#-*-perl-*-
#
# $Id: parse_headers,v 27.1 2007/01/02 13:54:46 biersma Exp $
#
# (c) 1999-2007 Morgan Stanley Dean Witter and Co.
# See ..../src/LICENSE for terms of distribution.
#
# This code pulls in all of the #define definitions, and creates
# arrays for each type of constant.  This will be used in
# constants.c.PL to autogenerate the functions which expand the
# macros.
#

use English;
use Config;

opendir(INCLUDE,$include) ||
  die "Unable to opendir $include: $ERRNO\n";

foreach my $dirent ( readdir(INCLUDE) ) {
  next unless $dirent =~ /^cm.*\.h$/;
  next unless -f "$include/$dirent"; # Skip dangling symlinks
  push(@headers,"$include/$dirent");
}

closedir(INCLUDE);

#
# Add in the cmqcfce.h only if we haven't found it already.
#
my %headers = map { $_ => 1 } @headers;
unless ( $headers{"$include/cmqcfce.h"} ) {
    my $found = 0;
    foreach my $incdir ( qw(./include ../include ../../include ../../../include) ) {
	next unless -f "$incdir/cmqcfce.h";
	push(@headers,"$incdir/cmqcfce.h");
	$found = 1;
	last;
    }
    die "Unable to locate cmqcfce.h\n" unless $found;
}


#
# Handle 64-bit support.  If your platform is 64 bit but doesn't set
# "use64bitall", please get us the details.
#
my $use_64_bit = 0;
if( $Config{use64bitall} ) {
    $use_64_bit = 1;
}

foreach my $header ( @headers ) {

    #print "Searching $header\n";

    open(HEADER,$header) or die "Unable to open $header: $ERRNO\n";

    #
    # To support the 64-bit macros, we support very simple conditionals,
    # nested one deep.
    #
    my $conditional;		# Name (undef if not in confitional)
    my $cond_else;		# 0: if #if, 1: in #else

    while ( <HEADER> ) {

	s/^\s*//;
	chomp;

	if (m@^#if !defined.*\s+/\* File not yet included\?\s+\*/@ ||
	    m@^#endif\s+/\* End of header file \*/@
	   ) {
	    #print "Skip include guard: $_\n";
	    next;
	}

	#
	# Entering a conditional?
	#
	if (/^#if defined\((.*?)\)/) {
	    if (defined $conditional) {
		die "Cannot handle nested conditional in [$header]: while in [$conditional], found [$1]";
	    }
	    $conditional = $1;
	    $cond_else = 0;
	    #print "Entering [$conditional]\n";
	} elsif (/^#else/) {
	    unless (defined $conditional) {
		die "Have #else without conditional in [$header]";
	    }
	    #print "Entering #else $conditional\n";
	    $cond_else = 1;
	} elsif (/^#endif/) {
	    unless (defined $conditional) {
		die "Have #endif without conditional in [$header]";
	    }
	    #print "Leaving [$conditional]\n";
	    $conditional = undef;
	}

	#
	# Handle line continuation
	#
	while ( m:\\$: ) {
	    s/\\$//;
	    my $cont = <HEADER>;
	    $cont =~ s/^\s*//;
	    $_ .= $cont;
	    chomp;
	}

	next unless /\#define/;

	#
	# Strip trailing C comments (there are a few in the V2 header
	# files), and trailing white space.
	#
	s:\s+/\*.*\*/::;
	s/\s*$//;

	my ($key,$value) = (split(/\s+/,$_,3))[1,2];
	#print STDERR "Have key [$key] value [$value]\n";

        #
        # Skip the MQ_64_BIT constant (which has no value)
        #
        next if ($key eq 'MQ_64_BIT');

        #unless (defined $value) {
        #    print STDERR "XX: key [$key] leads to undefined value\n";
        #}

	#
	# If we're in a conditionial 64-bit block, we may have to
	# skip a macro.
	#
	if (defined $conditional && $conditional eq 'MQ_64_BIT') {
	    if ($cond_else == 0 && $use_64_bit == 0) {
		#print "Skip 64-bit macro value [$key] [$value]\n";
		next;
	    } elsif ($cond_else && $use_64_bit) {
		#print "Skip 32-bit macro value [$key] [$value]\n";
		next;
	    }
	}

	next if $key eq "MQENTRY";
	next if $key eq "MQPOINTER";

	#
	# Skip a bunch of stuff needed only by handicapped C
	# programmers (we, OTOH, have perl ;-)
	#
	next if $key =~ /_ARRAY$/;
	next if $key =~ /_INCLUDED$/;
	next if $key =~ /_A$/;

	#
	# Skip some bogus macros added to 5.1 that we ain't gonna add
	# the already overly bloated MQSeries namespace.
	#
	next if $key eq 'MQCHANNELEXIT';
	next if $key eq 'MQCHANNELAUTODEFEXIT';
	next if $key eq 'MQDATACONVEXIT';
	next if $key eq 'MQTRANSPORTEXIT';

	#
	# We have to be careful only to skip the definitions which are
	# for default structures.
	#
	next if ( $key =~ /_DEFAULT$/ && $value =~ /,/ );

	$value =~ s/^\(//;
	$value =~ s/\)$//;

	#
	# Special case: Look for indication that this host has a
	# version of MQSeries installed which support the MQRFH
	# structure.    This is required to build RulesFormat.xs
	#
	if ( $key eq "MQRFH_STRUC_ID" ) {
	    $::has_mqrfh = 1;
	}

	if ( $key eq "MQMD_VERSION_2" ) {
	    $::has_mqmd2 = 1;
	}

	#
	# Hex
	#
	if ( $value =~ /^0x/ ) {
	    $value =~ s/L$//;
	    $constant_hex{$key} = eval($value);
	}

	#
	# Numeric constants
	#
	# NOTE: Special case to handle the MQ_64_BIT conditionals.
        # This works because the ONLY parameters that are
	# affected by these conditionals are the _LENGTH parameters
	# for some of the structures.
	#
        # At this time, we assume you are not using the 64-bit client
        # on Solaris.  Patches to support this (preferably with
        # auto-detection in the Makefile) are welcome.
        #
	elsif ( $value =~ /L$/ || $value =~ /^-?\d+$/) {

	    $value =~ s/L$//;

	    if (not exists $constant_numeric{$key} || # First time
                (not defined $value && not defined $constant_numeric{$key}) &&
                (defined $value && defined $constant_numeric{$key} && $constant_numeric{$key} eq $value) # No change
               ) {
		$constant_numeric{$key} = $value;
	    } else {
                print STDERR "WARNING: Have two conflicting constant values for [$key], using first [$constant_numeric{$key}] not second [$value]\n";
            }

	}
	#
	# Null strings -- very special
	#
	elsif ( $value =~ /^\"\\0/ ) {
	    # Strip all of the double quotes, and give us just the \0's
	    $value =~ s/\"//g;
	    # Count the null characters (i.e. count everything, and
	    # divide by 2, 'cause this is a string like "\0\0\0\0")
	    $constant_null{$key} = length($value)/2;
	}
	#
	# Non-null strings
	#
	elsif ( $value =~ /^\"/ ) {
	    # Strip all of the double quotes, and give us just the contents
	    # NOTE: This will handle line wrapped stuff (embedded "")
	    $value =~ s/\"//g;
	    # 5.1 encodes a bunch of characters in hex for some wierd reason...
	    $value =~ s/\\x(\w{2})/chr(hex($1))/ge;
	    $constant_string{$key} = $value;
	}
	#
	# Single character string
	#
	elsif ( $value =~ /\'/ ) {
	    $value =~ s/\'//g;
	    $value =~ s/\\x(\w{2})/chr(hex($1))/ge;
	    $constant_char{$key} = $value;
	}
	#
	# Ignore the function macros in cmqbc.h
	#
	elsif ( $value =~ /^mq[A-Z]/ ) {
	    next;
	}
	#
	# Don't know how to parse....
	#
	else {
	    warn "Unrecognized value: '$key' => '$value'\n";
	}

	# Debugging....
	# s/\s*/\t/;
	# print "$_\n";

    }

    close(HEADER);

}

1;
