#!/usr/local/bin/python
# -*- mode:python -*-
# vim: filetype=python
# Largely python format, but is meant to be preprocessed by yapps2

# Imported modules
import string
import sys
import getopt
import re
import weakref
import os
import tempfile
import filecmp

# XXX DEBUG - REMOVE
import pdb

# PARSER_ONLY_KEYS (list of strings):
# attribute keys who's values are consumed by the parser, and aren't put
# into the generated c/h

KVP_KEY_GLOBAL = "global"
KVP_KEY_C_LIST_TYPE = "c_list_type"
KVP_KEY_NAME = "name"
KVP_KEY_LINK = "link"
KVP_KEY_EQUALS_FN = "equals_fn"
KVP_KEY_FMT = "fmt"
KVP_KEY_VERSION = "version"

PARSER_ONLY_KEYS = [
        KVP_KEY_GLOBAL,
        KVP_KEY_C_LIST_TYPE,
        KVP_KEY_NAME,
        KVP_KEY_LINK,
        KVP_KEY_EQUALS_FN,
        KVP_KEY_FMT,
        KVP_KEY_VERSION,
        ]

# PYTHON INTEGRATION:
# these are used to designate whether a ddir is converted into a python
# list or a python dictionary.  They had better match the enum in gconfig.h.
# If they don't match it not going to go well for you.

GCI_DIR_PYTYPE_UNKNOWN = "GCI_DIR_PYTYPE_UNKNOWN"
GCI_DIR_PYTYPE_LIST = "GCI_DIR_PYTYPE_LIST"
GCI_DIR_PYTYPE_DICT = "GCI_DIR_PYTYPE_DICT"

#
# Parser definition
# This portion of the file is in Yapps2 format
#


from string import *
import re
from yappsrt import *

class ConfigParserScanner(Scanner):
    patterns = [
        ('","', re.compile(',')),
        ('":"', re.compile(':')),
        ('"vdir"', re.compile('vdir')),
        ('"variant"', re.compile('variant')),
        ('"dir"', re.compile('dir')),
        ("'='", re.compile('=')),
        ("','", re.compile(',')),
        ('"="', re.compile('=')),
        ('";"', re.compile(';')),
        ('"}"', re.compile('}')),
        ('"{"', re.compile('{')),
        ('[ \r\t\n]+', re.compile('[ \r\t\n]+')),
        ('/\\*[^*]*(\\*[^/][^*]*)*\\*/', re.compile('/\\*[^*]*(\\*[^/][^*]*)*\\*/')),
        ('\\s*//[^\n]*', re.compile('\\s*//[^\n]*')),
        ('DIR_DECLARE', re.compile('%dir')),
        ('VARIANT_DECLARE', re.compile('%variant')),
        ('PATCH_DECLARE', re.compile('%patch')),
        ('ENUM_DECLARE', re.compile('%enum')),
        ('TYPECOPY_DECLARE', re.compile('%typecopy')),
        ('VERBATIM_DECLARE', re.compile('%verbatim')),
        ('PATCHPOINT_DECLARE', re.compile('%patch_point')),
        ('TREE_DECLARE', re.compile('%tree')),
        ('TYPE_DECLARE', re.compile('%type')),
        ('COPY_BLOCK', re.compile('(?s)%copy[ \\t]*[h|c|ch|hc]\\n.*?\\n%copyend')),
        ('DIR_TYPE', re.compile('dir')),
        ('VARIANT_TYPE', re.compile('variant')),
        ('LIST_TYPE', re.compile('list')),
        ('MAP_TYPE', re.compile('map')),
        ('ISI_SLIST_ENTRY', re.compile('SLIST_ENTRY')),
        ('STAR', re.compile('\\*')),
        ('OPEN_SQ', re.compile('\\[')),
        ('CLOSE_SQ', re.compile('\\]')),
        ('OPEN_PAREN', re.compile('\\(')),
        ('CLOSE_PAREN', re.compile('\\)')),
        ('TYPE_KEYS', re.compile('(auto|extern|register|static|const|volatile|struct|enum|union)')),
        ('TYPE_BASES', re.compile('(signed|unsigned|char|short|long|int|float|double|bool)')),
        ('IDENT', re.compile('[a-zA-Z_][a-zA-Z_0-9]*')),
        ('WORD', re.compile('[a-zA-Z_0-9-]+')),
        ('INTEGER', re.compile('[0-9]+')),
        ('WHITE_SPACE_SLURP', re.compile('[ \r\t\n]*')),
        ('PARAMETER_VALUE_LINE', re.compile('[^";\n][^;\n]*')),
        ('QUOTED_STRING', re.compile('(?s)"(\\\\.|[^\\\\])*?"')),
        ('ANYTHING_LINE', re.compile('[^\n]*')),
        ('ANYTHING_PAREN', re.compile('[^\\)]*')),
        ('KEYVAL_KEY_RAW', re.compile('\\w([ \\t]*\\w)*')),
        ('KEYVAL_VALUE_RAW', re.compile('\\w([ \\t]*\\w)*')),
        ('ENUM_VALUE', re.compile('[^,}]*')),
        ('ENUM_DESC', re.compile("[a-zA-Z_0-9\\-.,']+")),
        ('END', re.compile('$')),
    ]
    def __init__(self, str):
        Scanner.__init__(self,None,['[ \r\t\n]+', '/\\*[^*]*(\\*[^/][^*]*)*\\*/', '\\s*//[^\n]*'],str)

class ConfigParser(Parser):
    def config(self):
        config = Config()
        while self._peek('END', 'DIR_DECLARE', 'ENUM_DECLARE', 'TYPECOPY_DECLARE', 'COPY_BLOCK', 'VARIANT_DECLARE', 'PATCH_DECLARE', 'TREE_DECLARE', 'TYPE_DECLARE') != 'END':
            _token_ = self._peek('DIR_DECLARE', 'ENUM_DECLARE', 'TYPECOPY_DECLARE', 'COPY_BLOCK', 'VARIANT_DECLARE', 'PATCH_DECLARE', 'TREE_DECLARE', 'TYPE_DECLARE')
            if _token_ == 'DIR_DECLARE':
                dir = self.dir(config)
            elif _token_ == 'ENUM_DECLARE':
                enum = self.enum(config)
            elif _token_ == 'TYPECOPY_DECLARE':
                typecopy = self.typecopy(config)
            elif _token_ == 'COPY_BLOCK':
                copy = self.copy(config)
            elif _token_ == 'VARIANT_DECLARE':
                variant = self.variant(config)
            elif _token_ == 'PATCH_DECLARE':
                patch = self.patch(config)
            else: # in ['TREE_DECLARE', 'TYPE_DECLARE']
                tree_or_type = self.tree_or_type(config)
        END = self._scan('END')
        config.fix_and_validate(); return config

    def dir(self, config):
        DIR_DECLARE = self._scan('DIR_DECLARE')
        dir = Dir()
        if self._peek('IDENT', '"{"') == '"{"':
            keyvalpairs = self.keyvalpairs()
            dir.setKvp(keyvalpairs)
        IDENT = self._scan('IDENT')
        dir.setName(IDENT)
        config.addDir(dir)
        self._scan('"{"')
        while self._peek('"}"', 'VERBATIM_DECLARE', 'LIST_TYPE', 'MAP_TYPE', '"dir"', '"variant"', '"vdir"', 'TYPE_KEYS', 'TYPE_BASES', 'IDENT', 'ISI_SLIST_ENTRY') != '"}"':
            _token_ = self._peek('VERBATIM_DECLARE', 'LIST_TYPE', 'MAP_TYPE', '"dir"', '"variant"', '"vdir"', 'TYPE_KEYS', 'TYPE_BASES', 'IDENT', 'ISI_SLIST_ENTRY')
            if _token_ == 'VERBATIM_DECLARE':
                verbatim_parameter = self.verbatim_parameter()
                dir.addVar(verbatim_parameter)
            elif _token_ != 'ISI_SLIST_ENTRY':
                dv_parameter = self.dv_parameter()
                dir.addVar(dv_parameter)
            else: # == 'ISI_SLIST_ENTRY'
                link_parameter = self.link_parameter(dir)
                dir.addVar(link_parameter)
        self._scan('"}"')
        if self._peek('";"', 'END', 'DIR_DECLARE', 'ENUM_DECLARE', 'TYPECOPY_DECLARE', 'COPY_BLOCK', 'VARIANT_DECLARE', 'PATCH_DECLARE', 'TREE_DECLARE', 'TYPE_DECLARE') == '";"':
            self._scan('";"')

    def variant(self, config):
        VARIANT_DECLARE = self._scan('VARIANT_DECLARE')
        variant = Variant()
        if self._peek('IDENT', '"{"') == '"{"':
            keyvalpairs = self.keyvalpairs()
            variant.setKvp(keyvalpairs)
        IDENT = self._scan('IDENT')
        variant.setName(IDENT)
        config.addDir(variant)
        self._scan('"{"')
        while self._peek('"}"', 'VERBATIM_DECLARE', 'LIST_TYPE', 'MAP_TYPE', '"dir"', '"variant"', '"vdir"', 'TYPE_KEYS', 'TYPE_BASES', 'IDENT') != '"}"':
            _token_ = self._peek('VERBATIM_DECLARE', 'LIST_TYPE', 'MAP_TYPE', '"dir"', '"variant"', '"vdir"', 'TYPE_KEYS', 'TYPE_BASES', 'IDENT')
            if _token_ == 'VERBATIM_DECLARE':
                verbatim_parameter = self.verbatim_parameter()
                variant.addVar(verbatim_parameter)
            else: 
                dv_parameter = self.dv_parameter()
                variant.addVar(dv_parameter)
        self._scan('"}"')
        if self._peek('";"', 'END', 'DIR_DECLARE', 'ENUM_DECLARE', 'TYPECOPY_DECLARE', 'COPY_BLOCK', 'VARIANT_DECLARE', 'PATCH_DECLARE', 'TREE_DECLARE', 'TYPE_DECLARE') == '";"':
            self._scan('";"')

    def patch(self, config):
        PATCH_DECLARE = self._scan('PATCH_DECLARE')
        patch = Patch()
        if self._peek('IDENT', '"{"') == '"{"':
            keyvalpairs = self.keyvalpairs()
            patch.setKvp(keyvalpairs)
        IDENT = self._scan('IDENT')
        patch.setTree(IDENT)
        IDENT = self._scan('IDENT')
        patch.setDir(IDENT)
        config.addPatch(patch)
        self._scan('"{"')
        while self._peek('"}"', 'LIST_TYPE', 'MAP_TYPE', '"dir"', '"variant"', '"vdir"', 'TYPE_KEYS', 'TYPE_BASES', 'IDENT') != '"}"':
            dv_parameter = self.dv_parameter()
            patch.addVar(dv_parameter)
        self._scan('"}"')
        if self._peek('";"', 'END', 'DIR_DECLARE', 'ENUM_DECLARE', 'TYPECOPY_DECLARE', 'COPY_BLOCK', 'VARIANT_DECLARE', 'PATCH_DECLARE', 'TREE_DECLARE', 'TYPE_DECLARE') == '";"':
            self._scan('";"')

    def tree_or_type(self, config):
        _token_ = self._peek('TREE_DECLARE', 'TYPE_DECLARE')
        if _token_ == 'TREE_DECLARE':
            TREE_DECLARE = self._scan('TREE_DECLARE')
            item = Tree()
            IDENT = self._scan('IDENT')
            item.setName(IDENT)
        else: # == 'TYPE_DECLARE'
            TYPE_DECLARE = self._scan('TYPE_DECLARE')
            item = Type()
            c_type = self.c_type()
            item.setName(c_type)
        config.addItem(item)
        self._scan('"{"')
        while self._peek('IDENT', '"}"') == 'IDENT':
            IDENT = self._scan('IDENT')
            option = IDENT
            self._scan('"="')
            parameter_value = self.parameter_value()
            item.addOption(option , parameter_value)
            self._scan('";"')
        self._scan('"}"')
        item.validate()
        if self._peek('";"', 'END', 'DIR_DECLARE', 'ENUM_DECLARE', 'TYPECOPY_DECLARE', 'COPY_BLOCK', 'VARIANT_DECLARE', 'PATCH_DECLARE', 'TREE_DECLARE', 'TYPE_DECLARE') == '";"':
            self._scan('";"')

    def enum(self, config):
        ENUM_DECLARE = self._scan('ENUM_DECLARE')
        enum = Enum()
        if self._peek('IDENT', '"{"') == '"{"':
            keyvalpairs = self.keyvalpairs()
            enum.setKvp(keyvalpairs)
        IDENT = self._scan('IDENT')
        enum.setName(IDENT)
        self._scan('"{"')
        enum_entry = self.enum_entry()
        enum.addEntry(enum_entry)
        while self._peek("','", '"}"') == "','":
            self._scan("','")
            enum_entry = self.enum_entry()
            enum.addEntry(enum_entry)
        self._scan('"}"')
        if self._peek('";"', 'END', 'DIR_DECLARE', 'ENUM_DECLARE', 'TYPECOPY_DECLARE', 'COPY_BLOCK', 'VARIANT_DECLARE', 'PATCH_DECLARE', 'TREE_DECLARE', 'TYPE_DECLARE') == '";"':
            self._scan('";"')
        config.addEnum(enum)

    def enum_entry(self):
        IDENT = self._scan('IDENT')
        entry = EnumEntry(IDENT)
        if self._peek('OPEN_PAREN', "'='", "','", '"}"') == 'OPEN_PAREN':
            OPEN_PAREN = self._scan('OPEN_PAREN')
            ANYTHING_PAREN = self._scan('ANYTHING_PAREN')
            entry.setDesc(ANYTHING_PAREN)
            CLOSE_PAREN = self._scan('CLOSE_PAREN')
        if self._peek("'='", "','", '"}"') == "'='":
            self._scan("'='")
            ENUM_VALUE = self._scan('ENUM_VALUE')
            entry.setValue(ENUM_VALUE)
        return entry

    def typecopy(self, config):
        TYPECOPY_DECLARE = self._scan('TYPECOPY_DECLARE')
        typecopy = Typecopy()
        c_type = self.c_type()
        typecopy.setIntrospectType(c_type)
        c_type = self.c_type()
        typecopy.setStructType(c_type)
        if self._peek('";"', 'END', 'DIR_DECLARE', 'ENUM_DECLARE', 'TYPECOPY_DECLARE', 'COPY_BLOCK', 'VARIANT_DECLARE', 'PATCH_DECLARE', 'TREE_DECLARE', 'TYPE_DECLARE') == '";"':
            self._scan('";"')
        config.addTypecopy(typecopy)

    def copy(self, config):
        tcopy = Copy()
        config.addCopy(tcopy)
        COPY_BLOCK = self._scan('COPY_BLOCK')
        tcopy.parseCopyBlock(COPY_BLOCK)

    def ident_seq(self):
        seq = []
        while 1:
            IDENT = self._scan('IDENT')
            seq.append(IDENT)
            if 0: break
        return seq

    def valid_type(self, param):
        _token_ = self._peek('"dir"', '"variant"', '"vdir"', 'TYPE_KEYS', 'TYPE_BASES', 'IDENT')
        if _token_ not in ['TYPE_KEYS', 'TYPE_BASES', 'IDENT']:
            compound_type = self.compound_type(param)
            type = compound_type
        else: # in ['TYPE_KEYS', 'TYPE_BASES', 'IDENT']
            c_type = self.c_type()
            type = c_type
        return type

    def compound_type(self, param):
        _token_ = self._peek('"dir"', '"variant"', '"vdir"')
        if _token_ == '"dir"':
            self._scan('"dir"')
            param.setIsDir()
        elif _token_ == '"variant"':
            self._scan('"variant"')
            param.setIsVariant()
        else: # == '"vdir"'
            self._scan('"vdir"')
            param.setIsVirtualDir()
            OPEN_PAREN = self._scan('OPEN_PAREN')
            QUOTED_STRING = self._scan('QUOTED_STRING')
            param.setVirtualLibrary(QUOTED_STRING)
            CLOSE_PAREN = self._scan('CLOSE_PAREN')
        IDENT = self._scan('IDENT')
        return IDENT

    def c_type_base(self):
        seq = []
        while self._peek('TYPE_KEYS', 'TYPE_BASES', 'IDENT') == 'TYPE_KEYS':
            TYPE_KEYS = self._scan('TYPE_KEYS')
            seq.append(TYPE_KEYS)
        _token_ = self._peek('TYPE_BASES', 'IDENT')
        if _token_ == 'TYPE_BASES':
            while 1:
                TYPE_BASES = self._scan('TYPE_BASES')
                seq.append(TYPE_BASES)
                if self._peek('TYPE_BASES', 'STAR', '";"', 'TYPE_KEYS', 'IDENT', '"{"', 'END', 'DIR_DECLARE', 'ENUM_DECLARE', 'TYPECOPY_DECLARE', 'COPY_BLOCK', 'VARIANT_DECLARE', 'PATCH_DECLARE', 'TREE_DECLARE', 'TYPE_DECLARE') != 'TYPE_BASES': break
        else: # == 'IDENT'
            IDENT = self._scan('IDENT')
            seq.append(IDENT)
        return string.join(seq)

    def c_type(self):
        c_type_base = self.c_type_base()
        type = c_type_base
        while self._peek('STAR', '";"', 'TYPE_KEYS', 'TYPE_BASES', 'IDENT', '"{"', 'END', 'DIR_DECLARE', 'ENUM_DECLARE', 'TYPECOPY_DECLARE', 'COPY_BLOCK', 'VARIANT_DECLARE', 'PATCH_DECLARE', 'TREE_DECLARE', 'TYPE_DECLARE') == 'STAR':
            STAR = self._scan('STAR')
            type = type + "*"
        return type

    def link_parameter(self, dir):
        link = DDirLinkParameter()
        link_entry = self.link_entry()
        link.setLinkType(link_entry)
        IDENT = self._scan('IDENT')
        link.setLinkName(IDENT)
        link.set_parent_dir(dir)
        self._scan('";"')
        return link

    def link_entry(self):
        ISI_SLIST_ENTRY = self._scan('ISI_SLIST_ENTRY')
        link = ISI_SLIST_ENTRY
        return link

    def verbatim_parameter(self):
        VERBATIM_DECLARE = self._scan('VERBATIM_DECLARE')
        WHITE_SPACE_SLURP = self._scan('WHITE_SPACE_SLURP')
        ANYTHING_LINE = self._scan('ANYTHING_LINE')
        param = VerbatimParameter(ANYTHING_LINE)
        return param

    def parameter_value(self):
        WHITE_SPACE_SLURP = self._scan('WHITE_SPACE_SLURP')
        _token_ = self._peek('QUOTED_STRING', 'PARAMETER_VALUE_LINE')
        if _token_ == 'QUOTED_STRING':
            QUOTED_STRING = self._scan('QUOTED_STRING')
            val = QUOTED_STRING
        else: # == 'PARAMETER_VALUE_LINE'
            PARAMETER_VALUE_LINE = self._scan('PARAMETER_VALUE_LINE')
            val = PARAMETER_VALUE_LINE
        return val

    def dv_parameter(self):
        param = Parameter()
        _token_ = self._peek('LIST_TYPE', 'MAP_TYPE', '"dir"', '"variant"', '"vdir"', 'TYPE_KEYS', 'TYPE_BASES', 'IDENT')
        if _token_ in ['LIST_TYPE', 'MAP_TYPE']:
            _token_ = self._peek('LIST_TYPE', 'MAP_TYPE')
            if _token_ == 'LIST_TYPE':
                LIST_TYPE = self._scan('LIST_TYPE')
                param.setIsAList()
            else: # == 'MAP_TYPE'
                MAP_TYPE = self._scan('MAP_TYPE')
                param.setIsAMap()
            compound_type = self.compound_type(param)
            param.setType(compound_type)
        else: 
            valid_type = self.valid_type(param)
            param.setType(valid_type)
        IDENT = self._scan('IDENT')
        param.setName(IDENT)
        if self._peek('"="', '"{"', '";"') == '"{"':
            keyvalpairs = self.keyvalpairs()
            param.setKvp(keyvalpairs)
        if self._peek('"="', '";"') == '"="':
            self._scan('"="')
            parameter_value = self.parameter_value()
            param.setDefValue(parameter_value)
        self._scan('";"')
        return param.real()

    def keyval_val(self):
        _token_ = self._peek('QUOTED_STRING', 'KEYVAL_VALUE_RAW')
        if _token_ == 'QUOTED_STRING':
            QUOTED_STRING = self._scan('QUOTED_STRING')
            val = QUOTED_STRING
        else: # == 'KEYVAL_VALUE_RAW'
            KEYVAL_VALUE_RAW = self._scan('KEYVAL_VALUE_RAW')
            val = KEYVAL_VALUE_RAW
        return val

    def keyval_key(self):
        _token_ = self._peek('QUOTED_STRING', 'KEYVAL_KEY_RAW')
        if _token_ == 'QUOTED_STRING':
            QUOTED_STRING = self._scan('QUOTED_STRING')
            key = QUOTED_STRING
        else: # == 'KEYVAL_KEY_RAW'
            KEYVAL_KEY_RAW = self._scan('KEYVAL_KEY_RAW')
            key = KEYVAL_KEY_RAW
        return key

    def keyval_one(self, kvp):
        val = ''
        keyval_key = self.keyval_key()
        key = keyval_key
        if self._peek('":"', '","', '"}"') == '":"':
            self._scan('":"')
            keyval_val = self.keyval_val()
            val = keyval_val
        kvp.addEntry(key,val)

    def keyval_list(self):
        kvp = Kvp()
        keyval_one = self.keyval_one(kvp)
        while self._peek('","', '"}"') == '","':
            self._scan('","')
            keyval_one = self.keyval_one(kvp)
        if self._peek('","', '"}"') == '","':
            self._scan('","')
        return kvp

    def keyvalpairs(self):
        kvp = None
        self._scan('"{"')
        if self._peek('QUOTED_STRING', 'KEYVAL_KEY_RAW', '"}"') != '"}"':
            keyval_list = self.keyval_list()
            kvp = keyval_list
        self._scan('"}"')
        return kvp


def parse(rule, text):
    P = ConfigParser(ConfigParserScanner(text))
    return wrap_error_reporter(P, rule)




############################################################
#
# Globals
#
############################################################

execname = ""
sourcefile = ""
verbose = False
force_write = False


############################################################
#
# Cleanup Defines
#
############################################################

# defines by which to reference generated files (with files[])
CFG_OUT_SOURCE = 'c'
CFG_OUT_HEADER = 'h'


############################################################
#
# Internal Namespace Defines
#
############################################################

#REFERENCE_VAR     = #(default, uninitialized)
#REFERENCE_ARRAY    = 'array'
REFERENCE_DIR      = 'dir'
REFERENCE_VARIANT  = 'variant'
REFERENCE_DDIR     = 'ddir'
REFERENCE_VDIR     = 'vdir'



############################################################
#
# Generated Namespace Defines
#
############################################################

INTROSPECT_SYMBOL_PREFIX = 'gci_' #added to the front of introspection symbols
INTROSPECT_SDIR_PREFIX = INTROSPECT_SYMBOL_PREFIX + 'sdir_'
        #added to the front of static introspection dirs
INTROSPECT_DDIR_PREFIX = INTROSPECT_SYMBOL_PREFIX + 'ddir_'
        #added to the front of dynamic introspection dirs
INTROSPECT_VARIANT_PREFIX = INTROSPECT_SYMBOL_PREFIX + 'variant_'
        #added to the front of introspecion variants
INTROSPECT_DEFAULTS_PREFIX = INTROSPECT_SYMBOL_PREFIX + 'dflt_'
        #added to the front of default struct declaration
INTROSPECT_IVAR_PREFIX = INTROSPECT_SYMBOL_PREFIX + 'ivar_'
        #added to the front of introspection vars
INTROSPECT_TYPE_PREFIX = INTROSPECT_SYMBOL_PREFIX + 'type_'
        #added to the front of introspection types
VARIANT_C_CLASS = "GCI_SUPERTYPE_VARIANT"
SDIR_C_CLASS = "GCI_SUPERTYPE_SDIR"

#
# Definition of the intermediate representation for the config
#
class Config:
    """Container for all config definiton information"""

    def __init__(self):
        """Initialize the Config object."""
        # dirs is a map from the name to a list of all versions of the
        # dir
        self.dirs = {}
        self.enums = []
        self.typecopys = {}
        self.copys = []
        self.ordered = []
        self.made_symbols = []
        self.version_enum = None
        self.patches = []

    def addItem(self, item):
            """Add a tree or type to the config"""
            assert isinstance(item, Item)
            self.ordered.append(item)

    def addPatch(self, patch):
            """Add a patch to the config"""
            assert isinstance(patch, Patch)
            self.patches.append(patch)
            self.ordered.append(patch)

    def addDir(self, dir):
        """Add another Dir object to the list."""
        assert isinstance(dir, Dir)
        # If the dir is really a variant, then it will have an enum
        # we want to add.
        enum = dir.enum
        if enum:
            self.addEnum(enum)
        # If we have record of a different version of the same dir
        # then add it to the list
        if dir.name in self.dirs:
            self.dirs[dir.name].append(dir)
        else:
            # First version of the dir we have seen, so create
            # a entry in the dict for that dir.
            dir_list = [dir]
            self.dirs[dir.name] = dir_list
            self.ordered.append(dir_list)

    def addEnum(self, enum):
        """Add another Enum object to the list."""
        assert isinstance(enum, Enum)
        self.enums.append(enum)
        self.ordered.append(enum)
        if enum.is_version:
            assert not self.version_enum, "Only one version enum allowed"
            self.version_enum = enum

    def addTypecopy(self, type):
        """Add another typecopy object to the list."""
        assert isinstance(type, Typecopy)
        type.putInDict(self.typecopys)

    def translateTypecopy(self, type):
        """Find and return a typecopy conversion."""
        if self.typecopys.has_key(type):
            return self.typecopys[type]
        return type

    def addCopy(self, copy):
        """Add another Copy object to the list."""
        assert isinstance(copy, Copy)
        self.copys.append(copy)
        self.ordered.append(copy)

    def hasDir(self, name):
        """Return whether the named Dir exists."""
        return name in self.dirs

    def dump(self, prefix = ''):
        """Dump the member data to the screen"""
        print prefix + "CONFIG INTERMEDIATE REPRESENTATION"
        print prefix + "------------------------------------"
        for name in self.dirs:
            for dir in self.dirs[name]:
                dir.dump(prefix)
        for copy in self.copys:
            copy.dump(prefix)
        print prefix + "Ordering:"
        for ent in self.ordered:
            print prefix + "\t" + str(ent)
        print self.typecopys

    def addSymbol(self, symbol):
            self.made_symbols.append(symbol)

    def hasSymbol(self, symbol):
            return symbol in self.made_symbols

    def printForwardDeclare(self, fh, prefix = ''):
        """Print all forward struct declarations to the filehandle."""
        for name in self.dirs:
            for dir in self.dirs[name]:
                dir.printForwardDeclare(fh, prefix)

        fh.write( '\n' )

    def printExternDeclare(self, fh, print_global=False, prefix = ''):
        """Print all extern struct declarations to the filehandle."""
        for name in self.dirs:
            for dir in self.dirs[name]:
                dir.printExternDeclare(fh, print_global, prefix)

        fh.write( '\n' )

    def printExtraHeaders(self, staticfh, globalfh, prefix = ''):
        """Print any extra headers to the filehandle."""
        if not self.enums:
            return
        # XXX any need for staticfh here?
        globalfh.write( '#include "isi_util/isi_scan_format.h"\n' )
        staticfh.write('#include <string.h>\n')

    def fix_versioning(self):
        """Hook up entries to correct version dirs"""

        if not self.version_enum:
            # If we don't have an explicit version enum, make a
            # placeholder one that will link to the default
            # version enum defined in gconfig_types.c
            self.version_enum = Enum()
            self.version_enum.addEntry(EnumEntry("DEADCODE"))
            self.version_enum.is_default_version = True
        if self.version_enum.entries[0].value is None:
            # Version numbering by default starts with 1
            self.version_enum.entries[0].value = "1"

        # Associate dirs with the enum entry representing their
        # version
        for name in self.dirs:
            for dir in self.dirs[name]:
                dir.set_version(self.version_enum)

        # Associate entries in dir to dirs with the correct version 
        # of their dir, and create new versions of a dir if there 
        # children imply their existence
        # XXX The main problem with this approach is that it brings
        # us further from the goal of being able to import dirs from 
        # other .gcfg's into our structures.  
        for version in self.version_enum.entries:
            for name in self.dirs:
                found = False
                for dir in self.dirs[name]:
                    if dir.version == version:
                        assert not found, "Two dirs named %s with version %s" %\
                                (name, version.name)
                        dir.fix_version(self.version_enum, self.dirs)
                        found = True
                if not found:
                    # No dir of this version yet defined. decide if we
                    # need to make one.  To do so, find the highest
                    # version of the dir that is still less than this
                    # version, and ask it if it sufficient to stand
                    # in as this version as well.
                    latest_found = None
                    for last_version in self.version_enum.entries:
                        if last_version == version:
                            break
                        for dir in self.dirs[name]:
                            if dir.version == last_version:
                                latest_found = dir
                    if latest_found and latest_found.needs_new_version(
                            version, self.version_enum, self.dirs):
                        latest_found.copy_and_update(version,
                                                     self.version_enum,
                                                     self.dirs)

        # Finally, hook up the versions of a given dir up in a chain
        # for use when doing the output.
        for name in self.dirs:
            last_dir = None
            for version in self.version_enum.entries:
                for dir in self.dirs[name]:
                    if dir.version == version:
                        dir.set_previous_dir(last_dir)
                        if last_dir:
                            last_dir.set_next_dir(dir)
                        last_dir = dir
            for dir in self.dirs[name]:
                # Variants need to fix the name of enums with
                # their new version-mangled name
                dir.finishing_touch(self)

        for patch in self.patches:
            patch.fix_version(self.version_enum, self.dirs)

    def validate_namefields(self):
        for name in self.dirs:
            for dir in self.dirs[name]:
                dir.validate_namefield(self)

    def fix_and_validate(self):
        self.fix_versioning()
        self.validate_namefields()

class Dir:
    """Represents a directory"""
    introspect_prefix = INTROSPECT_SDIR_PREFIX
    c_class = SDIR_C_CLASS

    def __init__(self):
        """Initialize the Directory object."""
        self.name = None
        self.is_global = False
        self.kvp = None
        self.as_var = None
        self.vars = []
        self.enum = None
        self.version = None
        self.checking_version = False
        self.version_fixed = False
        self.previous_dir = None
        self.next_dir = None
        self.version_enum = None

    def set_previous_dir(self, dir):
        self.previous_dir = dir

    def set_next_dir(self, dir):
        self.next_dir = dir

    def set_version(self, version_enum):
        """Determines what the Dir's version is and sets it."""
        if self.kvp and KVP_KEY_VERSION in self.kvp.dict:
            self.version = version_enum.get_entry(
                    self.kvp.dict[KVP_KEY_VERSION])
        else:
            self.version = version_enum.get_lowest_entry()

    def fix_version(self, version_enum, dirs):
        """Hooks up the dir references to the correct version of the dir"""
        self.version_enum = version_enum
        if self.version_fixed:
            return
        if not self.version:
            self.set_version(version_enum)
        for var in self.vars:
            var.fix_version(self.version, version_enum, dirs)
        self.version_fixed = True

    def needs_new_version(self, target_version, version_enum, dirs):
        """Determines this dir can stand in for target version or """ \
        """if a new version needs to be created"""
        # checking_version prevents us from gettin stuck in infinite
        # recursion.  
        if self.checking_version:
            return False
        self.checking_version = True
        ret = False
        for var in self.vars:
            if var.needs_new_version(self.version, target_version,
                                     version_enum, dirs):
                ret = True
                break
        self.checking_version = False
        return ret

    def copy_and_update(self, target_version, version_enum, dirs):
        """ Creates a copy of the dir at the given version."""
        new_dir = self.copy()
        new_dir.version = target_version
        dirs[new_dir.name].append(new_dir)
        new_dir.fix_version(version_enum, dirs)
        return new_dir

    def copy(self):
        """ Make a copy of the dir with everything but the version."""
        new = self.__class__()
        new.setName(self.name)
        new_kvp = self.kvp.copy() if self.kvp else None
        if new_kvp and KVP_KEY_VERSION in new_kvp.dict:
            del new_kvp.dict[KVP_KEY_VERSION]
        new.setKvp(new_kvp)
        for var in self.vars:
            new_var = var.copy()
            new_var.set_parent_dir(new)
            new.addVar(new_var)
        return new

    def setName(self, name):
        """Set the name to the given string."""
        assert self.name == None
        assert name == str(name)
        self.name = name

    def getName(self):
        return self.name

    def setKvp(self, kvp):
        """Set the key-value-pairs declaration to the given Kvp."""
        if kvp is None:
                return
        assert self.kvp == None
        assert isinstance(kvp, Kvp)
        self.kvp = kvp
        if KVP_KEY_GLOBAL in self.kvp.dict:
                self.is_global = True

    def addVar(self, var):
        """Add the given Parameter object to the vars list."""
        assert isinstance(var, (Parameter, DDirLinkParameter))
        self.vars.append(var)

    def getSelfAsVar(self):
        if not self.as_var:
            var = Parameter()
            var.setName(self.name)
            var.setType(self.name)
            var.setIsDir()
            var.dir = self
            var.is_global = True
            self.as_var = var
        return self.as_var

    def dump(self, prefix = ''):
        """Dump the member data to the screen."""
        print prefix + "Dir " + self.mangled_name() + " Version " + \
                (self.version.name if self.version else "None") + ":"
        for var in self.vars:
            var.dump(prefix + '\t')

    def printForwardDeclare(self, fh, prefix = ''):
        """Print a forward declaration of struct type to the
        filehandle."""
        fh.write(prefix + "struct %s;\n" % self.mangled_name())

    def mangled_name(self):
        """Gives the version mangled name so that different """ \
        """versions of the same dir don't tromp each other's """ \
        """symbols."""
        if not self.next_dir:
            # Highest Version, no mangling
            return self.name
        else:
            assert self.version
            return self.version.name + "_" + self.name

    def printStructDeclare(self, fh, config, prefix = ''):
        """Print struct declaration to the filehandle."""
        for var in self.vars:
                var.printStructPreDeclare(fh, config, prefix)
        fh.write(prefix + "struct %s {\n" % self.mangled_name())
        for var in self.vars:
                var.printStructEntryDeclare(fh, prefix + '\t')
        fh.write('};\n\n')

    def printDefaultsDeclare(self, fh, prefix = ''):
        """Print default-struct declaration to the filehandle."""
        defaults_struct_name = INTROSPECT_DEFAULTS_PREFIX + \
                self.mangled_name()
        fh.write(prefix + "static struct %s %s%s = {\n" %
                 (self.mangled_name(), INTROSPECT_DEFAULTS_PREFIX,
                 self.mangled_name()))
        for var in self.vars:
            var.printDefaultsDeclare(fh, prefix + '\t')
            var.set_default_location(defaults_struct_name)
        fh.write( '};\n\n' )

    def printDDirIterDeclare(self, fh, config, prefix = ''):
        """Print dynamic-dir iterator info.for any vars in dir."""
        for var in self.vars:
            var.printDDirIterDeclare(fh, config, prefix)

    def getMaybeStatic(self):
        return self.is_global and "" or "static "

    def printExternDeclare(self, fh, print_global=False, prefix = ''):
        """Print extern declaration of introspect struct to the fh.
           For 'print_global', give external gci_ivar definition.
           For 'not-print-global', gives internal gci_sdir defn."""
        if self.is_global and print_global:
            fh.write(prefix + "extern struct gci_ivar %s%s;\n" %
                     (INTROSPECT_IVAR_PREFIX, self.mangled_name()))
        if not print_global:
            fh.write(prefix + "%sstruct gci_sdir %s%s;\n" %
                     (self.getMaybeStatic(), self.introspect_prefix,
                     self.mangled_name()))

    def printIntrospectDeclare(self, fh, config, prefix = ''):
        """Print introspection declaration to the filehandle."""
        # Create the dir introspection struct
        fh.write(prefix + "%sstruct gci_sdir %s%s = {\n" %
                 (self.getMaybeStatic(), self.introspect_prefix,
                 self.mangled_name()))
        prefix2 = prefix + '\t'
        fh.write(prefix2 + 'base_name: "%s", \n' % self.name)
        fh.write(prefix2 + 'struct_name: "%s", \n' % self.mangled_name())
        fh.write(prefix2 + 'struct_size: sizeof(struct %s), \n' %
                 self.mangled_name())
        fh.write(prefix2 + 'is_variant: %s, \n' % self.is_variant_string())
        fh.write(prefix2 + 'default_vals: &%s%s, \n' % (
                 INTROSPECT_DEFAULTS_PREFIX, self.mangled_name()))
        if self.kvp and self.kvp.getString() != "":
            fh.write(prefix2 + 'kvp_init_string: "%s", \n' %
                     self.kvp.getString())
        if self.version_enum.is_default_version:
            # Link to the default enum defined in gconfig_types.c
            fh.write(prefix2 + 'version: 1, \n')
            fh.write(prefix2 + 'version_enum: '
                     '&gci_type_enum_default_version_enum,\n')
        else:
            fh.write(prefix2 + 'version: %s, \n' % self.version.name)
            fh.write(prefix2 + 'version_enum: &%s, \n' %
                     self.version_enum.symbol())
        fh.write(prefix2 + 'ivars: {\n')
        parent_struct = "struct %s" % self.mangled_name()
        for var in self.vars:
            var.printIntrospectDeclare(fh, config,
                                       parent_struct=parent_struct,
                                       prefix=prefix2)
        fh.write(prefix2 + '{0} } };\n\n')
        # Finally, create a gci_ivar for any global dirs
        if self.is_global:
            var = self.getSelfAsVar()
            fh.write(prefix + "struct gci_ivar %s = \n" %
                     self.global_ivar_symbol())
            var.printIntrospectDeclare(fh, config, prefix=prefix2,
                                       suffix = ';\n\n')

    def global_ivar_symbol(self):
            return INTROSPECT_IVAR_PREFIX + self.mangled_name()

    def output_to_files(self, files, config, dest_dir):
            """Output the correct lines for a dir to provided files"""
            # dir_config instances are expanded to both files
            self.printStructDeclare(files[CFG_OUT_HEADER].filehandle,
                                    config)
            self.printDefaultsDeclare(files[CFG_OUT_SOURCE].filehandle)
            self.printDDirIterDeclare(files[CFG_OUT_SOURCE].filehandle,
                                      config)
            self.printIntrospectDeclare(files[CFG_OUT_SOURCE].filehandle,
                                        config)

    def is_variant_string(self):
            """ "true" if this is a variant, "false" otherwise"""
            return "false"

    def finishing_touch(self, config):
            pass

    def validate_namefield(self, config):
        for var in self.vars:
            var.validate_namefield(config)

class Variant(Dir):
    introspect_prefix = INTROSPECT_VARIANT_PREFIX
    c_class = VARIANT_C_CLASS

    def __init__(self):
        Dir.__init__(self)
        self.introspect_prefix = INTROSPECT_VARIANT_PREFIX
        self.introspect_type = "gci_variant"
        self.enum = None

    def fix_version(self, version_enum, dirs):
        Dir.fix_version(self, version_enum, dirs)

    def finishing_touch(self, config):
        """Fixes the the enum to use the version mangled name"""
        # Cannot be run until the versions have been fixed.  
        self.enum = Enum()
        self.enum.setName(self.mangled_name() + "_type")
        self.vars.insert(0, self.enum_as_param())
        entry = EnumEntry(self.enum.name.upper() + "__INVALID")
        entry.setDesc("_invalid")
        entry.setValue("0")
        self.enum.addEntry(entry)
        for var in self.vars[1:]:
            entry = EnumEntry(self.enum.name.upper() + "_" + var.name.upper())
            entry.setDesc(var.name)
            self.enum.addEntry(entry)
        config.enums.append(self.enum)

    def enum_as_param(self):
        """ Get the enum associated with the Variant as a Parameter"""
        param = Parameter()
        param.setName("type")
        param.setType("enum " + self.enum.name)
        return param

    def getSelfAsVar(self):
        """ Get the Variant as a Parameter"""
        var = Dir.getSelfAsVar(self)
        var.reference=None
        var.setIsVariant()
        return var

    def printStructDeclare(self, fh, config, prefix = ''):
        """Print struct declaration to the filehandle."""
        for var in self.vars:
            var.printStructPreDeclare(fh, config, prefix)
        fh.write(prefix + "struct %s {\n" % self.mangled_name())
        self.enum_as_param().printStructEntryDeclare(fh, prefix + '\t')
        fh.write(prefix + "\tunion {\n")
        for var in self.vars[1:]:
            var.printStructEntryDeclare(fh, prefix + '\t\t')
        fh.write('\t};\n\n')
        fh.write('};\n\n')

    def printDefaultsDeclare(self, fh, prefix = ''):
        """Print default-struct declaration to the filehandle."""
        defaults_struct_name = INTROSPECT_DEFAULTS_PREFIX + self.mangled_name()
        fh.write(prefix + "static struct %s %s[] = {\n" %
                 (self.mangled_name(), defaults_struct_name))
        # Special case the default location for the type variable
        self.vars[0].set_default_location("%s[0]" % defaults_struct_name)

        # Use self.enum.entries[1:] to skip the invalid entry.
        i = 0
        for var, entry in zip(self.vars[1:], self.enum.entries[1:]):
            fh.write(prefix + "\t{type:%s" % entry.name)
            var.printDefaultsDeclare(fh, ",{", suffix="}")
            fh.write("},\n")
            var.set_default_location("%s[%d]" % (defaults_struct_name, i))
            i += 1
        fh.write(prefix + "\t{0}\n")
        fh.write('};\n\n')

    def is_variant_string(self):
        """ "true" if this is a variant, "false" otherwise"""
        return "true"

    def output_to_files(self, files, config, dest_dir):
        self.enum.output_to_files(files, config, dest_dir)
        Dir.output_to_files(self, files, config, dest_dir)

class Patch:
    """Represents a patch to a dir in another tree"""

    def __init__(self):
        self.tree = None
        self.dir = None
        self.kvp = None
        self.vars = []
        self.extra_vars = []
        self.version_enum = None

    def fix_version(self, version_enum, dirs):
        """Hooks up the dir references to the correct version of the dir"""
        self.version_enum = version_enum
        for var in self.vars:
            # Always set to highest version
            var.fix_version(version_enum.entries[-1], version_enum, dirs)

    def setKvp(self, kvp):
        """Set the key-value-pairs declaration to the given Kvp."""
        if kvp is None:
                return
        assert self.kvp == None
        assert isinstance(kvp, Kvp)
        self.kvp = kvp

    def setTree(self, tree):
        """Set tree to be patched"""
        assert self.tree == None
        assert isinstance(tree, str)
        self.tree = tree

    def setDir(self, dir):
        """Set dir to be patched"""
        assert self.dir == None
        assert isinstance(dir, str)
        self.dir = dir

    def addVar(self, var):
        """Add the given Parameter object to the vars list."""
        assert isinstance(var, Parameter)
        self.vars.append(var)

    def symbol(self):
        """Get a deterministic, but almost certainly unique symbol"""
        # XXX: import at top??
        import hashlib
        hasher = hashlib.sha1()
        hasher.update(self.tree)
        hasher.update(self.dir)
        for var in self.vars:
            hasher.update(var.name)
        return "gci_patch_" + hasher.hexdigest()

    def defaults_struct(self):
        """Name of struct type to hold our defaults"""
        return self.symbol() + "_defaults"

    def printDefaultsStructDefinition(self, fh, config):
        """Print struct declaration to the filehandle."""
        for var in self.vars:
            var.printStructPreDeclare(fh, config)
        fh.write("struct %s {\n" % self.defaults_struct())
        for var in self.vars:
            var.printStructEntryDeclare(fh,'\t')
        fh.write('};\n\n')

    def printDefaultsDeclare(self, fh):
        """Print default-struct declaration to the filehandle."""
        fh.write("static struct %s %s = {\n" %
            ((self.defaults_struct(),) * 2))
        for var in self.vars:
            var.printDefaultsDeclare(fh, '\t')
            var.set_default_location(self.defaults_struct())
        fh.write( '};\n\n' )

    def extra_ivars_location(self):
        return self.symbol() + "_extra_ivars"

    def create_old_vars(self, config):
        """Creates the ivars for all the older version of the added entries"""
        for var_index, var in enumerate(self.vars):
            if not var.has_reference_to_dir():
                continue
            next_version_location = "%s.ivars[%d]" % (self.symbol(), var_index)
            current_var = var
            while current_var.dir.previous_dir:
                last_var = current_var
                current_var = last_var.copy()
                current_var.fix_version(
                        last_var.dir.previous_dir.version,
                        config.version_enum, config.dirs)
                current_var.next_version_location = next_version_location
                next_version_location = "%s[%d]" % \
                        (self.extra_ivars_location(), len(self.extra_vars))
                last_var.previous_version_location = next_version_location
                self.extra_vars.append(current_var)

    def print_extra_ivars(self, file, config):
        """Outputs the extra ivars created for old versions"""
        if not self.extra_vars:
            return
        file.write("static struct gci_ivar %s[] = {\n" %
                   self.extra_ivars_location())
        for var in self.extra_vars:
            var.printIntrospectDeclare(file, config,
                                       parent_struct=None, prefix="\t")
        file.write('\t{0}};\n\n')

    def output_to_files(self, files, config, dest_dir):
        self.create_old_vars(config)
        header = files[CFG_OUT_HEADER].filehandle
        source = files[CFG_OUT_SOURCE].filehandle

        # Declare the gci_patch struct
        header.write("extern struct gci_patch %s;\n" % self.symbol())

        # Deal with any ddir
        for var in self.vars:
            var.printDDirIterDeclare(source, config)
        for var in self.extra_vars:
            var.printDDirIterDeclare(source, config)

        self.print_extra_ivars(source, config)

        for var in self.vars:
            if var.could_have_default():
                # Define the defaults struct (internal only, so put it static
                # in source)
                self.printDefaultsStructDefinition(source, config)
                # Fill an a defaults with the correct defaults
                self.printDefaultsDeclare(source)
                break
        # Declare our gci_patch structure
        source.write("struct gci_patch %s = {\n" % self.symbol())
        source.write('\t.tree = "%s",\n' % self.tree)
        source.write('\t.patched_dir = "%s",\n' %self.dir)
        source.write("\t.ivars = {\n")
        for var in self.vars:
            var.printIntrospectDeclare(source, config,
                                       parent_struct=None, prefix="\t")
        source.write('\t{0} } };\n\n')

        # Create .patch file

        patch_file_name = self.tree + "." +  self.symbol() + ".patch"
        files[patch_file_name] = patch = FileInfo(patch_file_name,
                                                  dest_dir)
        patch.filehandle.write(
                "%s:%s:%s" % (self.tree, self.symbol(),
                              "lib%s.so" % library_name))


class Enum:
    """Represents an enum"""

    def __init__(self):
        """Initialize the Enum object."""
        self.name = None
        self.entries = []
        self.kvp = None
        self.is_version = False
        self.is_default_version = False

    def get_entry(self, info):
        """Find entry by name, description, or value"""
        for entry in self.entries:
            if entry.matches(info):
                return entry
        assert False, "Could not interpret '%s' " % info

    def get_lowest_entry(self):
        assert self.entries, "Enum is empty"
        return self.entries[0]

    def setName(self, name):
        """Set the name to the given string."""
        assert self.name == None, \
                "Trying to set name to %s but it is already %s" % \
                (name, self.name)
        assert name == str(name)
        self.name = name

    def getName(self):
        return self.name

    def output_to_files(self, files, config, dest_dir):
        """Output the correct lines for an enum to provided files"""
        self.printEnumDeclare(files[CFG_OUT_HEADER].filehandle,
                              config)
        self.printEnumFmtScan(
                files[CFG_OUT_SOURCE].filehandle,
                files[CFG_OUT_HEADER].filehandle, config)
        self.printEnumType(files[CFG_OUT_SOURCE].filehandle, config)

    def addEntry(self, entry):
        """Add the given Enum object to the entries list."""
        assert isinstance(entry, EnumEntry)
        self.entries.append(entry)

    def setKvp(self, kvp):
        """Set the key-value-pairs declaration to the given Kvp."""
        if kvp is None:
            return
        assert self.kvp == None
        assert isinstance(kvp, Kvp)
        self.kvp = kvp
        if "version" in kvp.getDict():
            self.is_version = True

    def dump(self, prefix = ''):
        """Dump the member data to the screen."""
        print prefix + "Enum " + self.name + ":"
        for entry in self.entries:
            entry.dump(prefix + '\t')

    def printEnumDeclare(self, fh, config, prefix = ''):
        """Print enum C declaration to the filehandle."""
        fh.write( prefix + "enum %s {\n" % self.name )
        for entry in self.entries:
            entry.printEnumDeclare(fh, prefix + '\t')
        fh.write('};\n\n')

    def symbol(self):
            return "gci_type_enum_" + self.name 

    def printEnumFmtScan(self, fh, globalfh, config, prefix = ''):
        """Print enum introspection declaration to the filehandle."""
        globalfh.write(prefix +
                       "struct fmt_conv_ctx enum_%s_fmt(enum %s);\n"
                       % (self.name, self.name))
        globalfh.write(prefix + "struct fmt_scan_conv_ctx " +
                       " enum_%s_scan_fmt(enum %s *);\n\n"
                       % (self.name, self.name))
        fh.write(prefix +
                 "MAKE_ENUM_FMT_AND_SCAN(enum_%s, enum %s,\n"
                 % (self.name, self.name))
        for entry in self.entries:
                entry.printEnumFmtScan(fh, prefix + '\t')
        fh.write( prefix + ');\n' )

    def printEnumType(self, fh, config, prefix = ''):
        """Print enum introspection declaration to the filehandle."""
        # This output largely matches the GCI_FMT_TYPE() macro used in
        # gconfig_types.c
        fh.write(
"""static void _gci_fmt_print_%(Gcfg_type)s(struct gci_type *type,
struct fmt *fmt, const void *vptr)
{
%(C_type)s const var = *(%(C_type)s const *)vptr;
fmt_print(fmt, "%%{}", %(Fmt_type)s_fmt(var));
}
static bool _gci_fmt_scan_%(Gcfg_type)s(struct gci_type *type,
struct fmt *fmt, void *vptr)
{
return fmt_scan(fmt, "%%{}", %(Fmt_type)s_scan_fmt(vptr));
}
struct gci_type %(Symbol)s = {
name: "%(Name_type)s",
size: sizeof(%(C_type)s),
gt_fmt_print: &_gci_fmt_print_%(Gcfg_type)s,
gt_fmt_scan: &_gci_fmt_scan_%(Gcfg_type)s,
gt_type_equal: &gci_generic_type_equal,
};

"""
                % { "Symbol" : self.symbol(),
                    "Gcfg_type" : "enum_%s" % self.name,
                    "Name_type" : "enum %s" % self.name,
                    "C_type"    : "enum %s" % self.name,
                    "Fmt_type"  : "enum_%s" % self.name,
                })


class EnumEntry:
    """Represents an enum entry"""

    def __init__(self, name):
        """Initialize the EnumEntry object."""
        self.name = name
        self.desc = None
        self.value = None

    def matches(self, info):
        return info in (self.name, self.desc, self.value)

    def setDesc(self, desc):
        """Set the description to the given string."""
        assert self.desc == None
        assert desc == str(desc)
        self.desc = desc

    def setValue(self, value):
        """Set the value to the given string."""
        assert self.value == None
        assert value == str(value)
        self.value = value

    def dump(self, prefix = ''):
        """Dump the member data to the screen."""
        maybedesc = self.desc and " (desc:" + self.desc + ")" or ""
        maybevalue = self.value and " = value:" + self.value or ""
        print prefix + self.name + maybedesc + maybevalue

    def printEnumDeclare(self, fh, prefix = ''):
        """Print enum entry declaration to the filehandle."""
        fh.write(prefix + "%s" % self.name)
        if self.value:
            fh.write(" = %s" % self.value)
        fh.write(",\n")

    def printEnumFmtScan(self, fh, prefix = ''):
        """Print enum entry declaration to the filehandle."""
        fh.write(prefix + "ENUM_VAL(%s" % self.name)
        if self.desc:
            fh.write(", \"%s\"" % self.desc)
        fh.write("),\n")

class Typecopy:
    """Represents a typecopy that lets one Structure-entry Type be
    represented by a different introspection type."""

    def __init__(self):
        """Initialize the Typecopy object."""
        self.introspecttype = None
        self.structtype = None

    def setIntrospectType(self, type):
        """Set the given config-type."""
        assert type == str(type)
        self.introspecttype = type

    def setStructType(self, type):
        """Set the given code-type."""
        assert type == str(type)
        self.structtype = type

    def putInDict(self, dict):
            """Add ourselves to the given dictionary"""
            dict[self.structtype] = self.introspecttype

class Copy:
    """Represents a copy/passthrough batch of text"""

    def __init__(self):
        """Initialize the Copy object."""
        self.dest = []
        self.text = []

    def addDest(self, dest):
        """Add the given destination to the copy output."""
        assert dest == str(dest)
        self.dest.append(dest)

    def addText(self, text):
        assert text == str(text)
        self.text.append(text)

    def parseCopyBlock(self, rawtext):
        """Parse a copy block to separate the tokens and content

        Expect text like:
        %copy hc
        #include <a.h>
        ...
        %copyend

        """
        text = rawtext[5:-8]
        destre = re.search('^[ \t]*(c|h|ch|hc)[ \t]*(#.*)*\n',text)
        text = text[len(destre.group(0)):]
        for char in destre.group(1):
            self.addDest(char)
        self.addText(text)

    def dump(self, prefix = ''):
        """Dump the member data to the screen."""
        print prefix + "Copy to " + str(self.dest) + ":"
        print self.text

    def output_to_files(self, files, config, dest_dir):
        """Output the correct lines for a copy to provided files"""
        for dest in self.dest:
            # figure out what file we're aiming for
            if dest == 'h':
                fh = files[CFG_OUT_HEADER].filehandle
            elif dest == 'c':
                fh = files[CFG_OUT_SOURCE].filehandle
            else:
                assert False, "Internal failure: dest '%s' not known" % dest

            # and now write it
            fh.write("\n")
            for line in self.text:
                fh.write("%s\n" % line)
            fh.write("\n")

# XXX: this really shouldn't be just sprinkled in here.  Can it be a class
# method of something??
def Type2Symbol(type):
    return INTROSPECT_TYPE_PREFIX + \
            type.translate(string.maketrans(' *', '_p'))


class Kvp:
    """Key/Value Pair dictionary in both python and C representations"""

    def __init__(self):
        """Initialize the Kvp object."""
        self.dict = {}

    def addEntry(self, key, value):
        """Add an entry to this kvp object"""
        self.dict[escape(key.strip())] = escape(value.strip())

    def getDict(self):
        """Return the dictionary representing this kvp"""
        return self.dict

    def getString(self):
        """Write out the kvp string for the kvp, minus parser-only keys"""
        # XXX: merge this kvp object with isi_kvp??
        kvp_str = ""
        for key, value in self.dict.items():
            if key not in PARSER_ONLY_KEYS:
                if kvp_str != "":
                    kvp_str += ","
                else:
                    kvp_str += "{"
                kvp_str += "%s:%s" % (key, value)
        if kvp_str != "":
            kvp_str += "}"

        return kvp_str

    def copy(self):
        """Returns a copy of this kvp object"""
        new_kvp = Kvp()
        # Only shallow copy the dict, values are strings, so immutable
        new_kvp.dict = dict(self.dict)
        return new_kvp

class Parameter:
    """Represents a single C function parameter"""

    def __init__(self):
        """Initialize the Parameter object."""
        self.name = None
        self.type = None
        self.reference = None
        self.bits = None
        self.kvp = None
        self.def_value = None
        self.virtual_lib = None
        self.is_a_list = False
        self.is_a_map = False
        self.pytype = None
        self.default_location = None
        self.dir = None
        self.is_global = False
        self.previous_version_location = None
        self.next_version_location = None

    def __str__(self):
        """Return the string representation of the parameter."""
        pstr = self.type + ' '
        if self.reference == 'pointer':
           pstr += '*'
        pstr += self.name
        if self.reference == 'array':
           pstr += '[]'
        if self.bits:
           pstr += ' : ' + self.bits
        return pstr

    def copy(self):
        """Return a copy of the dir."""
        new = self.__class__()
        new.name = self.name
        new.type = self.type
        new.reference = self.reference
        new.kvp = self.kvp
        new.def_value = self.def_value
        new.virtual_lib = self.virtual_lib
        new.is_a_list = self.is_a_list
        new.is_a_map = self.is_a_map
        new.pytype = self.pytype
        return new

    def set_parent_dir(self, dir):
        pass

    def fix_version(self, current_version, version_enum, dirs):
        # If this references a dir (directly or through a ddir), find 
        # the correct version of the dir or create it,
        # copying from the previous version of the dir if need be.
        if self.has_reference_to_dir():
            latest_found = None
            assert self.type in dirs, "Don't know a dir named " + self.name
            for version in version_enum.entries:
                for dir in dirs[self.type]:
                    if dir.version == version:
                        if current_version == version:
                            # This is the final version we are
                            # looking for, so we are done here.
                            self.dir = dir
                        else:
                            # This is a version of the dir less
                            # than our target version.  By the
                            # end of the loop this will be the
                            # latest version of the dir less than
                            # or equal to the target version.  
                            latest_found = dir
                        break
                else:
                    # No dir of this version exists
                    if current_version == version:
                        assert latest_found, ("Expected a version of "
                                "dir %s to be equal or lower than "
                                "version %s, but only found %s" %
                                (self.type, current_version.name,
                                [dir.version.name for dir
                                in dirs[self.type]]))
                        # No dir of the target version exists, so
                        # create one if needed, or link to a sufficent
                        # older version.  
                        if latest_found.needs_new_version(
                                current_version, version_enum, dirs):
                            self.dir = latest_found.copy_and_update(
                                    current_version, version_enum, dirs)
                        else:
                            self.dir = latest_found
                if self.dir:
                    break
            else:
                assert False, "could not find own version(%s) in " \
                        "version enum %s" % (current_version.name if \
                        current_version else "(None)",
                        [ entry.name for entry in version_enum.entries])

    def needs_new_version(self, current_version, target_version,
                          version_enum, dirs):
        """See if this parameter would necessitate a new dir for """\
        """the given version"""

        # If we refence a dir, and it has or needs a version greater 
        # than the current version, but less than or equal to the target
        # version, then we need to create a new version of our parent 
        # dir.
        if self.has_reference_to_dir():
            latest_found = None
            after_my_version = False
            assert self.type in dirs
            for version in version_enum.entries:
                if not after_my_version:
                    for dir in dirs[self.type]:
                        if dir.version == version:
                            # Keep track of the highest version less 
                            # than or equal to our current version,
                            # so if the target version doesn't exists,
                            # we can query it to whether it needs the
                            # the target version.  
                            latest_found = dir
                            break
                    if version == current_version:
                        after_my_version = True
                else:
                    for dir in dirs[self.type]:
                        if dir.version == version:
                            return True
                    if version == target_version:
                        break
            assert latest_found, "reference to dir %s at lower " \
                    "version (%s) then is defined %s" % \
                    (self.type, current_version.name,
                    [dir.version.name for dir in dirs[self.type]])
            if latest_found.needs_new_version(
                    target_version, version_enum, dirs):
                return True
        return False

    def setIsAList(self):
        """Track that this is a list"""
        self.is_a_list = True
        self.pytype = GCI_DIR_PYTYPE_LIST

    def setIsAMap(self):
        """Track that this is a map"""
        self.is_a_map = True
        self.pytype = GCI_DIR_PYTYPE_DICT

    def setPytype(self, pytype):
        """Set this param's pytype"""
        self.pytype = pytype

    def set_default_location(self, location):
        """Tell the var the location of the structue its default is in."""
        # Dirs, Variants and Patches each put the default
        # for their vars in a different location
        assert isinstance(location, str)
        self.default_location = location

    def real(self):
        """Returns the correct class of ddir for the passed in keys"""
        if not self.is_a_list and not self.is_a_map:
            return self
        elif self.is_a_list and not self.is_a_map:
            # XXXJDR: Add errors/warnings for nonsensical input
            if self.kvp is not None:
                if KVP_KEY_C_LIST_TYPE in self.kvp.dict:
                    new_param = makeNewDDirParam(
                            self.kvp.dict[KVP_KEY_C_LIST_TYPE])
                    new_param.setDDirSaveField('')
                else:
                    new_param = makeNewDDirParam("ISI_VECTOR")
                if KVP_KEY_NAME in self.kvp.dict:
                    new_param.setDDirSaveField(self.kvp.dict[KVP_KEY_NAME])
                if KVP_KEY_LINK in self.kvp.dict:
                    new_param.setDDirNavField(self.kvp.dict[KVP_KEY_LINK])
                new_param.setKvp(self.kvp)
            else:
                new_param = makeNewDDirParam("ISI_VECTOR")
            new_param.setType(self.type)
            new_param.setName(self.name)
            new_param.setPytype(self.pytype)
            if self.def_value is not None:
                param.setDefValue(self.def_value)
            return new_param
        elif self.is_a_map and not self.is_a_list:
            # XXXDTJ: Add errors/warnings for nonsensical input
            new_param = makeNewDDirParam("ISI_STRMAP")
            new_param.setType(self.type)
            new_param.setName(self.name)
            new_param.setPytype(self.pytype)
            if self.def_value is not None:
                param.setDefValue(self.def_value)
            return new_param
        else:
            # XXX:BETTER VALDIATION
            assert False, "Impossible flag set = internal error"

    def setType(self, type):
        """Set the type to the given string."""
        assert self.type == None
        assert type == str(type)
        self.type = type

    def getRichType(self):
        """Get the full(rich) type."""
        if (self.reference in
                (REFERENCE_DIR, REFERENCE_DDIR, REFERENCE_VARIANT)):
            type = "struct %s*" % self.dir.mangled_name()
        elif self.reference == REFERENCE_VDIR:
            type = "void *"
        else:
            type = self.type
        return type

    def getStructType(self, config):
        # start with our rich type
        usetype = self.getRichType()
        # see if we have a typecopy conversion to use
        # (returns the unmolested version if there is none)
        usetype = config.translateTypecopy(usetype)
        # and now convert it to a usable C symbol
        return Type2Symbol(usetype)

    def setName(self, name):
        """Set the name to the given string."""
        assert self.name == None
        assert name == str(name)
        self.name = name

    def getRichName(self):
        """Get the full(rich) name."""
        name = self.name
        if self.bits:
                name = name + ":%s" % self.bits
        return name

    def setIsDir(self):
        """Track that this is a dir"""
        self.setReference(REFERENCE_DIR)

    def has_reference_to_dir(self):
        """Either a dir reference, or a ddir of dirs"""
        return self.reference in (REFERENCE_DIR, REFERENCE_VARIANT)

    def setIsVariant(self):
        """Track that this is a variant"""
        self.setReference(REFERENCE_VARIANT)

    def setIsVirtualDir(self):
        """Track that this is a vdir"""
        self.setReference(REFERENCE_VDIR)

    def setReference(self, reference):
        """Set the reference to the given string."""
        assert self.reference == None
        assert reference == str(reference)
        self.reference = reference

    def setBits(self, bits):
        """Set the bits to the given string."""
        assert self.bits == None
        assert bits == str(bits)
        self.bits = bits

    def setDefValue(self, def_value):
        """Set the default-value to the given string."""
        assert self.def_value == None
        assert def_value == str(def_value)
        self.def_value = def_value

    def setKvp(self, kvp):
        """Set the key-value-pairs declaration to the given Kvp."""
        if kvp is None:
                return
        assert self.kvp == None
        assert isinstance(kvp, Kvp)
        self.kvp = kvp

    def setVirtualLibrary(self, string):
        assert self.virtual_lib == None
        assert self.reference == REFERENCE_VDIR
        self.virtual_lib = string;

    def dump(self, prefix = ''):
        """Dump the member data to the screen."""
        print prefix + "Parameter \"" + str(self.name) + "\":"
        print prefix + "\tType:", str(self.type)
        print prefix + "\tReference:", str(self.reference)
        print prefix + "\tBits:", str(self.bits)
        if self.kvp:
            print prefix + "\tKeyValPairs:", str(self.kvp.getString())
            print prefix + "\tKeyValPairs (dict):", str(self.kvp.getDict())
        else:
            print prefix + "\tKeyValPairs: Unset"
        print prefix + "\tDefault Value:", str(self.def_value)

    def printDDirIterDeclare(self, fh, config, prefix = ''):
        """Print dynamic-dir iterator info -- normally nothing"""
        return

    def printStructPreDeclare(self, fh, config, prefix = ''):
        """Print stuff that needs to come before the struct
        declaration -- normally nothing"""
        return

    def printStructEntryDeclare(self, fh, prefix = '', suffix = ';\n'):
        """Print single-var structure declaration."""
        fh.write(prefix + "%s\t%s" %
                 (self.getRichType(), self.getRichName()) + suffix)

    def printDefaultsDeclare(self, fh, prefix = '', suffix = ',\n'):
        """Print single-var default declaration."""
        if self.def_value:
            fh.write(prefix + "%s: %s" %
                     (self.name, self.def_value) + suffix)

    def could_have_default(self):
        return self.reference is None

    def printIntrospectDeclare(self, fh, config, parent_struct=None,
                               prefix = '', suffix = ',\n'):
        """Print single-var introspection declaration."""
        offset = 'offsetof(%s,%s)' % (parent_struct, self.name) if \
            parent_struct else "-1"
        fh.write( prefix + '{\tname: "%s", \n' % self.getRichName() )
        prefix2 = prefix + '\t'
        # Only write the init string if our kvp exists
        # and contains keys which are not parser-only
        if self.kvp and self.kvp.getString() != "":
            fh.write(prefix2 + 'kvp_init_string: "%s", \n' %
                     self.kvp.getString() )
        if self.is_global:
            # Chain up the versions of the ivar if ivar is set as global
            if self.dir.previous_dir:
                fh.write(prefix2 + 'lower_version: &%s, \n' %
                         self.dir.previous_dir.global_ivar_symbol())
            if self.dir.next_dir:
                fh.write(prefix2 + 'higher_version: &%s, \n' %
                         self.dir.next_dir.global_ivar_symbol())
        else:
            # Chain up the versions of the ivar if added by a patch
            if self.previous_version_location:
                fh.write(prefix2 + 'lower_version: &%s, \n' %
                         self.previous_version_location)
            if self.next_version_location:
                fh.write(prefix2 + 'higher_version: &%s, \n' %
                         self.next_version_location)
        fh.write( prefix2 + 'offset: %s, \n' % offset)
        if self.reference == REFERENCE_DDIR:
            fh.write(prefix2 + 'supertype: GCI_SUPERTYPE_DDIR, \n')
            fh.write(prefix2 + 'cls: { ddir: &%s }, \n' %
                     (self.ddir_symbol_name()))
        elif self.reference == REFERENCE_DIR:
            fh.write(prefix2 + 'supertype: GCI_SUPERTYPE_SDIR, \n')
            fh.write(prefix2 + 'cls: { sdir: &%s%s }, \n' %(
                     INTROSPECT_SDIR_PREFIX, self.dir.mangled_name()))
        elif self.reference == REFERENCE_VARIANT:
            fh.write(prefix2 + 'supertype: GCI_SUPERTYPE_VARIANT, \n')
            fh.write(prefix2 + 'cls: { sdir: &%s%s }, \n' %
                     (INTROSPECT_VARIANT_PREFIX, self.dir.mangled_name()))
        elif self.reference == REFERENCE_VDIR:
            fh.write(prefix2 + 'supertype: GCI_SUPERTYPE_VDIR, \n')
            fh.write(prefix2 + 'cls: { vdir: &((struct gci_vdir)'
                     '{ type:"%s%s", lib:%s }) }, \n' %
                     (INTROSPECT_IVAR_PREFIX, self.type, self.virtual_lib))
        else: #STANDARD or POINTER
            fh.write( prefix2 + 'default_value: &%s.%s, \n' %
                    (self.default_location, self.name) )
            fh.write( prefix2 + 'supertype: GCI_SUPERTYPE_VAR, \n' )
            fh.write( prefix2 + 'cls: { type: &%s }, \n' % (
                      self.getStructType(config) ) )

        # XXX more here?
        fh.write( prefix + '}' + suffix )

    def validate_namefield(self, config):
        pass


class VerbatimParameter(Parameter):
    """Represents a single verbatim parameter"""

    def __init__(self, verbatim_text=None):
        assert verbatim_text == str(verbatim_text)
        self.verbatim_text = verbatim_text
        Parameter.__init__(self)

    def copy(self):
        new = Parameter.copy(self)
        new.verbatim_test = self.verbatim_text
        return new

    def dump(self, prefix = ''):
        """Dump the member data to the screen."""
        print prefix + "Verbatim parameter: \"" + \
                str(self.verbatim_text) + "\""

    def printStructEntryDeclare(self, fh, prefix = '', suffix = '\n'):
        """Print verbatim structure declaration."""
        fh.write(prefix + "%s" %
                 (self.verbatim_text) + suffix)

    def printDefaultsDeclare(self, fh, prefix = '', suffix = ''):
        """Print verbatim default declaration -- NONE."""
        return

    def printIntrospectDeclare(self, fh, config, parent_struct=None,
                               prefix = '', suffix = ''):
        """Print verbatim introspec declaration -- NONE."""
        return

class DDirParameter(Parameter):
    """Represents a single dynamic dir parameter"""

    def __init__(self):
        self.ddir_type = None
        self.ddir_nav_field = None
        self.ddir_save_field = ""
        Parameter.__init__(self)
        self.reference = REFERENCE_DDIR

    def copy(self):
        new = Parameter.copy(self)
        new.ddir_type = self.ddir_type
        new.ddir_nav_field = self.ddir_nav_field
        new.ddir_save_field = self.ddir_save_field
        return new

    def has_reference_to_dir(self):
        """Either a dir reference, or a ddir of dirs"""
        return True # XXX Needs to be fixed for ddirs of basic types

    def setDDirNavField(self, ddir_nav_field):
        """Note what dir field to use as the navigation field"""
        assert self.ddir_nav_field == None
        assert ddir_nav_field == str(ddir_nav_field)
        self.ddir_nav_field = ddir_nav_field

    def setDDirSaveField(self, ddir_save_field):
        """Note what field to use as the .gc savefile directory name"""
        assert self.ddir_save_field == ""
        assert ddir_save_field == str(ddir_save_field)
        self.ddir_save_field = ddir_save_field

    # Only works after the dir is defined.
    def template_class(self, config):
        return self.dir.c_class

    # Only works after the dir is defined.
    def introspect_prefix(self, config):
        return self.dir.introspect_prefix

class DDirLinkParameter():
    """Represents one of the available link macros (e.g. SLIST_ENTRY)"""

    def __init__(self):
        self.parent_dir = None
        self.link_type = None
        self.link_name = None
        self.name = None

    def copy(self):
        new = self.__class__()
        new.parent_dir = self.parent_dir
        new.link_type = self.link_type
        new.link_name = self.link_name
        new.name = self.name
        return new

    def fix_version(self, current_version, version_enum, dirs):
        pass

    def needs_new_version(self, current_version, target_version,version_enum,
            dirs):
       pass

    def setLinkType(self, type):
        # XXX validation
        self.link_type = type

    def setLinkName(self, name):
        # XXX validation?
        self.link_name = name

    def set_parent_dir(self, dir):
        # XXX validation
        self.parent_dir = dir

    def has_reference_to_dir(self):
        """Either a dir reference, or a ddir of dirs"""
        return False

    # Some functions to allow us to quack like a Parameter
    def printStructPreDeclare(self, fh, config, prefix = ''):
        pass

    def set_default_location(self, location):
        pass

    def printDDirIterDeclare(self, fh, config, prefix = ''):
        pass

    def validate_namefield(self, config):
        pass

    def printStructEntryDeclare(self, fh, prefix = '', suffix = ';\n'):
        """Print dynamic-dir structure-entry declaration."""
        fh.write(prefix + "%s(%s) %s" %
                 (self.link_type, self.parent_dir.mangled_name(),
                 self.link_name) + suffix)

    def printDefaultsDeclare(self, fh, prefix = '', suffix = ''):
        """Print verbatim default declaration -- NONE."""
        return

    def printIntrospectDeclare(self, fh, config, parent_struct=None,
                               prefix = '', suffix = ''):
        """Print verbatim introspec declaration -- NONE."""
        return

class IsiVectorDDirParameter(DDirParameter):

    def ddir_symbol_name(self):
        return "%s%s" % (INTROSPECT_DDIR_PREFIX, self.dir.mangled_name())

    def vec_type(self):
        return "%s_vec" % self.dir.mangled_name()

    def getDefaultsMergeable(self):
        return "false"

    def array_name(self):
        return "%s_array" % self.dir.mangled_name()

    def equal_fn(self):
        if self.kvp and KVP_KEY_EQUALS_FN in self.kvp:
            return self.kvp(KVP_KEY_EQUALS_FN)
        else:
            return None
    def fmt(self):
        if self.kvp and KVP_KEY_FMT in self.kvp:
            return self.kvp(KVP_KEY_FMT)
        else:
            return None

    def printStructPreDeclare(self, fh, config, prefix = ''):
        """Print stuff that needs to come before the struct
        declaration"""
        make_symbol = self.vec_type()
        if config.hasSymbol(make_symbol):
            return
        config.addSymbol(make_symbol)
        fh.write(prefix + "ISI_VECTOR_DECLARE(%s, %s, %s, 0, %s);\n" %
                 (self.vec_type(), self.getRichType(), self.array_name(),
                 self.vec_type()))
        fh.write(
'''#define %(upper_prefix)s_FOREACH VECTOR_FOREACH
#define %(upper_prefix)s_REVERSE VECTOR_FOREACH_REVERSE
#define %(upper_prefix)s_INITIALIZER VECTOR_INITIALIZER
#define %(upper_prefix)s_INIT_CLEAN(v) v __cleanup(%(lower_prefix)s_clean) = {}
'''
                % {"upper_prefix": self.vec_type().upper(),
                   "lower_prefix": self.vec_type(),
                })

    def printStructEntryDeclare(self, fh, prefix = '', suffix = ';\n'):
        """Print dynamic-dir structure-entry declaration."""
        fh.write(prefix + "struct %s\t%s" %
                 (self.vec_type(), self.getRichName()) + suffix)

    def printDDirIterDeclare(self, fh, config, prefix = ''):
        """Print dynamic-dir iterator info for this slist var
        (prefix ignored)"""

        make_symbol = self.ddir_symbol_name()
        if config.hasSymbol(make_symbol):
            return
        config.addSymbol(make_symbol)

        fh.write(
"""
ISI_VECTOR_DEFINE(%(Headtype)s, %(Headtype)s, NULL);

static void
%(Symbol)s_init(void *head_p,
    struct isi_error **error_out)
{
	struct %(Headtype)s *head = head_p;
	%(Headtype)s_init(head);
}
static void
%(Symbol)s_destroy(void *head_p,
    struct isi_error **error_out)
{
	struct %(Headtype)s *head = head_p;

	%(Headtype)s_clean(head);
}
static void
%(Symbol)s_cleanout(void *head_p,
    struct isi_error **error_out)
{
	struct %(Headtype)s *head = head_p;

	%(Headtype)s_clean(head);
}
static void
%(Symbol)s_insert(void *head_p, void *prior_elm_p, const char *name,
    void *elm_p,
    struct isi_error **error_out)
{
	struct %(Headtype)s *head = head_p;
	struct %(Elmtype)s *elm = elm_p;

	%(Headtype)s_insert(head, %(Headtype)s_len(head), elm);

	/* We only ever insert at the end */
}
static void
%(Symbol)s_remove(void *head_p, const char *elm_name, void *elm_p,
    struct isi_error **error_out)
{
	struct %(Headtype)s *head = head_p;
	struct %(Elmtype)s *elm = elm_p;
	struct isi_error *error = NULL;
	bool found;

	found = %(Headtype)s_remove_element(head, elm);
	if (!found) {
		error = gci_error_new("Could not remove; element not found");
		goto out;
	}
out:
	isi_error_handle(error, error_out);
}
static bool
%(Symbol)s_first(void *head_p, const char **elm_name_out, void **elm_out_pp,
    struct isi_error **error_out)
{
	struct %(Headtype)s *head = head_p;
	struct %(Elmtype)s **elm = (void*)elm_out_pp;
	if (%(Headtype)s_len(head) == 0)
		*elm = NULL;
	else
		*elm = head->%(array_name)s[0];
	return (*elm ? true : false);
}
static bool
%(Symbol)s_next(void *head_p, void *elm_p, const char **next_name_out,
    void **next_out_pp, struct isi_error **error_out)
{
	struct %(Headtype)s *head = head_p;
	struct %(Elmtype)s *elm = elm_p;
	struct %(Elmtype)s **next = (void*)next_out_pp;
	struct isi_error *error = NULL;
	int index;
	bool found;
	found = %(Headtype)s_find(head, elm, &index);
	if (!found) {
		error = gci_error_new("Could not get next; previous element "
		    "not found");
		goto out;
	} else {
		if (index + 1 >= %(Headtype)s_len(head))
			*next = NULL;
		else
			*next = head->%(array_name)s[index+1];
	}
out:
	isi_error_handle(error, error_out);
	return (*next ? true : false);
}
static struct gci_ddir %(Symbol)s = {
	init     : &%(Symbol)s_init,
	destroy  : &%(Symbol)s_destroy,
	cleanout  : &%(Symbol)s_cleanout,
	insert   : &%(Symbol)s_insert,
	remove   : &%(Symbol)s_remove,
	first    : &%(Symbol)s_first,
	next     : &%(Symbol)s_next,
	head_inline_in_parent: true,
	head_size: sizeof(struct %(Headtype)s),
	defaults_mergeable : %(defaults_mergeable)s,
	pytype : %(gci_pytype_enum)s,
	template_class : %(template_class)s,
	template : %(Template)s,
	save_field_str : \"%(Savefield)s\",
};

"""
                % { "Symbol": self.ddir_symbol_name(),
                    "Headtype": self.vec_type(),
                    "Elmtype": self.dir.mangled_name(),
                    "template_class": self.template_class(config),
                    "Template": "&%s%s" % \
                            (self.introspect_prefix(config),
                             self.dir.mangled_name()),
                    "Savefield": self.ddir_save_field,
                    "fmt": self.fmt() or self.ddir_symbol_name()+"_fmt",
                    "equal": self.equal_fn() or \
                            self.ddir_symbol_name()+"_equal",
                    "array_name": self.array_name(),
                    "percent_d": "%d",
                    "gci_pytype_enum": self.pytype,
                    "defaults_mergeable": self.getDefaultsMergeable(),
                })

class IsiStrmapDDirParameter(DDirParameter):

    def ddir_symbol_name(self):
        return "%smap_%s" % (INTROSPECT_DDIR_PREFIX, self.dir.mangled_name())

    def vec_type(self):
        return "%s_strmap" % self.dir.mangled_name()

    def getDefaultsMergeable(self):
        return "true"

    def equal_fn(self):
        if self.kvp and KVP_KEY_EQUALS_FN in self.kvp:
            return self.kvp(KVP_KEY_EQUALS_FN)
        else:
            return None
    def fmt(self):
        if self.kvp and KVP_KEY_FMT in self.kvp:
            return self.kvp(KVP_KEY_FMT)
        else:
            return None

    def printStructPreDeclare(self, fh, config, prefix = ''):
        """Print stuff that needs to come before the struct
        declaration"""
        make_symbol = self.vec_type()
        if config.hasSymbol(make_symbol):
            return
        config.addSymbol(make_symbol)
        fh.write(prefix + "ISI_STRMAP_DECLARE(%s, %s, %s);\n" %
                 (self.vec_type(), self.getRichType(), self.vec_type()))
        fh.write(
'''#define %(upper_prefix)s_FOREACH(key, val, map) ISI_STRMAP_FOREACH(key, \
    val, map, %(lower_prefix)s, %(lower_prefix)s)
'''
                % {"upper_prefix": self.vec_type().upper(),
                   "lower_prefix": self.vec_type(),
                })

    def printStructEntryDeclare(self, fh, prefix = '', suffix = ';\n'):
        """Print dynamic-dir structure-entry declaration."""
        fh.write(prefix + "struct %s\t%s" %
                 (self.vec_type(), self.getRichName()) + suffix)

    def printDDirIterDeclare(self, fh, config, prefix = ''):
        """Print dynamic-dir iterator info for this slist var
        (prefix ignored)"""

        make_symbol = self.ddir_symbol_name()
        if config.hasSymbol(make_symbol):
            return
        config.addSymbol(make_symbol)

        fh.write(
"""
ISI_STRMAP_DEFINE(%(Structname)s, %(Structname)s);

static void
%(Symbol)s_init(void *map_p,
    struct isi_error **error_out)
{
	struct %(Structname)s *map = map_p;
	%(Structname)s_init(map);
}
static void
%(Symbol)s_destroy(void *map_p,
    struct isi_error **error_out)
{
	struct %(Structname)s *map = map_p;

	%(Structname)s_free(map);
}
static void
%(Symbol)s_cleanout(void *map_p,
    struct isi_error **error_out)
{
	const char *key = NULL;
	%(Elmtype)s elm = NULL;
	struct %(Structname)s *map = map_p;

	%(upper_prefix)s_FOREACH(key, elm, map)
		%(Structname)s_remove(map, key);
}
static void
%(Symbol)s_insert(void *head_p,
    void *prior_elm_p, const char *name, void *elm_p,
    struct isi_error **error_out)
{
	struct %(Structname)s *head = head_p;
	%(Elmtype)s elm = elm_p;

	/* we don't check for errors / duplicates */

	%(Structname)s_set(head, name, elm);
}
static void
%(Symbol)s_remove(void *head_p,
    const char *name, void *elm_p, struct isi_error **error_out)
{
	struct %(Structname)s *head = head_p;
	%(Elmtype)s elm = elm_p;
	const char *key = NULL;
	struct isi_error *error = NULL;
	bool found = false;

	key = _%(Structname)s_hack_maybe_stealkey(head, &elm);
	ASSERT(key != NULL,
	    "Illegal use of strmap ddir->remove with active iterator");
	found = %(Structname)s_remove(head, key);
	if (!found) {
		error = gci_error_new("Could not remove; element not found");
		isi_error_handle(error, error_out);
	}
}
static bool
%(Symbol)s_first(void *head_p,
    const char **name_out, void **elm_out_pp,
    struct isi_error **error_out)
{
	struct %(Structname)s *head = head_p;
	%(Elmtype)s *elm = (void *)elm_out_pp;
	const struct %(Structname)s_iter_elm *iter_elm = NULL;
	struct isi_error *error = NULL;

	if (%(Structname)s_is_empty(head)) {
		*elm = NULL;
		*name_out = NULL;
	} else {
		%(Structname)s_iter_init(head);
		iter_elm = %(Structname)s_iter_next(head, &error);
		if (error) {
			isi_error_handle(error, error_out);
			*elm = NULL;
			*name_out = NULL;
		} else {
			*elm = *iter_elm->val;
			*name_out = iter_elm->key;
		}
	}
	return (*elm ? true : false);
}
static bool
%(Symbol)s_next(void *head_p,
    void *elm_p, const char **next_name_out, void **next_out_pp,
    struct isi_error **error_out)
{
	struct %(Structname)s *head = head_p;
	%(Elmtype)s *next = (void*)next_out_pp;
	const struct %(Structname)s_iter_elm *iter_elm = NULL;
	struct isi_error *error = NULL;

	iter_elm = %(Structname)s_iter_next(head, &error);
	if (error) {
		*next = NULL;
		goto out;
	}
	if (iter_elm->val == NULL) {
		*next = NULL;
		*next_name_out = NULL;
	} else {
		*next = *iter_elm->val;
		*next_name_out = iter_elm->key;
	}

out:
	if (error)
		isi_error_handle(error, error_out);
	return (*next ? true : false);
}
static struct gci_ddir %(Symbol)s = {
	init     : &%(Symbol)s_init,
	destroy  : &%(Symbol)s_destroy,
	cleanout  : &%(Symbol)s_cleanout,
	insert   : &%(Symbol)s_insert,
	remove   : &%(Symbol)s_remove,
	first    : &%(Symbol)s_first,
	next     : &%(Symbol)s_next,
	head_inline_in_parent: true,
	head_size: sizeof(struct %(Structname)s),
	defaults_mergeable : %(defaults_mergeable)s,
	pytype : %(gci_pytype_enum)s,
	template_class : %(template_class)s,
	template : %(Template)s,
	save_field_str : \"%(Savefield)s\",
};

"""
                % { "Symbol": self.ddir_symbol_name(),
                    "Structname": self.vec_type(),
                    "Elmtype": self.getRichType(),
                    "template_class": self.template_class(config),
                    "Template": "&%s%s" % \
                            (self.introspect_prefix(config),
                             self.dir.mangled_name()),
                    "Savefield": self.ddir_save_field,
                    "fmt": self.fmt() or self.ddir_symbol_name()+"_fmt",
                    "equal": self.equal_fn() or \
                            self.ddir_symbol_name()+"_equal",
                    "percent_d": "%d",
                    "upper_prefix": self.vec_type().upper(),
                    "gci_pytype_enum": self.pytype,
                    "defaults_mergeable": self.getDefaultsMergeable(),
                })

class SlistDDirParameter(DDirParameter):

    def ddir_symbol_name(self):
        # beware nav_field and save_field may be None still
        # XXX probably ought to error out in that case (at least for nav_field)
        return "%s%s_%s_%s" % \
                (INTROSPECT_DDIR_PREFIX, self.dir.mangled_name(),
                 self.ddir_nav_field, self.ddir_save_field)

    def head_type(self):
        return "%s_head" % self.dir.mangled_name()

    def getDefaultsMergeable(self):
        if self.kvp is not None and KVP_KEY_NAME in self.kvp.dict:
                return "true"
        else:
                return "false"

    def printStructPreDeclare(self, fh, config, prefix = ''):
        """Print stuff that needs to come before the struct declaration"""
        make_symbol = self.head_type()
        if config.hasSymbol(make_symbol):
            return
        config.addSymbol(make_symbol)

        fh.write(prefix + "SLIST_HEAD(%s, %s);\n" %
                 (self.head_type(), self.dir.mangled_name()))

    def printStructEntryDeclare(self, fh, prefix = '', suffix = ';\n'):
        """Print dynamic-dir structure-entry declaration.
        This is an inline SLIST_HEAD declaration."""
        fh.write(prefix + "struct %s\t%s" %
                 (self.head_type(), self.getRichName()) + suffix)

    def printDDirIterDeclare(self, fh, config, prefix = ''):
        """Print dynamic-dir iterator info for this slist var
        (prefix ignored)"""

        make_symbol = self.ddir_symbol_name()
        if config.hasSymbol(make_symbol):
            return
        config.addSymbol(make_symbol)

        fh.write(
"""static void
%(Symbol)s_init(void *head_p,
    struct isi_error **error_out)
{
	struct %(Headtype)s *head = head_p;
	SLIST_INIT(head);
}
static void
%(Symbol)s_destroy(void *head_p,
    struct isi_error **error_out)
{
	return;
}
static void
%(Symbol)s_cleanout(void *head_p,
    struct isi_error **error_out)
{
	return;
}
static void
%(Symbol)s_insert(void *head_p,
    void *prior_elm_p, const char *name, void *elm_p,
    struct isi_error **error_out)
{
	struct %(Headtype)s *head = head_p;
	struct %(Elmtype)s *elm = elm_p;
	struct %(Elmtype)s *prior_elm = prior_elm_p;
	if (prior_elm)
		SLIST_INSERT_AFTER(prior_elm, elm, %(Navfield)s);
	else
		SLIST_INSERT_HEAD(head, elm, %(Navfield)s);
}
static void
%(Symbol)s_remove(void *head_p, const char *elm_name, void *elm_p,
    struct isi_error **error_out)
{
	struct %(Headtype)s *head = head_p;
	struct %(Elmtype)s *elm = elm_p;
	SLIST_REMOVE(head, elm, %(Elmtype)s, %(Navfield)s);
}
static bool
%(Symbol)s_first(void *head_p, const char **elm_name_out, void **elm_out_pp,
    struct isi_error **error_out)
{
	struct %(Headtype)s *head = head_p;
	struct %(Elmtype)s **elm = (void*)elm_out_pp;
	*elm = SLIST_FIRST(head);
	return (*elm ? true : false);
}
static bool
%(Symbol)s_next(void *head_p, void *elm_p, const char **next_name_out,
    void **next_out_pp, struct isi_error **error_out)
{
	struct %(Elmtype)s *elm = elm_p;
	struct %(Elmtype)s **next = (void*)next_out_pp;
	*next = SLIST_NEXT(elm, %(Navfield)s);
	return (*next ? true : false);
}
static struct gci_ddir %(Symbol)s = {
	init     : &%(Symbol)s_init,
	destroy  : &%(Symbol)s_destroy,
	cleanout  : &%(Symbol)s_cleanout,
	insert   : &%(Symbol)s_insert,
	remove   : &%(Symbol)s_remove,
	first    : &%(Symbol)s_first,
	next     : &%(Symbol)s_next,
	head_inline_in_parent: true,
	head_size: sizeof(struct %(Headtype)s),
	defaults_mergeable : %(defaults_mergeable)s,
	pytype : %(gci_pytype_enum)s,
	template_class : %(template_class)s,
	template : %(Template)s,
	save_field_str : \"%(Savefield)s\",
};

"""
                % { "Symbol": "%s" % self.ddir_symbol_name(),
                    "Headtype": "%s" % self.head_type(),
                    "Elmtype": "%s" % self.dir.mangled_name(),
                    "Navfield": "%s" % self.ddir_nav_field,
                    "template_class": self.template_class(config),
                    "Template": "&%s%s" % \
                            (self.introspect_prefix(config),
                             self.dir.mangled_name()),
                    "Savefield": "%s" % self.ddir_save_field,
                    "gci_pytype_enum": self.pytype,
                    "defaults_mergeable": self.getDefaultsMergeable(),
                });

    def validate_namefield(self, config):
        if self.ddir_save_field:
            potentials = [var for var in self.dir.vars if var.name ==
                    self.ddir_save_field]
            assert len(potentials) == 1, \
                    "Missing or duplicate name field in %s" % \
                            self.dir.mangled_name()


class StailqDDirParameter(DDirParameter):

    def printStructEntryDeclare(self, fh, prefix = '', suffix = ';\n'):
        """Print dynamic-dir structure-entry declaration."""
        fh.write(prefix + "%s\t%s" %
                 ("STAILBLAH", self.getRichName()) + suffix)

    def printDDirIterDeclare(self, fh, config, prefix = ''):
        """Print dynamic-dir iterator info for this var"""
        fh.write( prefix + "%s\tSTAILGOBBLEDYGOOK\n" ) #XXX

def makeNewDDirParam(ddir_type):
    if ddir_type == "SLIST":
        return SlistDDirParameter()
    elif ddir_type == "STAILQ":
        return StailqDDirParameter()
    elif ddir_type == "ISI_VECTOR":
        return IsiVectorDDirParameter()
    elif ddir_type == "ISI_STRMAP":
        return IsiStrmapDDirParameter()

class Item:
    """Pure virtual class to hold common code for %tree and %type.
    Expects subclass to have a type_name, a required_keys,
    and an optional_keys"""

    def __init__(self):
        self.options = {}

    def setName(self, name):
        self.name = name

    def addOption(self, key, value):
        self.options[key] = value

    def validate(self):
        """Validates against specific class's required_keys
        and options_keys"""

        for key in self.required_keys:
            assert key in self.options, "%s missing %s" % (self.name, key)
        for key in self.options.keys():
            assert key in self.required_keys or key in self.optional_keys, \
                    "unrecognized option '%s' in '%s'" % (key, self.name)

    def output_to_files(self, files, config, dest_dir):
        """Currently just debug output"""
        to_write = "/*\n"
        to_write +=' * %s %s\n' % (self.type_name, self.name)
        for (key, value) in self.options.iteritems():
            to_write += " * %s = %s\n" % (key, value)
        to_write += " */\n"
        files[CFG_OUT_HEADER].filehandle.write(to_write)

class Tree(Item):
    """Represents a %tree"""

    type_name = "Tree"

    # The root is the root directory for the tree.
    ROOT_KEY = "root"

    required_keys = (ROOT_KEY,)

    # The display name is the name used for looking up the tree.
    # self.name is the symbol for the tree.
    DISPLAY_NAME_KEY = "display_name"

    # If a tree is hidden, then it can not be looked up.
    HIDDEN_KEY = "hidden"

    optional_keys = (DISPLAY_NAME_KEY,
                     "primary_config",
                     "fallback_config_ro",
                     "change_log_file",
                     "defaults_config_ro",
                     "mode",
                     "keep_files",
                     HIDDEN_KEY,
                    )

    # If a mode is not specified, default to 0640
    DEFAULT_MODE = "0640"

    def __init__(self):
        Item.__init__(self)
        self.addOption("mode", self.DEFAULT_MODE)

    def output_to_files(self, files, config, dest_dir):
        """Here we make a gci_tree"""

        # First the Header

        header = files[CFG_OUT_HEADER].filehandle
        header.write("extern struct gci_tree *%s;\n" % self.name)

        # Now the .c file

        # If there is no display name, then use the symbol name
        if self.DISPLAY_NAME_KEY not in self.options:
            self.options[self.DISPLAY_NAME_KEY] = '"%s"' % self.name

        source = files[CFG_OUT_SOURCE].filehandle
        source.write("static struct gci_tree _%s = { \n" % self.name)
        for (key, value) in self.options.iteritems():
            if key == self.HIDDEN_KEY:
                continue
            source.write("\t.%s = %s%s,\n" %
                    (key, "&gci_ivar_" if key == self.ROOT_KEY else "", value))
        source.write('\t.symbol_name = "%s",\n' % self.name)
        source.write("};\n")
        source.write("struct gci_tree *%s = &_%s;\n" % ((self.name,) * 2))

        if not (self.HIDDEN_KEY in self.options and
            self.options[self.HIDDEN_KEY].lower() == "true"):
                tree_file_name = self.name + ".tree"
                # XXX: style??
                files[tree_file_name] = tree = FileInfo(tree_file_name,
                                                        dest_dir)
                tree.filehandle.write(
                        "%s:%s:%s" %
                        (self.options[self.DISPLAY_NAME_KEY].replace('"', ''),
                        self.name, "lib%s.so" % library_name))

class Type(Item):
    """Represents a %type"""

    type_name = "Type"
    required_keys = ("print_fmt",
                     "scan_fmt",
                    )
    optional_keys = ("free",
                     "copy",
                     "init",
                     "equals",
                    )

    def output_to_files(self, files, config, dest_dir):
        """Here we make a gci_type"""

        # Convert our actual type (e.g. struct s *) to a name which
        # is a valid c identifier (e.g. struct_sp) and strip the prefix
        c_name = Type2Symbol(self.name).lstrip(INTROSPECT_TYPE_PREFIX)
        nop_free = "_gci_nop_free_%s" % (c_name)
        generic_copy = "_gci_generic_copy_%s" % (c_name)
        generic_equal = "_gci_generic_equal_%s" % (c_name)
        nop_init = "_gci_nop_init_%s" % (c_name)
        inline = not '*' in self.name

        # First the Header
        header = files[CFG_OUT_HEADER].filehandle
        header.write('extern struct gci_type %s%s;\n' %
                     (INTROSPECT_TYPE_PREFIX, c_name))

        if not 'free' in self.options:
            header.write(
                    "void %s(%s *x);\n" % (nop_free, self.name))

        if not 'copy' in self.options:
            header.write(
                    "void %s(%s *x, %s *y);\n" %
                    (generic_copy, self.name, self.name))

        if not 'equals' in self.options:
            header.write(
                    "bool %s(%s *x, %s *y);\n" %
                    (generic_equal, self.name, self.name))

        if not 'init' in self.options:
            header.write("void %s(%s " % (nop_init, self.name))
            if inline:
                header.write("*")
            header.write("x);\n")


        # Now the .c file 
        source = files[CFG_OUT_SOURCE].filehandle

        if not 'free' in self.options:
            source.write("void %s(%s *x){}\n" % (nop_free, self.name))

        if not 'copy' in self.options:
            source.write("void %s(%s *x, %s *y)\n "
                         "{\n"
                         "\tmemcpy(x, y, sizeof(%s));\n"
                         "}\n" %
                         (generic_copy, self.name, self.name, self.name))

        if not 'equals' in self.options:
            source.write("bool %s(%s *x, %s *y)\n "
                         "{\n"
                         "\treturn !memcmp(x, y, sizeof(%s));\n"
                         "}\n" %
                         (generic_equal, self.name, self.name, self.name))

        if not 'init' in self.options:
            source.write("void %s(%s "  % (nop_init, self.name))
            if inline:
                source.write("*")
            source.write("x){};\n")

        source.write('GCI_DEEP_TYPE')

        if inline:
            source.write('_INLINE')

        source.write('(%s, %s, %s, %s, %s, %s, %s, %s, %s)\n\n' %
                     (self.name, self.name, c_name,
                     self.options['print_fmt'], self.options['scan_fmt'],
                     self.options.get('free', nop_free),
                     self.options.get('copy', generic_copy),
                     self.options.get('equals', generic_equal),
                     self.options.get('init', nop_init)))

#
# Customized parser
#

class ExtendedConfigParser(ConfigParser):
    """Customized config parser"""

    def abort(self, message):
        """Raise an exception with the current context information."""
        raise SyntaxError(self._scanner.pos, message)


#
# Published parsing function for config definition files
#

def parseConfigDefs(text):
    """Parse the config and return an intermediate representation."""
    # This function was modeled after parse() (in this module) and
    # yappsrt.wrap_error_reporter().

    # Instantiate the parser.
    parser = ExtendedConfigParser(ConfigParserScanner(text))

    try:
        # Attempt to parse the text.
        return parser.config()

    except SyntaxError, e:
        # Print out the part of the input that we had been scanning.
        input = parser._scanner.input
        print_error(input, e, parser._scanner)
        sys.exit('Aborting')

    except NoMoreTokens, e:
        print 'Could not complete parsing; stopped around here:'
        print parser._scanner
        sys.exit('Aborting')

    except Exception, e:
        # We will assume that there was a syntactic problem in the
        # input, and gather all the relevant debugging information.

        # Query for the scanner's last known context.
        pos = parser._scanner.pos
        input = parser._scanner.input

        # Generate a bogus syntax error so that we have something to
        # pass to the print_error utility.
        s = SyntaxError(pos, 'Exception raised')

        # Print out the debugging information.
        print_error(input, s, parser._scanner)

        # Propagate the exception.
        raise



############################################################
#
# Utility functions
#
############################################################

# Write out the data if it's different than what's already in the file.
def write_file_if_different(filename, temp_f):
    temp_f.flush()

    if not os.path.exists(filename) or \
        not filecmp.cmp(filename, temp_f.name) or \
        force_write:
            if verbose:
                sys.stdout.write('Writing new %s\n' % filename)
            os.rename(temp_f.name, filename)
    elif verbose:
        sys.stdout.write('Generated %s is unchanged\n' % filename)


    # Python is going to try unlink() on the temp file.
    try:
        temp_f.close()
    except OSError:
        pass

class FileInfo:
    """Container for a file, including temporary, permanent, """ \
    """and canonical name details"""

    def __init__(self, finalname, dest_dir):
        self.finalname = finalname
        if dest_dir:
            self.fullpath = os.path.join(dest_dir, self.finalname)
        else:
            self.fullpath = self.finalname
        self.name  = os.path.basename(self.finalname)
        self.filehandle = tempfile.NamedTemporaryFile(
                dir=os.path.dirname(self.fullpath))
        os.chmod(self.filehandle.name, 0644)

    def symbolname(self):
        """Return the filename usable as a C symbol"""
        return "_" + self.name.upper().replace('.','_')

def old_getfile(files, which):
    return files[which].filehandle

def openFiles(out_source=None, out_header=None, dest_dir=None):
    """Open file objects for all generated files."""
    files = {}

    files[CFG_OUT_SOURCE] = FileInfo(out_source, dest_dir)
    files[CFG_OUT_HEADER] = FileInfo(out_header, dest_dir)

    return files

def closeFiles(files):
    """Close all file objects in the given dictionary."""
    for file in files.values():
        write_file_if_different(file.fullpath, file.filehandle)

def escape(string):
    """Escape the characters necessary to generate a valid c string."""
    escape_dict = {'"':'\\"', '\\':'\\\\','\n':'\\n'}
    escaped_string = ''

    for char in string:
        escaped_string += escape_dict.get(char,char)

    return escaped_string


############################################################
#
# C code generation functions
#
############################################################

def generate_all(files, config, dest_dir):
    """Generate all the C files."""

    # Write the introductory information to each file.
    generate_header_intro(files[CFG_OUT_HEADER])
    generate_source_intro(files[CFG_OUT_SOURCE],
                          files[CFG_OUT_HEADER].finalname)

    # Put forward declarations of structs so that they can reference each
    # other
    config.printForwardDeclare(files[CFG_OUT_HEADER].filehandle, '')
    # And put forward extern declarations of structs so that they can
    # link to each other
    config.printExternDeclare(files[CFG_OUT_HEADER].filehandle,
                              print_global=True, prefix='')
    config.printExternDeclare(files[CFG_OUT_SOURCE].filehandle,
                              print_global=False, prefix='')

    # And make sure any extra declarations are there (ie enum helper macros)
    config.printExtraHeaders(files[CFG_OUT_SOURCE].filehandle,
                             files[CFG_OUT_HEADER].filehandle)

    # Loop through all the configuration entities in order
    for elem in config.ordered:
        # elem is a list of Dirs, an Enum, or a Copy.
        if isinstance(elem, list):
            # Dirs come in sub-lists
            for ent in elem:
                ent.output_to_files(files, config, dest_dir)
        else:
            elem.output_to_files(files, config, dest_dir)
    # Write summary information to the header.
    generate_header_end(files[CFG_OUT_HEADER])

def generate_header_intro(file):
    """Write the introductory information to the header file."""
    file.filehandle.write(
r"""/*
 * File: %s
 * Auto-generated from %s using %s
 *
 * Copyright (c) 2009 - 2009
 * Isilon Systems, Inc.  All rights reserved.
 */

#ifndef %s
#define %s

#include "isi_gconfig/gconfig.h"

#ifdef __cplusplus
extern "C" {
#endif

"""
            % (file.name,
               sourcefile,
               execname,
               file.symbolname(),
               file.symbolname(),
            ))
#
## perhaps also some sort of  #include <isi_gconfig/isi_gcfg_types.h> /*XXX needed? */

def generate_header_end(file):

    """Write the final lines to the header file."""
    file.filehandle.write(
r"""
#ifdef __cplusplus
}
#endif

#endif /* %s */
"""
            % file.symbolname())

def generate_source_intro(file, h_file_name):
    """Write the introductory information to the source file."""
    file.filehandle.write(
r"""/*
 * File: %s
 * Auto-generated from %s using %s
 *
 * Copyright (c) 2009 - 2009
 * Isilon Systems, Inc.  All rights reserved.
 */

#include <stddef.h>
#include "isi_gconfig/gconfig_internal.h"
#include "%s"

"""
            % (file.name,
               sourcefile,
               execname,
               h_file_name,
            ))



############################################################
#
# Main function
#
############################################################


def show_usage(extra_msg=None):
    usage_msg = """
Usage: %s <infile.gcfg>
    Parse <infile.gcfg> and generate <infile.c> and <infile.h>
""" % (execname)

    if (extra_msg):
            print "%s\n" % extra_msg
    print usage_msg


def main():
    """Parse the global-config definiton and generate H and C files."""

    # Parse the command line options.
    global execname
    execname = sys.argv[0]
    (optlist, args) = getopt.getopt(sys.argv[1:], 'vfs:d:l:h',
                                    ['verbose', 'force-write', 'dest-dir=',
                                    'suffix=', 'lib=', 'help'])

    global verbose, force_write, library_name
    library_name = None
    in_file = None
    dest_dir = None
    suffix = "_gcfg"
    for (option, value) in optlist:
        if option in ('-v', '--verbose'):
            verbose = True
        if option in ('-f', '--force-write'):
            force_write = True
        if option in ('-d', '--dest-dir'):
            dest_dir = value
        if option in ('-l', '--lib'):
            library_name = value
        if option in ('-h', '--help'):
            show_usage()
            sys.exit(0)

    if not args or len(args) != 1:
        show_usage()
        sys.exit(1)

    # Parse and validate first arg as config-file name
    in_file = args[0]
    if in_file[-5:]!='.gcfg':
        show_usage("Error: input file must end in .gcfg")
        sys.exit(1)
    cfg_out_root = os.path.basename(in_file[:-5])
    if (cfg_out_root[-5:] == "_gcfg"):
        cfg_out_root = cfg_out_root[:-5]
    cfg_out_source = cfg_out_root + suffix + '.c'
    cfg_out_header = cfg_out_root + suffix + '.h'
    global sourcefile
    sourcefile = in_file

    # Read the global-config definition file.
    if verbose:
        print "Parsing %s -> %s, %s" % \
                (in_file, cfg_out_source, cfg_out_header)
    master = open(in_file)
    defs = master.read()
    master.close()

    # Parse the config-definition into the intermediate representation.
    cdef = parseConfigDefs(defs)

    if verbose:
        cdef.dump()

    # Open all the files.
    files = openFiles(out_source=cfg_out_source, out_header=cfg_out_header,
                      dest_dir=dest_dir)

    # Generate all the C files.
    generate_all(files, cdef, dest_dir)

    # Close all the files.
    closeFiles(files)

############################################################
#
# Main function invocation
#
############################################################

if __name__ == '__main__':
    try:
        main()
    except:
        # XXX: Uncomment two lines below to drop into debugger on error
        # tb = sys.exc_info()[2]
        # pdb.post_mortem(tb)
        raise
