moved of_g to c_gen.of_g_legacy, introduced loxi_globals

as of_g includes loads of legacy data (and a representation that
is increasingly tailored to the c backend only), I've moved it to
c_gen.of_g. In its place, I have created loxi_globals, which gives
access to the list of versions and the IR.

Also loxi reorg: move command line processing to cmdline.py
diff --git a/c_gen/build_of_g.py b/c_gen/build_of_g.py
new file mode 100755
index 0000000..6e536fa
--- /dev/null
+++ b/c_gen/build_of_g.py
@@ -0,0 +1,559 @@
+# Copyright 2013, Big Switch Networks, Inc.
+#
+# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
+# the following special exception:
+#
+# LOXI Exception
+#
+# As a special exception to the terms of the EPL, you may distribute libraries
+# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
+# that copyright and licensing notices generated by LoxiGen are not altered or removed
+# from the LoxiGen Libraries and the notice provided below is (i) included in
+# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
+# documentation for the LoxiGen Libraries, if distributed in binary form.
+#
+# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
+#
+# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
+# a copy of the EPL at:
+#
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# EPL for the specific language governing permissions and limitations
+# under the EPL.
+
+import sys
+
+import re
+import string
+import os
+import glob
+import copy
+import collections
+import c_gen.of_g_legacy as of_g
+import c_gen.type_maps as type_maps
+import c_gen.loxi_utils_legacy as loxi_utils
+import loxi_globals
+import c_gen.identifiers as identifiers
+import pyparsing
+import loxi_front_end.parser as parser
+import c_gen.translation as translation
+import loxi_front_end.frontend as frontend
+from loxi_ir import *
+from generic_utils import *
+
+root_dir = os.path.dirname(os.path.realpath(__file__))
+
+versions = {}
+# TODO:  Put these in a class so they get documented
+
+## Dict indexed by version giving all info related to version
+#
+# This is local; after processing, the information is stored in
+# of_g variables.
+
+def add_class(wire_version, cls, members):
+    """
+    Process a class for the given version and update the unified
+    list of classes as needed.
+
+    @param wire_version The wire version for this class defn
+    @param cls The name of the class being added
+    @param members The list of members with offsets calculated
+    """
+    memid = 0
+
+    sig = loxi_utils.class_signature(members)
+    if cls in of_g.unified:
+        uc = of_g.unified[cls]
+        if wire_version in uc:
+            debug("Error adding %s to unified. Wire ver %d exists" %
+                  (cls, wire_version))
+            sys.exit(1)
+        uc[wire_version] = {}
+        # Check for a matching signature
+        for wver in uc:
+            if type(wver) != type(0): continue
+            if wver == wire_version: continue
+            if not "use_version" in uc[wver]:
+                if sig == loxi_utils.class_signature(uc[wver]["members"]):
+                    log("Matched %s, ver %d to ver %d" %
+                          (cls, wire_version, wver))
+                    # have a match with existing version
+                    uc[wire_version]["use_version"] = wver
+                    # What else to do?
+                    return
+    else:  # Haven't seen this entry before
+        log("Adding %s to unified list, ver %d" % (cls, wire_version))
+        of_g.unified[cls] = dict(union={})
+        uc = of_g.unified[cls]
+
+    # At this point, need to add members for this version
+    uc[wire_version] = dict(members = members)
+
+    # Per member processing:
+    #  Add to union list (I'm sure there's a better way)
+    #  Check if it's a list
+    union = uc["union"]
+    if not cls in of_g.ordered_members:
+        of_g.ordered_members[cls] = []
+    for member in members:
+        m_name = member["name"]
+        m_type = member["m_type"]
+        if m_name.find("pad") == 0:
+            continue
+        if m_name in union:
+            if not m_type == union[m_name]["m_type"]:
+                debug("ERROR:   CLASS: %s. VERSION %d. MEMBER: %s. TYPE: %s" %
+                      (cls, wire_version, m_name, m_type))
+                debug("    Type conflict adding member to unified set.")
+                debug("    Current union[%s]:" % m_name)
+                debug(union[m_name])
+                sys.exit(1)
+        else:
+            union[m_name] = dict(m_type=m_type, memid=memid)
+            memid += 1
+        if not m_name in of_g.ordered_members[cls]:
+            of_g.ordered_members[cls].append(m_name)
+
+def update_offset(cls, wire_version, name, offset, m_type):
+    """
+    Update (and return) the offset based on type.
+    @param cls The parent class
+    @param wire_version The wire version being processed
+    @param name The name of the data member
+    @param offset The current offset
+    @param m_type The type declaration being processed
+    @returns A pair (next_offset, len_update)  next_offset is the new offset
+    of the next object or -1 if this is a var-length object.  len_update
+    is the increment that should be added to the length.  Note that (for
+    of_match_v3) it is variable length, but it adds 8 bytes to the fixed
+    length of the object
+    If offset is already -1, do not update
+    Otherwise map to base type and count and update (if possible)
+    """
+    if offset < 0:    # Don't update offset once set to -1
+        return offset, 0
+
+    count, base_type = loxi_utils.type_dec_to_count_base(m_type)
+
+    len_update = 0
+    if base_type in of_g.of_mixed_types:
+        base_type = of_g.of_mixed_types[base_type][wire_version]
+
+    base_class = base_type[:-2]
+    if (base_class, wire_version) in of_g.is_fixed_length:
+        bytes = of_g.base_length[(base_class, wire_version)]
+    else:
+        if base_type == "of_match_v3_t":
+            # This is a special case: it has non-zero min length
+            # but is variable length
+            bytes = -1
+            len_update = 8
+        elif base_type in of_g.of_base_types:
+            bytes = of_g.of_base_types[base_type]["bytes"]
+        else:
+            print "UNKNOWN TYPE for %s %s: %s" % (cls, name, base_type)
+            log("UNKNOWN TYPE for %s %s: %s" % (cls, name, base_type))
+            bytes = -1
+
+    # If bytes
+    if bytes > 0:
+        len_update = count * bytes
+
+    if bytes == -1:
+        return -1, len_update
+
+    return offset + (count * bytes), len_update
+
+def calculate_offsets_and_lengths(ordered_classes, classes, wire_version):
+    """
+    Generate the offsets for fixed offset class members
+    Also calculate the class_sizes when possible.
+
+    @param classes The classes to process
+    @param wire_version The wire version for this set of classes
+
+    Updates global variables
+    """
+
+    lists = set()
+
+    # Generate offsets
+    for cls in ordered_classes:
+        fixed_offset = 0 # The last "good" offset seen
+        offset = 0
+        last_offset = 0
+        last_name = "-"
+        for member in classes[cls]:
+            m_type = member["m_type"]
+            name = member["name"]
+            if last_offset == -1:
+                if name == "pad":
+                    log("Skipping pad for special offset for %s" % cls)
+                else:
+                    log("SPECIAL OFS: Member %s (prev %s), class %s ver %d" %
+                          (name, last_name, cls, wire_version))
+                    if (((cls, name) in of_g.special_offsets) and
+                        (of_g.special_offsets[(cls, name)] != last_name)):
+                        debug("ERROR: special offset prev name changed")
+                        debug("  cls %s. name %s. version %d. was %s. now %s" %
+                              cls, name, wire_version,
+                              of_g.special_offsets[(cls, name)], last_name)
+                        sys.exit(1)
+                    of_g.special_offsets[(cls, name)] = last_name
+
+            member["offset"] = offset
+            if m_type.find("list(") == 0:
+                (list_name, base_type) = loxi_utils.list_name_extract(m_type)
+                lists.add(list_name)
+                member["m_type"] = list_name + "_t"
+                offset = -1
+            elif m_type.find("struct") == 0:
+                debug("ERROR found struct: %s.%s " % (cls, name))
+                sys.exit(1)
+            elif m_type == "octets":
+                log("offset gen skipping octets: %s.%s " % (cls, name))
+                offset = -1
+            else:
+                offset, len_update = update_offset(cls, wire_version, name,
+                                                  offset, m_type)
+                if offset != -1:
+                    fixed_offset = offset
+                else:
+                    fixed_offset += len_update
+                    log("offset is -1 for %s.%s version %d " %
+                        (cls, name, wire_version))
+            last_offset = offset
+            last_name = name
+        of_g.base_length[(cls, wire_version)] = fixed_offset
+        if (offset != -1):
+            of_g.is_fixed_length.add((cls, wire_version))
+    for list_type in lists:
+        classes[list_type] = []
+        of_g.ordered_classes[wire_version].append(list_type)
+        of_g.base_length[(list_type, wire_version)] = 0
+
+def order_and_assign_object_ids():
+    """
+    Order all classes and assign object ids to all classes.
+
+    This is done to promote a reasonable order of the objects, putting
+    messages first followed by non-messages.  No assumptions should be
+    made about the order, nor about contiguous numbering.  However, the
+    numbers should all be reasonably small allowing arrays indexed by
+    these enum values to be defined.
+    """
+
+    # Generate separate message and non-message ordered lists
+    for cls in of_g.unified:
+        if loxi_utils.class_is_message(cls):
+            of_g.ordered_messages.append(cls)
+        elif loxi_utils.class_is_list(cls):
+            of_g.ordered_list_objects.append(cls)
+        else:
+            of_g.ordered_non_messages.append(cls)
+
+    of_g.ordered_messages.sort()
+    of_g.ordered_pseudo_objects.sort()
+    of_g.ordered_non_messages.sort()
+    of_g.ordered_list_objects.sort()
+    of_g.standard_class_order.extend(of_g.ordered_messages)
+    of_g.standard_class_order.extend(of_g.ordered_non_messages)
+    of_g.standard_class_order.extend(of_g.ordered_list_objects)
+
+    # This includes pseudo classes for which most code is not generated
+    of_g.all_class_order.extend(of_g.ordered_messages)
+    of_g.all_class_order.extend(of_g.ordered_non_messages)
+    of_g.all_class_order.extend(of_g.ordered_list_objects)
+    of_g.all_class_order.extend(of_g.ordered_pseudo_objects)
+
+    # Assign object IDs
+    for cls in of_g.ordered_messages:
+        of_g.unified[cls]["object_id"] = of_g.object_id
+        of_g.object_id += 1
+    for cls in of_g.ordered_non_messages:
+        of_g.unified[cls]["object_id"] = of_g.object_id
+        of_g.object_id += 1
+    for cls in of_g.ordered_list_objects:
+        of_g.unified[cls]["object_id"] = of_g.object_id
+        of_g.object_id += 1
+    for cls in of_g.ordered_pseudo_objects:
+        of_g.unified[cls] = {}
+        of_g.unified[cls]["object_id"] = of_g.object_id
+        of_g.object_id += 1
+
+
+def initialize_versions():
+    """
+    Create an empty datastructure for each target version.
+    """
+
+    for version in loxi_globals.OFVersions.target_versions:
+        wire_version = version.wire_version
+        version_name = of_g.of_version_wire2name[wire_version]
+        of_g.wire_ver_map[wire_version] = version_name
+        versions[version_name] = dict(
+            version_name = version_name,
+            wire_version = wire_version,
+            classes = {})
+        of_g.ordered_classes[wire_version] = []
+
+    of_g.target_version_list = [ v.wire_version for v in loxi_globals.OFVersions.target_versions ]
+
+def build_ordered_classes():
+    """
+    Read in from files given on command line and update global state
+
+    @fixme Should select versions to support from command line
+    """
+
+    for version, protocol in loxi_globals.ir.items():
+        wire_version = version.wire_version
+        # Populate global state
+        version_name = of_g.of_version_wire2name[wire_version]
+
+        for ofclass in protocol.classes:
+            if ofclass.name in ("of_group_add", "of_group_modify", "of_group_delete"):
+                continue
+            of_g.ordered_classes[wire_version].append(ofclass.name)
+            legacy_members = []
+            pad_count = 0
+            for m in ofclass.members:
+                if type(m) == OFPadMember:
+                    m_name = 'pad%d' % pad_count
+                    if m_name == 'pad0': m_name = 'pad'
+                    legacy_members.append(dict(m_type='uint8_t[%d]' % m.length,
+                                               name=m_name))
+                    pad_count += 1
+                else:
+                    # HACK the C backend does not yet support of_oxm_t
+                    if m.oftype == 'of_oxm_t':
+                        m_type = 'of_octets_t'
+                    else:
+                        enum = find(lambda e: e.name == m.oftype, protocol.enums)
+                        if enum and "wire_type" in enum.params:
+                            m_type = enum.params["wire_type"]
+                        else:
+                            m_type = m.oftype
+                    legacy_members.append(dict(m_type=m_type, name=m.name))
+            versions[version_name]['classes'][ofclass.name] = legacy_members
+
+        for enum in protocol.enums:
+            for entry in enum.entries:
+                identifiers.add_identifier(
+                    translation.loxi_name(entry.name),
+                    entry.name, enum.name, entry.value, wire_version,
+                    of_g.identifiers, of_g.identifiers_by_group)
+
+def populate_type_maps():
+    """
+    Use the type members in the IR to fill out the legacy type_maps.
+    """
+
+    def split_inherited_cls(cls):
+        if cls == 'of_meter_band_stats': # HACK not a subtype of of_meter_band
+            return None, None
+        for parent in sorted(type_maps.inheritance_data.keys(), reverse=True):
+            if cls.startswith(parent):
+                return (parent, cls[len(parent)+1:])
+        return None, None
+
+    def find_experimenter(parent, cls):
+        for experimenter in sorted(of_g.experimenter_name_to_id.keys(), reverse=True):
+            prefix = parent + '_' + experimenter
+            if cls.startswith(prefix) and cls != prefix:
+                return experimenter
+        return None
+
+    def find_type_value(ofclass, m_name):
+        for m in ofclass.members:
+            if isinstance(m, OFTypeMember) and m.name == m_name:
+                return m.value
+        raise KeyError("ver=%d, cls=%s, m_name=%s" % (wire_version, cls, m_name))
+
+    # Most inheritance classes: actions, instructions, etc
+    for version, protocol in loxi_globals.ir.items():
+        wire_version = version.wire_version
+        for ofclass in protocol.classes:
+            cls = ofclass.name
+            parent, subcls = split_inherited_cls(cls)
+            if not (parent and subcls):
+                continue
+            if parent == 'of_oxm':
+                type_len = find_type_value(ofclass, 'type_len')
+                oxm_class = (type_len >> 16) & 0xffff
+                if oxm_class != 0x8000:
+                    # Do not include experimenter OXMs in the main table
+                    val = type_maps.invalid_type
+                else:
+                    val = (type_len >> 8) & 0xff
+            else:
+                val = find_type_value(ofclass, 'type')
+            type_maps.inheritance_data[parent][wire_version][subcls] = val
+
+            # Extensions (only actions for now)
+            experimenter = find_experimenter(parent, cls)
+            if parent == 'of_action' and experimenter:
+                val = find_type_value(ofclass, 'subtype')
+                type_maps.extension_action_subtype[wire_version][experimenter][cls] = val
+                if wire_version >= of_g.VERSION_1_3:
+                    cls2 = parent + "_id" + cls[len(parent):]
+                    type_maps.extension_action_id_subtype[wire_version][experimenter][cls2] = val
+
+    # Messages
+    for version, protocol in loxi_globals.ir.items():
+        wire_version = version.wire_version
+        for ofclass in protocol.classes:
+            cls = ofclass.name
+            # HACK (though this is what loxi_utils.class_is_message() does)
+            if not [x for x in ofclass.members if isinstance(x, OFDataMember) and x.name == 'xid']:
+                continue
+            if type_maps.class_is_virtual(cls):
+                continue
+            # HACK hide of_group subclasses from legacy c backend
+            if ofclass.name in ("of_group_add", "of_group_modify", "of_group_delete"):
+                continue
+            subcls = cls[3:]
+            val = find_type_value(ofclass, 'type')
+            if not val in type_maps.message_types[wire_version].values():
+                type_maps.message_types[wire_version][subcls] = val
+
+            # Extensions
+            experimenter = find_experimenter('of', cls)
+            if experimenter:
+                val = find_type_value(ofclass, 'subtype')
+                type_maps.extension_message_subtype[wire_version][experimenter][cls] = val
+
+    type_maps.generate_maps()
+
+def analyze_input():
+    """
+    Add information computed from the input, including offsets and
+    lengths of struct members and the set of list and action_id types.
+    """
+
+    # Generate header classes for inheritance parents
+    for wire_version, ordered_classes in of_g.ordered_classes.items():
+        classes = versions[of_g.of_version_wire2name[wire_version]]['classes']
+        for cls in ordered_classes:
+            if cls in type_maps.inheritance_map:
+                new_cls = cls + '_header'
+                of_g.ordered_classes[wire_version].append(new_cls)
+                classes[new_cls] = classes[cls]
+
+    # Generate action_id classes for OF 1.3
+    for wire_version, ordered_classes in of_g.ordered_classes.items():
+        if not wire_version in [of_g.VERSION_1_3]:
+            continue
+        classes = versions[of_g.of_version_wire2name[wire_version]]['classes']
+        for cls in ordered_classes:
+            if not loxi_utils.class_is_action(cls):
+                continue
+            action = cls[10:]
+            if action == '' or action == 'header':
+                continue
+            name = "of_action_id_" + action
+            members = classes["of_action"][:]
+            of_g.ordered_classes[wire_version].append(name)
+            if type_maps.action_id_is_extension(name, wire_version):
+                # Copy the base action classes thru subtype
+                members = classes["of_action_" + action][:4]
+            classes[name] = members
+
+    # @fixme If we support extended actions in OF 1.3, need to add IDs
+    # for them here
+
+    for wire_version in of_g.wire_ver_map.keys():
+        version_name = of_g.of_version_wire2name[wire_version]
+        calculate_offsets_and_lengths(
+            of_g.ordered_classes[wire_version],
+            versions[version_name]['classes'],
+            wire_version)
+
+def unify_input():
+    """
+    Create Unified View of Objects
+    """
+
+    global versions
+
+    # Add classes to unified in wire-format order so that it is easier
+    # to generate things later
+    keys = versions.keys()
+    keys.sort(reverse=True)
+    for version in keys:
+        wire_version = versions[version]["wire_version"]
+        classes = versions[version]["classes"]
+        for cls in of_g.ordered_classes[wire_version]:
+            add_class(wire_version, cls, classes[cls])
+
+
+def log_all_class_info():
+    """
+    Log the results of processing the input
+
+    Debug function
+    """
+
+    for cls in of_g.unified:
+        for v in of_g.unified[cls]:
+            if type(v) == type(0):
+                log("cls: %s. ver: %d. base len %d. %s" %
+                    (str(cls), v, of_g.base_length[(cls, v)],
+                     loxi_utils.class_is_var_len(cls,v) and "not fixed"
+                     or "fixed"))
+                if "use_version" in of_g.unified[cls][v]:
+                    log("cls %s: v %d mapped to %d" % (str(cls), v,
+                           of_g.unified[cls][v]["use_version"]))
+                if "members" in of_g.unified[cls][v]:
+                    for member in of_g.unified[cls][v]["members"]:
+                        log("   %-20s: type %-20s. offset %3d" %
+                            (member["name"], member["m_type"],
+                             member["offset"]))
+
+def generate_all_files():
+    """
+    Create the files for the language target
+    """
+    for (name, fn) in lang_module.targets.items():
+        path = of_g.options.install_dir + '/' + name
+        os.system("mkdir -p %s" % os.path.dirname(path))
+        with open(path, "w") as outfile:
+            fn(outfile, os.path.basename(name))
+        print("Wrote contents for " + name)
+
+if __name__ == '__main__':
+    of_g.loxigen_log_file = open("loxigen.log", "w")
+    of_g.loxigen_dbg_file = sys.stdout
+
+    of_g.process_commandline()
+    # @fixme Use command line params to select log
+
+    if not config_sanity_check():
+        debug("Config sanity check failed\n")
+        sys.exit(1)
+
+    # Import the language file
+    lang_file = "lang_%s" % of_g.options.lang
+    lang_module = __import__(lang_file)
+
+    # If list files, just list auto-gen files to stdout and exit
+    if of_g.options.list_files:
+        for name in lang_module.targets:
+            print of_g.options.install_dir + '/' + name
+        sys.exit(0)
+
+    log("\nGenerating files for target language %s\n" % of_g.options.lang)
+
+    initialize_versions()
+    read_input()
+    populate_type_maps()
+    analyze_input()
+    unify_input()
+    order_and_assign_object_ids()
+    log_all_class_info()
+    generate_all_files()
diff --git a/c_gen/c_code_gen.py b/c_gen/c_code_gen.py
index e35ee8d..1106f2d 100644
--- a/c_gen/c_code_gen.py
+++ b/c_gen/c_code_gen.py
@@ -31,7 +31,7 @@
 """
 
 import sys
-import of_g
+import c_gen.of_g_legacy as of_g
 import c_match
 from generic_utils import *
 from c_gen import flags, type_maps, c_type_maps
@@ -40,6 +40,7 @@
 
 import c_gen.identifiers as identifiers
 
+# 'property' is for queues. Could be trouble
 
 ################################################################
 #
diff --git a/c_gen/c_dump_gen.py b/c_gen/c_dump_gen.py
index 9d487e2..a0af14e 100644
--- a/c_gen/c_dump_gen.py
+++ b/c_gen/c_dump_gen.py
@@ -33,7 +33,7 @@
 """
 
 import sys
-import of_g
+import c_gen.of_g_legacy as of_g
 import c_gen.match as match
 import c_gen.flags as flags
 from generic_utils import *
diff --git a/c_gen/c_show_gen.py b/c_gen/c_show_gen.py
index fe3e3dc..fc3edb8 100644
--- a/c_gen/c_show_gen.py
+++ b/c_gen/c_show_gen.py
@@ -33,7 +33,7 @@
 """
 
 import sys
-import of_g
+import c_gen.of_g_legacy as of_g
 import c_gen.match as match
 import c_gen.flags as flags
 from generic_utils import *
diff --git a/c_gen/c_test_gen.py b/c_gen/c_test_gen.py
index c9c8245..bddc642 100644
--- a/c_gen/c_test_gen.py
+++ b/c_gen/c_test_gen.py
@@ -59,7 +59,7 @@
 """
 
 import sys
-import of_g
+import c_gen.of_g_legacy as of_g
 import c_gen.match as match
 import c_gen.flags as flags
 from generic_utils import *
diff --git a/c_gen/c_type_maps.py b/c_gen/c_type_maps.py
index 69dc804..acc2314 100644
--- a/c_gen/c_type_maps.py
+++ b/c_gen/c_type_maps.py
@@ -29,7 +29,7 @@
 # @brief C code generation for LOXI type related maps
 #
 
-import of_g
+import c_gen.of_g_legacy as of_g
 import sys
 from generic_utils import *
 import c_gen.type_maps as type_maps
diff --git a/c_gen/c_validator_gen.py b/c_gen/c_validator_gen.py
index e799918..126530a 100644
--- a/c_gen/c_validator_gen.py
+++ b/c_gen/c_validator_gen.py
@@ -33,7 +33,7 @@
 """
 
 import sys
-import of_g
+import c_gen.of_g_legacy as of_g
 import c_gen.match as match
 import c_gen.flags as flags
 from generic_utils import *
diff --git a/c_gen/of_g_legacy.py b/c_gen/of_g_legacy.py
new file mode 100644
index 0000000..adf5c2b
--- /dev/null
+++ b/c_gen/of_g_legacy.py
@@ -0,0 +1,446 @@
+# Copyright 2013, Big Switch Networks, Inc.
+#
+# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
+# the following special exception:
+#
+# LOXI Exception
+#
+# As a special exception to the terms of the EPL, you may distribute libraries
+# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
+# that copyright and licensing notices generated by LoxiGen are not altered or removed
+# from the LoxiGen Libraries and the notice provided below is (i) included in
+# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
+# documentation for the LoxiGen Libraries, if distributed in binary form.
+#
+# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
+#
+# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
+# a copy of the EPL at:
+#
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# EPL for the specific language governing permissions and limitations
+# under the EPL.
+
+##
+# @brief Global data structs for LOXI code generation
+#
+# @fixme This needs to be refactored and brought into the 21st century.
+#
+
+import sys
+# @fixme Replace with argparse
+
+################################################################
+#
+# Configuration global parameters
+#
+################################################################
+
+##
+# The map from wire protocol to enum identifier generated from input
+# This is built from the version-specific structs file info.
+# @fixme This should go away when the process structs file is updated
+wire_ver_map = {}
+
+##
+# The list of wire versions which are to be supported
+target_version_list = []
+
+##
+# The dictionary of config variables related to code
+#
+# @param gen_unified_fns  Boolean; Generate top level function definitions for
+# accessors which are independent of the version; the alternative is to only
+# use the function pointers in the class definitions.  These functions support
+# better inlining optimizations.
+#
+# @param gen_fn_ptrs Boolean; Generate the functions pointed to by pointer
+# in the class (struct) definitions; the alternative is to only use the
+# unified (use_) functions
+#
+# @param use_obj_id  Use object IDs in struct defns   CURRENTLY NOT SUPPORTED
+#
+# @param return_base_types For 'get' accessors, return values when possible.
+# Otherwise all values are returned thru a call by variable parameter
+#
+# @param use_static_inlines Generate low level accessors as static inline
+# and put them in header files rather than .c files.
+#
+# @param copy_semantics One of "read", "write" or "grow".  This defines the
+# way that buffer references are managed.  Currently on "read" is supported.
+#
+# @param encode_typedefs Use object and member IDs (rather than names)
+# when generating the names used for accessor function typedefs
+#
+# @param get_returns One of "error", "value", or "void";
+# CURRENTLY ONLY "error" IS SUPPORTED.  "error" means
+# all get operations return an error code.  "value" means return a base_type
+# value when possible or void if not.  "void" means always return void
+# and use a call-by-variable parameter
+#
+
+# @fixme These are still very C specific and should probably either
+# go into lang_c.py or be swallowed by command line option parsing
+code_gen_config = dict(
+    gen_unified_fns=True,
+#    gen_fn_ptrs=True,  # WARNING: Haven't tested with this in a while
+    gen_fn_ptrs=False,
+    use_obj_id=False,
+    use_static_inlines=False,
+    copy_semantics="read",  # Only read implemented: read, write, grow
+    encoded_typedefs=False,
+    get_returns="error",   # Only error implemented; error, value, void
+)
+
+## These members do not get normal accessors
+
+skip_members = ["version", "type", "length", "err_type", "stats_type", "len",
+                "type_len", "actions_len", "_command"]
+
+## Some OpenFlow string length constants
+#
+# These are a few length constants needed for array processing
+ofp_constants = dict(
+    OF_MAX_TABLE_NAME_LEN = 32,
+    OF_MAX_PORT_NAME_LEN  = 16,
+    OF_ETH_ALEN = 6,
+    OF_DESC_STR_LEN   = 256,
+    OF_SERIAL_NUM_LEN = 32
+)
+
+## List of mixed data types
+#
+# This is a list of data types which require special treatment
+# because the underlying datatype has changed between versions.
+# The main example is port which went from 16 to 32 bits.  We
+# define per-version accessors for these types and those are
+# used in place of the normal ones.
+#
+# The wire protocol number is used to identify versions.  For now,
+# the value is the name of the type to use for that version
+#
+# This is the map between the external type (like of_port_no_t)
+# which is used by customers of this code and the internal
+# datatypes (like uint16_t) that appear on the wire for a
+# particular version.
+#
+of_mixed_types = dict(
+    of_port_no_t = {
+        1: "uint16_t",
+        2: "uint32_t",
+        3: "uint32_t",
+        4: "uint32_t",
+        "short_name":"port_no"
+        },
+    of_port_desc_t = {
+        1: "of_port_desc_t",
+        2: "of_port_desc_t",
+        3: "of_port_desc_t",
+        4: "of_port_desc_t",
+        "short_name":"port_desc"
+        },
+    of_bsn_vport_t = {
+        1: "of_bsn_vport_t",
+        2: "of_bsn_vport_t",
+        3: "of_bsn_vport_t",
+        4: "of_bsn_vport_t",
+        "short_name":"bsn_vport"
+        },
+    of_fm_cmd_t = { # Flow mod command went from u16 to u8
+        1: "uint16_t",
+        2: "uint8_t",
+        3: "uint8_t",
+        4: "uint8_t",
+        "short_name":"fm_cmd"
+        },
+    of_wc_bmap_t = { # Wildcard bitmap
+        1: "uint32_t",
+        2: "uint32_t",
+        3: "uint64_t",
+        4: "uint64_t",
+        "short_name":"wc_bmap"
+        },
+    of_match_bmap_t = { # Match bitmap
+        1: "uint32_t",
+        2: "uint32_t",
+        3: "uint64_t",
+        4: "uint64_t",
+        "short_name":"match_bmap"
+        },
+    of_match_t = { # Match object
+        1: "of_match_v1_t",
+        2: "of_match_v2_t",
+        3: "of_match_v3_t",
+        4: "of_match_v3_t",  # Currently uses same match as 1.2 (v3).
+        "short_name":"match"
+        },
+)
+
+## Base data types
+#
+# The basic types; Value is a list: bytes, to_wire, from_wire
+# The accessors deal with endian, alignment and any other host/network
+# considerations.  These are common across all versions
+#
+# For get accessors, assume we memcpy from wire buf and then apply ntoh
+# For set accessors, assume we apply hton and then memcpy to wire buf
+#
+# to/from wire functions take a pointer to class and change in place
+of_base_types = dict(
+    char = dict(bytes=1, use_as_rv=1, short_name="char"),
+    uint8_t = dict(bytes=1, use_as_rv=1, short_name="u8"),
+    uint16_t = dict(bytes=2, to_w="u16_hton", from_w="u16_ntoh", use_as_rv=1,
+                    short_name="u16"),
+    uint32_t = dict(bytes=4, to_w="u32_hton", from_w="u32_ntoh", use_as_rv=1,
+                    short_name="u32"),
+    uint64_t = dict(bytes=8, to_w="u64_hton", from_w="u64_ntoh", use_as_rv=1,
+                    short_name="u64"),
+#    of_cookie_t = dict(bytes=8, to_w="u64_hton", from_w="u64_ntoh", use_as_rv=1#,
+#                    short_name="cookie"),
+#    of_counter_t = dict(bytes=8, to_w="u64_hton", from_w="u64_ntoh", use_as_rv=1,
+#                    short_name="counter"),
+    of_mac_addr_t = dict(bytes=6, short_name="mac"),
+    of_ipv4_t = dict(bytes=4, short_name="ipv4"),
+    of_ipv6_t = dict(bytes=16, short_name="ipv6"),
+    of_port_name_t = dict(bytes=ofp_constants["OF_MAX_PORT_NAME_LEN"],
+                          short_name="port_name"),
+    of_table_name_t = dict(bytes=ofp_constants["OF_MAX_TABLE_NAME_LEN"],
+                           short_name="tab_name"),
+    of_desc_str_t = dict(bytes=ofp_constants["OF_DESC_STR_LEN"],
+                         short_name="desc_str"),
+    of_serial_num_t = dict(bytes=ofp_constants["OF_SERIAL_NUM_LEN"],
+                           short_name="ser_num"),
+    of_match_v1_t = dict(bytes=40, to_w="match_v1_hton",
+                         from_w="match_v1_ntoh",
+                         short_name="match_v1"),
+    of_match_v2_t = dict(bytes=88, to_w="match_v2_hton",
+                         from_w="match_v2_ntoh",
+                         short_name="match_v2"),
+    of_match_v3_t = dict(bytes=-1, to_w="match_v3_hton",
+                         from_w="match_v3_ntoh",
+                         short_name="match_v3"),
+#    of_match_v4_t = dict(bytes=-1, to_w="match_v4_hton",
+#                         from_w="match_v4_ntoh",
+#                         short_name="match_v4"),
+    of_octets_t = dict(bytes=-1, short_name="octets"),
+    of_bitmap_128_t = dict(bytes=16, short_name="bitmap_128"),
+)
+
+of_scalar_types = ["char", "uint8_t", "uint16_t", "uint32_t", "uint64_t",
+                   "of_port_no_t", "of_fm_cmd_t", "of_wc_bmap_t",
+                   "of_match_bmap_t", "of_port_name_t", "of_table_name_t",
+                   "of_desc_str_t", "of_serial_num_t", "of_mac_addr_t",
+                   "of_ipv6_t", "of_ipv4_t", "of_bitmap_128_t"]
+
+base_object_members = """\
+    /* The control block for the underlying data buffer */
+    of_wire_object_t wire_object;
+    /* The LOCI type enum value of the object */
+    of_object_id_t object_id;
+
+    /*
+     * Objects need to track their "parent" so that updates to the
+     * object that affect its length can be pushed to the parent.
+     * Treat as private.
+     */
+    of_object_t *parent;
+
+    /*
+     * Not all objects have length and version on the wire so we keep
+     * them here.  NOTE: Infrastructure manages length and version.
+     * Treat length as private and version as read only.
+     */
+    int length;
+    of_version_t version;
+
+    /*
+     * Many objects have a length and/or type represented in the wire buffer
+     * These accessors get and set those value when present.  Treat as private.
+     */
+    of_wire_length_get_f wire_length_get;
+    of_wire_length_set_f wire_length_set;
+    of_wire_type_get_f wire_type_get;
+    of_wire_type_set_f wire_type_set;
+
+    of_object_track_info_t track_info;
+
+    /*
+     * Metadata available for applications.  Ensure 8-byte alignment, but
+     * that buffer is at least as large as requested.  This data is not used
+     * or inspected by LOCI.
+     */
+    uint64_t metadata[(OF_OBJECT_METADATA_BYTES + 7) / 8];
+"""
+
+##
+# LOXI identifiers
+#
+# Dict indexed by identifier name.  Each entry contains the information
+# as a DotDict with the following keys:
+# values: A dict indexed by wire version giving each verion's value or None
+# common: The common value to use for this identifier at the LOXI top level (TBD)
+# all_same: If True, all the values across all versions are the same
+# ofp_name: The original name for the identifier
+# ofp_group: The ofp enumerated type if defined
+
+identifiers = {}
+
+##
+# Identifiers by original group
+# Keys are the original group names.  Value is a list of LOXI identifiers
+
+identifiers_by_group = {}
+
+## Ordered list of class names
+# This is per-wire-version and is a list of the classes in the order
+# they appear in the file.  That is important because of the assumption
+# that data members are defined before they are included in a superclass.
+ordered_classes = {} # Indexed by wire version
+
+## Per class ordered list of member names
+ordered_members = {}
+
+## Ordered list of message classes
+ordered_messages = []
+
+## Ordered list of non message classes
+ordered_non_messages = []
+
+## The objects that need list support
+ordered_list_objects = []
+
+## Stats request/reply are pseudo objects
+ordered_pseudo_objects = []
+
+## Standard order is normally messages followed by non-messages
+standard_class_order = []
+
+## All classes in order, including psuedo classes for which most code
+# is not generated.
+all_class_order = []
+
+## Map from class, wire_version to size of fixed part of class
+base_length = {}
+
+## Map from class, wire_version to size of variable-offset, fixed length part of class
+extra_length = {
+    ("of_packet_in", 3): 2,
+    ("of_packet_in", 4): 2,
+}
+
+## Boolean indication of variable length, per class, wire_version,
+is_fixed_length = set()
+
+## The global object ID counter
+object_id = 1  # Reserve 0 for root object
+
+## The unified view of all classes.  See internal readme.
+unified = {}
+
+## Indicates data members with non-fixed start offsets
+# Indexed by (cls, version, member-name) and value is prev-member-name
+special_offsets = {}
+
+## Define Python variables with integer wire version values
+VERSION_1_0 = 1
+VERSION_1_1 = 2
+VERSION_1_2 = 3
+VERSION_1_3 = 4
+
+# Ignore version for some functions
+VERSION_ANY = -1
+
+## @var supported_wire_protos
+# The wire protocols this version of LoxiGen supports
+supported_wire_protos = set([1, 2, 3, 4])
+version_names = {1:"VERSION_1_0", 2:"VERSION_1_1", 3:"VERSION_1_2",
+                 4:"VERSION_1_3"}
+short_version_names = {1:"OF_1_0", 2:"OF_1_1", 3:"OF_1_2", 4:"OF_1_3"}
+param_version_names = {1:"1.0", 2:"1.1", 3:"1.2", 4:"1.3"}
+
+##
+# Maps and ranges related to versioning
+
+# For parameter version indications
+of_param_version_map = {
+    "1.0":VERSION_1_0,
+    "1.1":VERSION_1_1,
+    "1.2":VERSION_1_2,
+    "1.3":VERSION_1_3
+    }
+
+# For parameter version indications
+of_version_map = {
+    "1.0":VERSION_1_0,
+    "1.1":VERSION_1_1,
+    "1.2":VERSION_1_2,
+    "1.3":VERSION_1_3
+    }
+
+# The iteration object that gives the wire versions supported
+of_version_range = [VERSION_1_0, VERSION_1_1, VERSION_1_2, VERSION_1_3]
+of_version_max = VERSION_1_3
+
+
+of_version_name2wire = dict(
+    OF_VERSION_1_0=VERSION_1_0,
+    OF_VERSION_1_1=VERSION_1_1,
+    OF_VERSION_1_2=VERSION_1_2,
+    OF_VERSION_1_3=VERSION_1_3
+    )
+
+of_version_wire2name = {
+    VERSION_1_0:"OF_VERSION_1_0",
+    VERSION_1_1:"OF_VERSION_1_1",
+    VERSION_1_2:"OF_VERSION_1_2",
+    VERSION_1_3:"OF_VERSION_1_3"
+    }
+
+
+################################################################
+#
+# Experimenters, vendors, extensions
+#
+# Although the term "experimenter" is used for identifying
+# external extension definitions, we generally use the term
+# extension when refering to the messages or objects themselves.
+#
+# Conventions:
+#
+# Extension messages should start with of_<experimenter>_
+# Extension actions should start with of_<experimenter>_action_
+# Extension instructions should start with of_<experimenter>_instructions_
+#
+# Currently, the above conventions are not enforced; the mapping
+# is done brute force in type_maps.py
+#
+################################################################
+
+# The map of known experimenters to their experimenter IDs
+experimenter_name_to_id = dict(
+    bsn = 0x005c16c7,
+    nicira = 0x00002320,
+    openflow = 0x000026e1
+    )
+
+def experimenter_name_lookup(experimenter_id):
+    """
+    Map an experimenter ID to its LOXI recognized name string
+    """
+    for name, id in of_g.experimenter_name_to_id.items():
+        if id == experimenter_id:
+            return name
+    return None
+
+################################################################
+#
+# Debug
+#
+################################################################
+
+loxigen_dbg_file = sys.stdout
+loxigen_log_file = sys.stdout
diff --git a/c_gen/templates/locitest/test_validator.c b/c_gen/templates/locitest/test_validator.c
index eb3cbb6..a4b55eb 100644
--- a/c_gen/templates/locitest/test_validator.c
+++ b/c_gen/templates/locitest/test_validator.c
@@ -26,7 +26,7 @@
 :: # under the EPL.
 ::
 :: include('_copyright.c')
-:: import of_g
+:: import c_gen.of_g_legacy as of_g
 :: import c_gen.loxi_utils_legacy as loxi_utils
 :: from c_gen import type_maps