Steve,

This is the first patch in the set to enable the cross compile generation of the look-up table headers.  This patch only add in the gen_tables.py script to augment the gen_tables.c file.  This script requires Python 2.7 and the Pyparsing library.  I have tested this using a x86 cross compile build and an x86 regular build.  I compared the generated header files and they were identical. I was not able to pull down the audit source due to firewall rules but I believe the patch should apply without any problems.  

This script analyzes the preprocessor output of building the gen_tables.c file with the appropriated header table to find the values for each variable.  I had to do some creative parsing to catch all of the different enumerations that the preprocessor does not handle.  I could not find a easier way to do this other than pulling in the Pyparsing library.  I would not consider myself a Python developer so there could be a better way to do this parsing.  The headers are then generated using the same logic as is in the gen_tables.c file.

Thanks,
Clayton

Clayton Shotwell
Software Engineer
clshotwe@rockwellcollins.com
www.rockwellcollins.com

diff -urN /dev/null b/lib/gen_tables.py
--- /dev/null        2013-06-19 11:25:31.230442052 -0500
+++ b/lib/gen_tables.py        2013-08-19 14:27:55.639872141 -0500
@@ -0,0 +1,458 @@
+#!/usr/bin/python
+################################################################################
+# Copyright 2013, Rockwell Collins.  All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+# Authors:
+#      Clayton Shotwell <clshotwe@rockwellcollins.com>
+#
+# Description:
+#      Generator of lookup tables to replace the gen_tables.c method developed
+#      Miloslav Trmac <mitr@redhat.com> to make audit package cross compilable.
+#      The logic in this script mimics the logic in gen_tables.c before the last
+#      modification.
+#
+# Usage: gen_tables.py [-h] [--i2s] [--i2s-transtab] [--s2i]
+#                             [--uppercase | --lowercase] [--duplicate-ints]
+#                             prefix header source output
+#
+#        Generate tables header files.
+#
+#        positional arguments:
+#          prefix            The prefix of the output file to use
+#          header            The header file to parse table values from
+#          source            The source of the preprocessor from the compiler
+#          output            The output header file
+#
+#        optional arguments:
+#          -h, --help        show this help message and exit
+#          --i2s             Generate i2s tables
+#          --i2s-transtab    Generate transtab tables
+#          --s2i             Generate s2i tables
+#          --uppercase       All characters are uppercase
+#          --lowercase       All characters are lowercase
+#          --duplicate-ints  Allow duplicate integers
+
+import argparse
+import ctypes
+import os
+import re
+import sys
+from operator import attrgetter
+from pyparsing import Group, Word, Suppress, alphas, alphanums, nums, cppStyleComment, \
+                Optional, ZeroOrMore
+
+# Number of entries to print per line
+NUM_ENTIRES_IN_LINE = 10
+
+# Global table entries variable that is used everywhere
+ENTRIES = []
+
+# The ratio of table size to number of non-empty elements allowed for a
+# "direct" s2i table; if the ratio would be bigger, bsearch tables are used
+# instead.
+#
+# 2 looks like a lot at a first glance, but the bsearch tables need twice as
+# much space per element, so with the ratio equal to 2 the direct table uses
+# no more memory and is faster.
+DIRECT_THRESHOLD = 2
+
+# Set to True to enable some debug output
+DEBUG = False
+
+class Entry:
+        def __init__(self, new_s, val):
+                self.st = new_s
+                self.val = val
+                self.offset = 0
+                self.orig_index = 0
+        
+        def set_position(self, offset):
+                self.offset = offset
+        
+        def set_orig_index(self, orig_index):
+                self.orig_index = orig_index
+        
+        def get_str(self):
+                return self.st
+        
+        def __repr__(self):
+                return "<Entry st=%s val=%s>" % (self.st, self.val)
+        
+        def __str__(self):
+                return "Entry of st=%s, val=%s, offset=%d, orig_index=%d" % \
+                                (self.st, self.val, self.offset, self.orig_index)
+
+def output_strings(prefix, outfile):
+        try:
+                # Calculate the position each entry will be in the string
+                index = 0
+                for i in range(len(ENTRIES)):
+                        ENTRIES[i].set_position(index)
+                        # Increment the index by the length of the name plus 1 for the null
+                        # character at the end.
+                        index += len(ENTRIES[i].get_str()) + 1
+                # Write out the strings
+                outfile.write("static const char %s_strings[] = \"" % prefix)
+                for i in range(len(ENTRIES)):
+                        if (i != 0) and (i % NUM_ENTIRES_IN_LINE == 0):
+                                outfile.write('"\n\t"')
+                        outfile.write(ENTRIES[i].get_str())
+                        if (i != (len(ENTRIES) - 1)):
+                                outfile.write('\\0')
+                outfile.write('";\n')
+        except:
+                # If an error is found, raise the exception so the main function can close
+                # and delete the outfile
+                exc_type, exc_obj, exc_tb = sys.exc_info()
+                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
+                print("Unexpected error in output_strings:", exc_type, fname, exc_tb.tb_lineno)
+                raise
+
+def output_s2i(prefix, outfile, uppercase, lowercase):
+        try:
+                # Check for duplicate values
+                for i in range(len(ENTRIES) - 1):
+                        assert (ENTRIES[i].get_str() <= ENTRIES[i + 1].get_str()), "Entries not in the correct order"
+                        if (ENTRIES[i].get_str() == ENTRIES[i + 1].get_str()):
+                                print("Duplicate value %s: %d, %d" % \
+                                                (ENTRIES[i].get_str(), ENTRIES[i].val, ENTRIES[i + 1].val))
+                                raise
+                
+                # Write out the index to value index values
+                outfile.write("static const unsigned %s_s2i_s[] = {" % prefix)
+                for i in range(len(ENTRIES)):
+                        if (i % NUM_ENTIRES_IN_LINE == 0):
+                                outfile.write('\n\t')
+                        outfile.write("%i," % ENTRIES[i].offset)
+                outfile.write('\n};\n')
+                
+                # Write out the string to value actual values
+                outfile.write("static const int %s_s2i_i[] = {" % prefix)
+                for i in range(len(ENTRIES)):
+                        if (i % NUM_ENTIRES_IN_LINE == 0):
+                                outfile.write('\n\t')
+                        outfile.write("%i," % ENTRIES[i].val)
+                outfile.write('\n};\n')
+                
+                # Verify the strings are all uppercase or lowercase depending on the arguments
+                # passed in
+                if uppercase:
+                        for i in range(len(ENTRIES)):
+                                assert (all(ord(c) < 128 for c in ENTRIES[i].get_str()) and \
+                                                ENTRIES[i].get_str().isupper()), "String %s is not uppercase" % ENTRIES[i].get_str()
+                if lowercase:
+                        for i in range(len(ENTRIES)):
+                                assert (all(ord(c) < 128 for c in ENTRIES[i].get_str()) and \
+                                                ENTRIES[i].get_str().islower()), "String %s is not lowercase" % ENTRIES[i].get_str()
+                if uppercase or lowercase:
+                        outfile.write("static int %s_s2i(const char *s, int *value) {\n" \
+                                        "\tsize_t len, i;\n" \
+                                        "\tlen = strlen(s);\n" \
+                                        "\t{ char copy[len + 1];\n" \
+                                        "\tfor (i = 0; i < len; i++) {\n" \
+                                        "\t\tchar c = s[i];\n" % prefix)
+                        if uppercase:
+                                outfile.write("\t\tcopy[i] = GT_ISLOWER(c) ? c - 'a' + 'A' : c;\n")
+                        else:
+                                outfile.write("\t\tcopy[i] = GT_ISUPPER(c) ? c - 'A' + 'a' : c;\n")
+                        outfile.write("\t}\n" \
+                                        "\tcopy[i] = 0;\n" \
+                                        "\treturn s2i__(%s_strings, %s_s2i_s, %s_s2i_i, %d, copy, value);\n" \
+                                        "\t}\n" \
+                                        "}\n" % (prefix, prefix, prefix, len(ENTRIES)))
+                else:
+                        outfile.write("static int %s_s2i(const char *s, int *value) {\n" \
+                                        "\treturn s2i__(%s_strings, %s_s2i_s, %s_s2i_i, %d, s, value);\n" \
+                                        "}\n" % (prefix, prefix, prefix, prefix, len(ENTRIES)))
+        except:
+                # If an error is found, raise the exception so the main function can close
+                # and delete the outfile
+                exc_type, exc_obj, exc_tb = sys.exc_info()
+                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
+                print("Unexpected error in output_s2i:", exc_type, fname, exc_tb.tb_lineno)
+                raise
+
+def output_i2s(prefix, outfile, allow_duplicate_ints):
+        try:
+                # Check for duplicate values
+                for i in range(len(ENTRIES) - 1):
+                        assert (ENTRIES[i].val <= ENTRIES[i + 1].val), "Entries not in the correct order"
+                        if (not allow_duplicate_ints) and (ENTRIES[i].val == ENTRIES[i + 1].val):
+                                print("Duplicate value %d: %s, %s" % (ENTRIES[i].val, ENTRIES[i].get_str(), \
+                                                ENTRIES[i + 1].get_str()))
+                                raise
+                
+                # Find all of the unique values
+                unique_entries = []
+                for i in range(len(ENTRIES)):
+                        # If the unique_entries is empty or the last unique_entries entry is different from the
+                        # entry being compared, append the entry
+                        if (len(unique_entries) == 0) or (unique_entries[-1].val != ENTRIES[i].val):
+                                unique_entries.append(ENTRIES[i])
+                
+                # Determine which mapping to use based on the treshold
+                max_val = unique_entries[-1].val
+                min_val = unique_entries[0].val
+                if ((float(max_val - min_val)/len(unique_entries)) <= DIRECT_THRESHOLD):
+                        outfile.write("static const unsigned %s_i2s_direct[] = {" % prefix)
+                        next_index = min_val
+                        i = 0
+                        while True:
+                                if (((next_index - min_val) % 10) == 0):
+                                        outfile.write("\n\t")
+                                while (unique_entries[i].val < next_index):
+                                        # This can happen if (allow_duplicate_ints)
+                                        i += 1
+                                if (unique_entries[i].val == next_index):
+                                        assert(unique_entries[i].offset <= sys.maxint)
+                                        outfile.write("%i," % unique_entries[i].offset)
+                                else:
+                                        outfile.write("-1u,")
+                                if (next_index == max_val):
+                                        break
+                                next_index += 1
+                        outfile.write("\n};\nstatic const char *%s_i2s(int v) {\n" \
+                                        "\treturn i2s_direct__(%s_strings, %s_i2s_direct, %d, %d, v);\n" \
+                                        "}\n" % (prefix, prefix, prefix, min_val, max_val))
+                else:
+                        outfile.write("static const int %s_i2s_i[] = {" % prefix)
+                        for i in range(len(unique_entries)):
+                                if (i % 10 == 0):
+                                        outfile.write("\n\t")
+                                outfile.write("%i," % unique_entries[i].val)
+                        outfile.write("\n};\nstatic const unsigned %s_i2s_s[] = {" % prefix)
+                        for i in range(len(unique_entries)):
+                                if (i % 10 == 0):
+                                        outfile.write("\n\t")
+                                assert(unique_entries[i].offset <= sys.maxint)
+                                outfile.write("%i," % unique_entries[i].offset)
+                        outfile.write("\n };\n static const char *%s_i2s(int v) {\n" \
+                                        "\treturn i2s_bsearch__(%s_strings, %s_i2s_i, %s_i2s_s, %u, v);\n" \
+                                        "}\n" % (prefix, prefix, prefix, prefix, len(unique_entries)))
+        except:
+                # If an error is found, raise the exception so the main function can close
+                # and delete the outfile
+                exc_type, exc_obj, exc_tb = sys.exc_info()
+                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
+                print("Unexpected error in output_i2s:", exc_type, fname, exc_tb.tb_lineno)
+                raise
+
+def output_i2s_transtab(prefix, outfile):
+        """
+                Output the string to integer mapping table as a transtab[].
+                values must be sorted in the desired order.
+        """
+        try:
+                outfile.write("static const struct transtab %s_table[] = {" % prefix)
+                for i in range(len(ENTRIES)):
+                        if (i % NUM_ENTIRES_IN_LINE == 0):
+                                outfile.write('\n\t')
+                        outfile.write("{%i,%u}," % (ENTRIES[i].val, ENTRIES[i].offset))
+                outfile.write("\n};\n#define %s_NUM_ENTRIES (sizeof(%s_table) / sizeof(*%s_table))\n" % \
+                                (prefix.upper(), prefix, prefix))
+        except:
+                # If an error is found, raise the exception so the main function can close
+                # and delete the outfile
+                exc_type, exc_obj, exc_tb = sys.exc_info()
+                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
+                print("Unexpected error in output_i2s_transtab:", exc_type, fname, exc_tb.tb_lineno)
+                raise
+
+def lookup_enum(look_str, buf):
+        try:
+                # Pull all of the enums out of the preprocessor output out only once
+                # to help speed up all of the lookups
+                if not hasattr(lookup_enum, "enums"):
+                        if DEBUG:
+                                print("Pulling out the enums from the preprocessor output")
+                        # Regex pattern to parse out the enums from the preprocessor output
+                        enum_regex = "enum.*?{(?P<s>.*?)}"
+                        lookup_enum.enums = re.findall(enum_regex, buf, flags=(re.M | re.S))
+                
+                # find which enum contains the string we are looking for
+                for i in range(len(lookup_enum.enums)):
+                        if look_str in lookup_enum.enums[i]:
+                                # Determine the value of the variable in the enum
+                                enum_string = "enum preproc { " + lookup_enum.enums[i] + " }"
+                                enum_string = "".join([line.strip() for line in enum_string])
+                                if DEBUG:
+                                        print("Found %s in %s" % (look_str, enum_string))
+                                
+                                identifier = Word(alphas, alphanums+'_')
+                                opt_value = Word(nums, nums+'x+<>/*')
+                                
+                                enum_value = Group(identifier('name') + Optional(Suppress('=') + opt_value('value')))
+                                enum_list = Group(enum_value + ZeroOrMore(Suppress(',') + enum_value))
+                                enum = Suppress('enum') + identifier('enum') + Suppress('{') + enum_list('list') + \
+                                                Suppress('}')
+                                enum.ignore(cppStyleComment)
+                                
+                                for item, start, stop in enum.scanString(enum_string):
+                                        temp = 0
+                                        for entry in item.list:
+                                                if DEBUG:
+                                                        print("Checking %s against %s" % (look_str, entry.name))
+                                                if entry.name == look_str:
+                                                        if entry.value != '':
+                                                                # Need to call eval becuase some enums have math in them
+                                                                try:
+                                                                        value = eval(entry.value)
+                                                                except:
+                                                                        print("Found invalid value %s" % entry.value)
+                                                        else:
+                                                                value = temp
+                                                        if DEBUG:
+                                                                print("Matched the enum name to value %d" % value)
+                                                        return value
+                                                temp += 1
+        except:
+                exc_type, exc_obj, exc_tb = sys.exc_info()
+                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
+                print("Unexpected error in output_i2s_transtab:", exc_type, fname, exc_tb.tb_lineno)
+        print("Unable to find enum value")
+        return None
+
+def evaluate_string(eval_str, buf):
+        if DEBUG:
+                print("Evaluating string %s" % eval_str)
+        
+        # Regex expression for pulling apart the values in the preprocessor output
+        eval_regex = "(?P<val>\w+)"
+        # Since the string can be anything, it must be parsed into individual parts
+        # and evaluated separately to find any enum values
+        matches = re.findall(eval_regex, eval_str)
+        if len(matches) <= 0:
+                print("Could not find any matches")
+        
+        local_s = eval_str
+        value = None
+        i = 0
+        for i in range(len(matches)):
+                try:
+                        # If the current item is abled to evaled, there is nothing to do
+                        val = eval(matches[i])
+                except:
+                        try:
+                                # Need to check to see if the last character is a "U" and remove it
+                                # if this does not except, a valid number was found
+                                if matches[i][-1] == 'U':
+                                        val = eval(matches[i][:-1])
+                                        local_s = local_s.replace(matches[i], "%d" % val)
+                                else:
+                                        # Need to do a enum look up for anything that doesnt translate into a number
+                                        val = lookup_enum(matches[i], buf)
+                                        if val is not None:
+                                                local_s = local_s.replace(matches[i], "%d" % val)
+                        except:
+                                # This case will be hit if the "U" removal fails
+                                val = lookup_enum(matches[i], buf)
+                                if val is not None:
+                                        local_s = local_s.replace(matches[i], "%d" % val)
+        try:
+                # This will fail if all of the enums were not found rather
+                # than handling the failues in the above steps
+                # Also, need to convert to a signed 32 bit int for the output value
+                value = ctypes.c_int32(eval(local_s)).value
+                if DEBUG:
+                        print("Found value %d for %s" % (value, matches[i]))
+        except:
+                print("Could not parse string %s" % local_s)
+        
+        # Verify the mess above resulted in a number being found
+        if value is None:
+                print("Failed to find value for %s" % eval_str)
+                raise
+        return value
+        
+def remove_output(outfile):
+        path = outfile.name
+        outfile.close()
+        os.remove(path)
+        sys.exit(1)
+
+def main():
+        
+        # Setup the argument parser and parse the arguments given
+        parser = argparse.ArgumentParser(description='Generate tables header files.')
+        parser.add_argument('--i2s', dest='gen_i2s', action='store_true',
+                        help='Generate i2s tables')
+        parser.add_argument('--i2s-transtab', dest='gen_i2s_transtab', action='store_true',
+                        help='Generate transtab tables')
+        parser.add_argument('--s2i', dest='gen_s2i', action='store_true',
+                        help='Generate s2i tables')
+        # Make sure uppercase and lowercase are mutually exclusive
+        group = parser.add_mutually_exclusive_group()
+        group.add_argument('--uppercase', dest='uppercase', action='store_true',
+                        help='All characters are uppercase')
+        group.add_argument('--lowercase', dest='lowercase', action='store_true',
+                        help='All characters are lowercase')
+        parser.add_argument('--duplicate-ints', dest='allow_duplicate_ints', action='store_true',
+                        help='Allow duplicate integers')
+        parser.add_argument('prefix', help='The prefix of the output file to use')
+        parser.add_argument('source', type=argparse.FileType('r'),
+                        help='The source of the preprocessor from the compiler')
+        parser.add_argument('output', type=argparse.FileType('w'),
+                        help='The output header file')
+        args = parser.parse_args()
+        
+        # Regex pattern to parse out the macro and string from the _S calls
+        source_regex = "{ \((?P<val>.*?)\), \(\"(?P<s>\S+)\"\), 0, 0 }"
+        
+        # First parse the header file for all of the preprocessor source that need to
+        # be looked up
+        buf = args.source.read()
+        matches = re.findall(source_regex, buf, flags=re.MULTILINE)
+        
+        # Check to make sure we have matches
+        if (len(matches) <= 0):
+                print("Failed to find valid source")
+                remove_output(args.output)
+                sys.exit(1)
+        
+        try:
+                # Create all of the entry structures
+                global ENTRIES
+                for i in range(len(matches)):
+                        ENTRIES.append(Entry(matches[i][1], evaluate_string(matches[i][0], buf)))
+                        ENTRIES[i].set_orig_index(i)
+                        if DEBUG:
+                                print(ENTRIES[i])
+                
+                # Sort the entries alphabetically
+                ENTRIES = sorted(ENTRIES, key=attrgetter('st'))
+                # Print out the output header
+                args.output.write("/* This is a generated file, see Makefile.am for its inputs. */\n")
+                output_strings(args.prefix, args.output)
+                if args.gen_s2i:
+                        output_s2i(args.prefix, args.output, args.uppercase, args.lowercase)
+                if args.gen_i2s:
+                        ENTRIES = sorted(ENTRIES, key=attrgetter('val'))
+                        output_i2s(args.prefix, args.output, args.allow_duplicate_ints)
+                if args.gen_i2s_transtab:
+                        ENTRIES = sorted(ENTRIES, key=attrgetter('orig_index'))
+                        output_i2s_transtab(args.prefix, args.output)
+        except:
+                # On an error, close and remove the file before returning an error
+                print("Failed to write the output file correctly")
+                exc_type, exc_obj, exc_tb = sys.exc_info()
+                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
+                print("Unexpected error:", exc_type, fname, exc_tb.tb_lineno)
+                remove_output(args.output)
+                sys.exit(1)
+
+if __name__ == '__main__':
+        main()