#
# Copyright 2002-2003 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
#ident	"@(#)extract_defines	1.3	03/06/14 SMI"
#
# This script scans the exacct header files and extracts the names of any
# #defines that are to be exported by the Exacct modules.  All such #defines
# are written out as an array of structs to a file which is subsequently
# included into the module.  Parameters to this script are the name of the
# module to generate for, and the output file to use.
#

use warnings;
use strict;

# Forward declarations
sub default_typefn;
sub catalog_typefn;

#
# Map of module names to files and lists + patterns of macros to declare.
# Each entry in the hash is keyed by the module name, and the value of each
# entry is a list of actions, where each action is a (keyword, param) pair.
# The valid actions are:
#     typefn => <fn_ptr>
#         fn_ptr is a function which when given a constant name,
#         returns the type - see default_typefn and catalog_typefn.
#     declare => [ <constant>, ... ]
#         Add the passed list of constants.
#     scan => [ <file> <regular expression> ]
#         Scan the specified file in /usr/include
#         for #defines that match the passed RE.
#
our %ModMap = (
	Exacct => [
		typefn	=> \&default_typefn,
		declare	=> [ qw(P_PID P_TASKID P_PROJID) ],
		scan 	=> [ 'sys/exacct.h' =>
			     qr/(?:EW|EP|EXR)_\w+/ ],
	],
	Catalog => [
		typefn	=> \&catalog_typefn,
		scan 	=> [ 'sys/exacct_catalog.h' =>
			     qr/EX[TCD]_\w+/ ],
	],
	File => [
		typefn	=> \&default_typefn,
		# From exacct.h.
		declare	=> [ qw(EO_HEAD EO_TAIL EO_NO_VALID_HDR
			     EO_POSN_MSK EO_VALIDATE_MSK) ],
	],
	Object => [
		typefn	=> \&default_typefn,
		# From sys/exacct.h.
		declare	=> [ qw(EO_ERROR EO_NONE EO_ITEM EO_GROUP) ],
	],
);

#
# Constants may have a 'type' associated, currently only used by ::Catalog
# (see below).  For all other cases the type is 'other'.
#
sub default_typefn
{
	return('other');
}

#
# ::Catalog uses the 'type' field to determine whether a given constant is a
# type, a catalog or a data id.  This function works out what type of constant
# has been passed and returns the appropriate type.
#
sub catalog_typefn
{
	my ($define) = @_;
	if ($define =~ /_MASK$/) {
		return('other');
	} elsif ($define =~ /^EXT_/) {
		return('type');
	} elsif ($define =~ /^EXC_/) {
		return('catlg');
	} elsif ($define =~ /^EXD_/) {
		return('id');
	} else {
		return('other');
	}
}

#
# Process a C header file, looking for #defines of interest.  Candidates are
# saved in the $defines arrayref.  Note nested includes are not processed.
#
sub process_file
{
	my ($file, $filelist, $pattern, $typefn, $defines) = @_;
	my $fh;
	if ($_ = (grep(m{/$file$}, @$filelist))[0]) {
		open($fh, '<', $_) || die("Can't open $_: $!\n");
	} else {
		die("Can't find $file\n");
	}
	my $line;
	while (defined($line = <$fh>)) {
		if ($line =~ /#define\s+\b($pattern)\b/) {
			$defines->{$1} = &$typefn($1);
		}
	}
	close($fh);
}

#
# Main routine.
#

# Check arguments and open the output file.
die("Usage is extract_defines <module> <output file> <input files...>\n")
    unless (@ARGV >= 2);
my ($module, $outfile, @filelist) = @ARGV;
my $mm;
if (! defined($mm = $ModMap{$module})) {
	die("Don't know how to handle module $module\n")
}
my $out;
if (! open($out, ">$outfile")) {
	die("Can't open $outfile: $!\n");
}

# Perform the appropriate set of actions from ModMap for the specified module.
my $defines = {};
my $tfn = \&default_typefn;
my $i = 0;
while ($i < @$mm) {
	my $act = $$mm[$i++];
	my $parm = $$mm[$i++];
	if ($act eq 'typefn') {
		$tfn = $parm;
	} elsif ($act eq 'declare') {
		foreach my $d (@$parm) {
			$defines->{$d} = &$tfn($d);
		}
	} elsif ($act eq 'scan') {
		process_file($parm->[0], \@filelist, $parm->[1], $tfn,
		    $defines);
	} else {
		die("Illegal action $act\n");
	}
}

# Print the structure definition.
print $out ("static constval_t constants[] = {\n");
foreach my $def (sort(keys(%$defines))) {
	my $type = $defines->{$def};
	my $len = length($def);
	my $t = "\t" . "\t" x (3 - int(($len + 3) / 8));
	print $out ("\t\"$def\",$t$len,\t$type,\n\t    (unsigned int) $def,\n");
}
print $out ("\tNULL,\t\t\t\t0,\tother,\n\t    0,\n};\n");
close($out);
exit(0);
