"""Module for processing TCG TPM2 library object descriptions. The descriptions are scraped from the tables of parts 2 and 3 of the specification by a different module and fed through this module for processing. """ from __future__ import print_function import re import sys from command_generator import Command from structure_generator import AttributeStructure from structure_generator import ConstantType from structure_generator import Field from structure_generator import Interface from structure_generator import Structure from structure_generator import Typedef from structure_generator import Union def _DebugLog(*args, **kwargs): """When used - sends its inputs to stderr. This function can be used when debugging this module. Its footprint is similar to print(), but the default destination is sys.stderr, which is handy when the script generates stdio output redirected into a file. Args: *args: a list of items of various types to print. Printed space separated, each one converted to str before being printed. **kwargs: a dictionary of variables to pass to print(), if any. In fact the only key this function cares about is 'endl', which allows to suppress adding a newline to the printed string. """ endl = kwargs.get('endl', '\n') print(' '.join(str(x) for x in args), end=endl, file=sys.stderr) class Table(object): """Representation of TCG TPM2 library specification tables. The purpose of this class is to both generate new TPM2 objects and to keep track of the previously generated objects for post processing (generating C code). The HTML scraper finds tables in the specifications and builds up the tables' contents in this object, one at a time. This object's table representation includes table title, table header and one or more then table rows. The table title must start with 'Table ### xxx', where ### is monotonously increasing table number and xxx is some description allowing to deduce the type of the object defined by this table. The cells of the table include names and types of the components, various decorations are used to convey additional information: array boundaries, values' limits, return values, selectors, etc, etc. Once the entire table is scraped, the scraper invokes a method to process it (ProcessTable). The title of the table is examined by this module and the appropriate processing method is invoked to actually convert the internal table representation into a TPM2 object. Two maps are maintained persistently over the life time of this object, the map of types (keyed by the type name scraped from part 2) and map of commands (keyed by the command name, scraped from part 3). One other thing this module produces is the text for the .h file defining all structures and types this module encountered. Attributes: _alg_id_table: actual table of various TPM2 algorithms, a copy of Table 9 from part 2. It is used to convert encoded algorithm specs used in other tables into a list of matching algorithms. _h_file: a multiline string, the accumulated .h file defining all TPM objects processed so far. _type_map: a dictionary of various TPM types, keyed by the string - the type name _command_map: a dictionary of command_generator.Command objects, keyed by the string, the command name skip_tables: a tuple of integers, the numbers of tables which should not be included in the .h file, as the same information was derived from part 4 earlier. _title: a string, title of the currently being processed specification table _title_type: a string, base type of the object defined by the currently being processed specification table _alg_type: a string, in some tables included in the title in curly brackets, to indicate what type of the algorithm this table deals with (usually RSA or ECC) _body: a list of strings, rows of the currently being processed specification table _has_selector_column: a Boolean, set to True if the third column of the table is the selector to be used to process this row (as in picking the object type when the table represents a union) _skip_printing: a Boolean, set to True if the table contents should not be included on tpm_types.h - some tables are also defined in files extracted from Part 4 of the specification. """ # Match table titles with processing routines. TABLE_ROUTER = ( (re.compile('(Constants|Defines for Logic Values)'), '_ProcessConstants'), (re.compile('(of Types for|Base Types)'), '_ProcessTypes'), (re.compile('Definition of .* Type'), '_ProcessInterfaceOrType'), (re.compile('Unmarshaling Errors'), '_ProcessEnum'), (re.compile(r'Definition of [\S]+ (Structure|Union)'), '_ProcessStructureOrUnion'), (re.compile('Definition of .* Bits'), '_ProcessBits'), (re.compile(r' TPM2_\S+ (Command|Response)'), '_ProcessCommand'), ) # The TCG spec in some cases uses so called 'Algorithm macros' to describe # all algorithms a type should apply to. The macros are described in details # in section 4.12 of part 2 of the spec. # # Basically, the macro consists of the prefix '!ALG' followed by dot # separated descriptions of algorithm types this marco applies to. # # The algorithm types are expressed as sequences or lower or upper case # letters, and should match the third column of Table 9 either inclusively # (in case the type letters are in upper case, or exclusively, in case the # type letters are in lower case. _alg_macro = re.compile(r'!ALG\.([a-z\.]+)', re.IGNORECASE) def __init__(self): """Create a Table class instance.""" self._alg_id_table = [] # Allow re-initializing attributes outside __init__() (in Init()) self.Init() self._h_file = '' self._type_map = {} self._command_map = {} self.skip_tables = () def Init(self, title=''): """Initialize a new table. This function is invoked each time a new table is encountered in the spec. A typical table header could look like this: 'Table 10 - Definition of (UINT16) {ECC} TPM_ECC_CURVE Constants' The algorithm type in curly brackets, if present, is redundant, it is stripped off before the table header comment is generated for the .h file. Some titles include the parenthesized base type the defined object should be typedef'ed from. Args: title: a string, the title of the table as included in the TCG spec. """ title_bracket_filter = re.compile(r'({.*?}) ?') title_type_filter = re.compile(r'(\(.*?\)) ?') # Retrieve base type, if present in the table title. m = title_type_filter.search(title) if m: # the header shown in the docstring above would result in the match of # '(UINT16)', remove the parenthesis and save the base type. self._title_type = m.groups()[0][1:-1] self._title = title_type_filter.sub('', title).strip() else: self._title_type = '' self._title = title.strip() # Now retrieve algorithm type, if present in the table title. m = title_bracket_filter.search(self._title) self._alg_type = '' if m: self._title = title_bracket_filter.sub('', self._title).strip() alg_type = m.groups()[0][1:-1].strip() if not alg_type.startswith('!'): self._alg_type = alg_type self._body = [] self._has_selector_column = False self._skip_printing = False def _SplitByAlg(self, word): """Split the passed in word by the regex used to pick TPM algorithms. The TPM algorithm regex is used all over the spec in situations where similar code needs to be generated for different algorithms of a certain type. A string in the spec might look like one of the following: TPMI_!ALG.S_KEY_BITS or !ALG.S_KEY_SIZES_BITS. The split would result in a three element list: the part before !ALG (could be empty), the letters between '!ALG.' and _ or end of the string, and the part after the letters included in the algorithm regex. TPMI_!ALG.S_KEY_BITS => ['TPMI_', 'S', '_KEY_BITS'] !ALG.S_KEY_SIZES_BITS => ['', 'S', '_KEY_SIZES_BITS'] The first and last elements of the split are used as the prefix and suffix of the type names included in the generated file. In some cases there is no regex suffix present, only the !ALG string, as in the selector column in table 127 (TPM_ALG_!ALG) In this case the split by the !ALG string is attempted, and the generated list has just two elements. In yet some other cases, say in Table 126 where the type field does not change at all set to TPMI_ALG_SYM_MODE for all fields. In such cases the split returns a single element list, the second element set to None is added to the list. Args: word: a string, the encoded algorithm string to be split. Returns: a tuple of two strings, first and last elements of the split, either one could be empty. """ parts = self._alg_macro.split(word) if len(parts) == 1: parts = word.split('!ALG') if len(parts) == 1: return word, None return parts[0].strip('_'), parts[-1].strip('_') def SetSkipTables(self, skip_tables): """Set the tuple of table numbers to be ignored by the parser.""" self.skip_tables = skip_tables def _AddToHfile(self, text=''): self._h_file += text + '\n' def _SetBaseType(self, old_type, tpm_obj): """Set the base type for a new object. Many TPM objects are typedefed hierarchically, for instance uint16_t => UINT16 => TPM_ALG_ID_Marshal => TPMI_ALG_HASH_Marshal This causes excessive nesting when marshaling and unmarshaling, which is bad from both performance and stack size requirements point of view. This function will discover the 'basest' type and set it in the tpm object, this would help to generate direct marshaling/unmarshaling functions. Args: old_type: a string, name of the immediate type this tpm object typedefed from. tpm_obj: a tpm object, derived from TPMType """ base_type = old_type while base_type in self._type_map: try: base_type = self._type_map[base_type].old_type except AttributeError: break # The underlying type is not a typedef tpm_obj.SetBaseType(base_type) def _AddTypedef(self, old_type, new_type): if not self._skip_printing: self._AddToHfile('typedef %s %s;' % (old_type, new_type)) # No need to generate marshaling/unmarshaling code for BOOL type. if new_type != 'BOOL': self._type_map[new_type] = Typedef(old_type, new_type) self._SetBaseType(old_type, self._type_map[new_type]) def InProgress(self): """Return True when the parser is in the middle of a table.""" return self._title def _GetMaxLengths(self, table): """Find out maximum lengths of the table columns. This function helps to generate nicely aligned definitions in the output .h file, by making sure that each field's name starts in the same column, far enough for all fields' types to fit. Args: table: a list of string lists. Each component consists of at least two elements, the first one the field or constant type, the second one the field name or constant value. Returns: a tuple of two integers, the first one - the length of the longest string in the first colume, the second one - the length of the longest string in the second column. """ lengths = [0, 0] for row in table: for i in range(len(lengths)): if len(row[i]) > lengths[i]: lengths[i] = len(row[i]) return tuple(lengths) def NewRow(self): """Start a new row in the internal table representation.""" self._body.append([]) def NewCell(self): """Start a new cell in the last row.""" self._body[-1].append('') def AddData(self, data): """Add data to the last cell of the last row.""" if not self._body: return # Ignore end of line and whitespace formatting. self._body[-1][-1] += data def ProcessTable(self): """Process accumulated table contents. This function is invoked when the parser state machine detects that the entire HTML table has been processed. The received contents is handled based on the table title by finding the matching entry in TABLE_ROUTER tuple. The result of processing is adding a new TPM type to the _type_map dictionary, or a new command descriptor to the _command_map dictionary. """ # The table has a selector column if it has at least three columns, and # the third column is titled 'Selector'. self._has_selector_column = (len(self._body[0]) >= 3 and self._body[0][2].strip() == 'Selector') # Preprocess representation of the table scraped from the spec. Namely, # remove the header row, and strip all other cells before adding them to # self._body[], which becomes a list including all scraped table cells, # stripped. self._body = [[cell.strip() for cell in row] for row in self._body[1:]] if 'TPM_ALG_ID Constants' in self._title: self._alg_id_table = [[x[0], x[2].replace(' ', ''), x[3]] for x in self._body] # The name of the type defined in the table, when present, is always the # fifth element in the stripped header, for instance: # 'Table 10 - Definition of TPM_ECC_CURVE Constants' try: type_name = self._title.split()[4] except IndexError: type_name = '' # Based on the table title, find the function to process the table and # generate a TPM specification object of a certain type. table_func = '' for regex, func in self.TABLE_ROUTER: if regex.search(self._title): table_func = func break else: self._AddToHfile('// Unprocessed: %s' % self._title) return if int(self._title.split()[1]) in self.skip_tables: self._skip_printing = True self._AddToHfile('// Skipped: %s' % self._title) else: self._AddToHfile('// %s' % self._title) # Invoke a TPM type specific processing function. getattr(self, table_func)(type_name) def _ProcessCommand(self, _): """Process command description table from part 3. Each TPM command has two tables associated with it, one describing the request structure, and another one describing the response structure. The first time a TPM command is encountered, a Command object is created and its 'request_args' property is set, the second time it is encountered - the existing object's 'response_args' property is set. """ command_name = self._title.split()[2] if command_name not in self._command_map: command = Command(command_name) self._command_map[command_name] = command else: command = self._command_map[command_name] params = [] # The first three fields in each request and response are always the same # and are not included in the generated structures. Let's iterate over the # rest of the fields. for row in self._body[3:]: # A dictionary describing a request or response structure field. field = {} # Ignore the '@' decoration for now. field_type, field['name'] = row[0], row[1].lstrip('@') # The '+' decoration means this field can be conditional. if field_type.endswith('+'): field_type = field_type[:-1] field['has_conditional'] = 'TRUE' else: field['has_conditional'] = 'FALSE' field['type'] = field_type if len(row) > 2: field['description'] = row[2] else: field['description'] = '' # Add the new field to the list of request or response fields. params.append(field) if ' Command' in self._title: command.request_args = params else: command.response_args = params def _PickAlgEntries(self, alg_type_str): """Process algorithm id table and find all matching entries. See comments to _alg_macro above. Args: alg_type_str: a string, one or more dot separated encoded algorithm types. Returns: A table of alg_type (Table 9 of part 2) entries matching the passed in encoded type string. """ filtered_table = [] for alg_type in alg_type_str.split('.'): if re.match('^[A-Z]+$', alg_type): # Exclusive selection, must exactly match algorithm type from table 9 # (which is in the second column). Add to the return value all # matching rows of table 9. extension = [] for row in self._alg_id_table: if row[1] == alg_type: if self._alg_type and self._alg_type != row[2]: continue extension.append(row) filtered_table.extend(extension) elif re.match('^[a-z]+$', alg_type): # Inclusive selection. All type letters must be present in the type # column, but no exact match is required. for row in self._alg_id_table: for char in alg_type.upper(): if char not in row[1]: break else: if not self._alg_type or self._alg_type == row[2]: filtered_table.append(row) return filtered_table def _ParseAlgorithmRegex(self, token): """Process a token as an algorithm regex. This function tries to interpret the passed in token as an encoded algorithm specification. In case the encoded algorithm regex matches, the function splits the token into prefix, algorithm description and suffix, and then retrieves the list of all algorithms matching the algorithm description. Args: token: a string, potentially including the algorithm regex. Returns: in case the regex matched returns a tri-tuple of two strings (prefix and suffix, either one could be empty) and a list of matching algorithms from the algorithm descriptors table. If there has been no match - returns None. """ elements = self._alg_macro.split(token) if len(elements) == 3: # The token matched the algorithm regex, Find out prefix and suffix to # be used on the generated type names, and the algorithm regex suffix to # use to find matching entries in the algorithm table. name_prefix, alg_suffix, name_suffix = tuple(elements) name_prefix = name_prefix.strip('_') name_suffix = name_suffix.strip('_') return name_prefix, name_suffix, self._PickAlgEntries(alg_suffix) def _ProcessInterface(self, type_name): """Processes spec tables describing interfaces.""" result = self._ParseAlgorithmRegex(type_name) if result: name_prefix, name_suffix, alg_list = tuple(result) # Process all matching algorithm types for alg_desc in alg_list: alg_base = alg_desc[0].replace('TPM_ALG_', '') new_name = '_'.join([name_prefix, alg_base, name_suffix]).strip('_') new_if = Interface(self._title_type, new_name) self._AddTypedef(self._title_type, new_name) for row in self._body: new_value = row[0] if new_value.startswith('$!ALG'): new_if.supported_values = alg_base + '_' + '_'.join( new_value.split('_')[1:]) elif new_value.startswith('$'): new_if.supported_values = new_value[1:] elif new_value.startswith('#'): new_if.error_code = new_value[1:] self._type_map[new_name] = new_if self._AddToHfile('\n') return new_if = Interface(self._title_type, type_name) self._AddTypedef(self._title_type, type_name) self._type_map[type_name] = new_if self._SetBaseType(type_name, new_if) for row in self._body: new_value = row[0] result = self._ParseAlgorithmRegex(new_value) if result: # The field is described using the algorithm regex. The above comment # applies. name_prefix, name_suffix, alg_list = tuple(result) for alg_desc in alg_list: alg_base = alg_desc[0].replace('TPM_ALG_', '') new_if.valid_values.append('_'.join( [name_prefix, alg_base, name_suffix]).strip('_')) else: if new_value.startswith('{'): bounds = tuple( [x.strip() for x in new_value[1:-1].strip().split(':')]) new_if.bounds.append(bounds) elif new_value.startswith('+'): new_if.conditional_value = new_value[1:] elif new_value.startswith('#'): new_if.error_code = new_value[1:] elif new_value.startswith('$'): new_if.supported_values = new_value[1:] else: new_if.valid_values.append(new_value) return def _ProcessTypedefs(self, type_name): """Processes spec tables defining new types.""" result = self._ParseAlgorithmRegex(type_name) if result: name_prefix, name_suffix, alg_list = tuple(result) for alg_desc in alg_list: alg_base = alg_desc[0].replace('TPM_ALG_', '') new_type = '%s_%s_%s' % (name_prefix, alg_base, name_suffix) self._AddTypedef(self._title_type, new_type) self._AddToHfile('\n') else: self._AddTypedef(self._title_type, type_name) def _ProcessBits(self, type_name): """Processes spec tables describing attributes (bit fields).""" bits_lines = [] base_bit = 0 tpm_obj = AttributeStructure(self._title_type, type_name) self._type_map[type_name] = tpm_obj self._SetBaseType(self._title_type, tpm_obj) for bits_line in self._body: field, name = tuple(bits_line[:2]) if not field: continue if name.startswith('TPM_'): # Spec inconsistency fix. name_pieces = [x.lower() for x in name.split('_')[1:]] name = name_pieces[0] for piece in name_pieces[1:]: name += piece[0].upper() + piece[1:] bit_range = [x.replace(' ', '') for x in field.split(':')] field_base = int(bit_range[-1]) if field_base != base_bit: field_name = 'reserved%d' % base_bit field_width = field_base - base_bit if field_width > 1: field_name += '_%d' % (field_base - 1) bits_lines.append(['%s : %d' % (field_name, field_width)]) tpm_obj.reserved.append(field_name.replace('reserved', '')) if len(bit_range) > 1: field_width = int(bit_range[0]) - field_base + 1 else: field_width = 1 if re.match('reserved', name, re.IGNORECASE): name = 'reserved%d' % field_base if field_width > 1: name += '_%d' % (field_base + field_width - 1) tpm_obj.reserved.append(name.replace('reserved', '')) bits_lines.append([name, ': %d' % field_width]) base_bit = field_base + field_width max_type_len, _ = self._GetMaxLengths(bits_lines) self._AddToHfile('typedef struct {') for bits_line in bits_lines: self._AddToHfile(' %s %-*s %s;' % (self._title_type, max_type_len, bits_line[0], bits_line[1])) self._AddToHfile('} %s;\n' % type_name) def _ExpandAlgs(self, row): """Find all algorithms encoded in the variable name. Args: row: a list of strings, a row of a structure or union table scraped from part 2. Returns: A list for structure_generator.Field objects, one per expanded algorithm. """ alg_spec = row[0].split() expansion = [] m = self._alg_macro.search(alg_spec[0]) if m: alg_type = m.groups()[0] # Find all algorithms of this type in the alg id table alg_entries = self._PickAlgEntries(alg_type) if len(alg_spec) == 2 and alg_spec[1][0] == '[': # This is the case of a union of arrays. raw_size_parts = self._alg_macro.split(alg_spec[1][1:-1]) size_prefix = raw_size_parts[0].strip('_') size_suffix = raw_size_parts[2].strip('_') for alg_desc in alg_entries: alg_base = alg_desc[0].replace('TPM_ALG_', '') size = '_'.join([size_prefix, alg_base, size_suffix]).strip('_') if self._has_selector_column: selector_parts = self._alg_macro.split(row[2]) selector_prefix = selector_parts[0].strip('_') selector_suffix = selector_parts[2].strip('_') selector = '_'.join([selector_prefix, alg_base, selector_suffix]).strip('_') else: selector = '' expansion.append(Field(row[1], alg_base.lower(), selector=selector, array_size=size)) else: type_prefix, type_suffix = self._SplitByAlg(row[1]) if self._has_selector_column: selector_prefix, selector_suffix = self._SplitByAlg(row[2]) else: selector = '' for alg_desc in alg_entries: alg_base = alg_desc[0].replace('TPM_ALG_', '') if type_suffix is not None: var_type = '_'.join([type_prefix, alg_base, type_suffix]).strip('_') else: var_type = type_prefix if self._has_selector_column: selector = '_'.join([selector_prefix, alg_base, selector_suffix]).strip('_') expansion.append(Field(var_type, alg_base.lower(), selector=selector)) return expansion def _ProcessInterfaceOrType(self, type_name): if type_name.startswith('TPMI_'): self._ProcessInterface(type_name) else: self._ProcessTypedefs(type_name) def _StructOrUnionToHfile(self, body_fields, type_name, union_mode, tpm_obj): body_lines = [] for field in body_fields: tpm_obj.AddField(field) body_lines.append([field.field_type, field.field_name]) if field.array_size: body_lines[-1][-1] += '[%s]' % field.array_size if field.selector_value: body_lines[-1].append(field.selector_value) max_type_len, _ = self._GetMaxLengths(body_lines) tpm2b_mode = type_name.startswith('TPM2B_') space_prefix = '' if union_mode: self._AddToHfile('typedef union {') else: if tpm2b_mode: self._AddToHfile('typedef union {') space_prefix = ' ' self._AddToHfile(' struct {') else: self._AddToHfile('typedef struct {') for line in body_lines: guard_required = len(line) > 2 and line[2].startswith('TPM_ALG_') if not line[1]: continue if guard_required: self._AddToHfile('#ifdef %s' % line[2]) self._AddToHfile(space_prefix + ' %-*s %s;' % ( max_type_len, line[0], line[1])) if guard_required: self._AddToHfile('#endif') if tpm2b_mode: self._AddToHfile(' } t;') self._AddToHfile(' TPM2B b;') self._AddToHfile('} %s;\n' % type_name) self._type_map[type_name] = tpm_obj def _ProcessStructureOrUnion(self, type_name): """Processes spec tables describing structure and unions. Both of these object types share a lot of similarities. Union types have the word 'Union' in the table title. Args: type_name: a string, name of the TPM object type """ union_mode = 'Union' in self._title if union_mode: tpm_obj = Union(type_name) else: tpm_obj = Structure(type_name) body_fields = [] for row in self._body: if row[0].startswith('#'): tpm_obj.error_code = row[0][1:] continue if (len(row) < 2 or row[1].startswith('#') or row[0].startswith('//')): continue value = row[0] if value.endswith('='): value = value[:-1] tpm_obj.size_check = True if self._alg_macro.search(value): body_fields.extend(self._ExpandAlgs(row)) continue array_size = None run_time_size = None vm = re.search(r'^(\S+)\s*\[(\S+)\]\s*\{(.*:*)\}', value) selector = '' if vm: value, run_time_size, bounds = vm.groups() lower, upper = [x.strip() for x in bounds.split(':')] if upper: array_size = upper tpm_obj.AddUpperBound(run_time_size, upper) else: array_size = run_time_size if lower: tpm_obj.AddLowerBound(run_time_size, lower) else: vm = re.search(r'^\[(\S+)\]\s*(\S+)', value) if vm: selector, value = vm.groups() else: vm = re.search(r'^(\S+)\s*\{(.+)\}', value) if vm: value, bounds = vm.groups() if ':' in bounds: lower, upper = [x.strip() for x in bounds.split(':')] if upper: tpm_obj.AddUpperBound(value, upper) if lower: tpm_obj.AddLowerBound(value, lower) if self._has_selector_column: selector = row[2] field_type = row[1] if field_type.startswith('+') or field_type.endswith('+'): field_type = field_type.strip('+') conditional = 'TRUE' else: conditional = 'FALSE' if field_type or value: body_fields.append(Field(field_type, value, array_size=array_size, run_time_size=run_time_size, selector=selector, conditional_value=conditional)) self._StructOrUnionToHfile(body_fields, type_name, union_mode, tpm_obj) def _ProcessEnum(self, _): """Processes spec tables describing enums.""" if self._skip_printing: return self._AddToHfile('enum {') for value, row in enumerate(self._body): self._AddToHfile(' %s = %d,' % (row[0], value + 1)) self._AddToHfile('};\n') def _ProcessTypes(self, _): """Processes spec tables describing new types.""" for type_, name_, _ in self._body: m = self._alg_macro.search(name_) if not m: self._AddTypedef(type_, name_) continue qualifier = [x for x in ('ECC', 'RSA') if x == type_.split('_')[-1]] alg_suffix = m.groups()[0] type_base = self._alg_macro.split(name_)[0] for alg_desc in self._PickAlgEntries(alg_suffix): if qualifier and alg_desc[2] != qualifier[0]: continue self._AddTypedef(type_, alg_desc[0].replace('TPM_ALG_', type_base)) self._AddToHfile() def _ProcessConstants(self, type_name): """Processes spec tables describing constants.""" if self._title_type: self._AddTypedef(self._title_type, type_name) constant_defs = [] tpm_obj = ConstantType(self._title_type, type_name) for row in self._body: name = row[0].strip() if not name: continue if name.startswith('#'): tpm_obj.error_code = name[1:] continue if name == 'reserved' or len(row) < 2: continue value = row[1].strip() rm = re.match(r'^(\(.*?\))', value) if rm: value = '%s' % rm.groups()[0] else: v_list = value.split() if len(v_list) > 2 and v_list[1] == '+': value = '((%s)(%s))' % (type_name, ' '.join(v_list[:3])) if ' ' in value and not value.startswith('('): value = '(%s)' % value constant_defs.append([name, value]) tpm_obj.valid_values.append(name) if self._title_type: self._type_map[type_name] = tpm_obj self._SetBaseType(self._title_type, tpm_obj) if self._skip_printing: return max_name_len, max_value_len = self._GetMaxLengths(constant_defs) for row in constant_defs: self._AddToHfile('#define %-*s %*s' % (max_name_len, row[0], max_value_len, row[1])) self._AddToHfile() def GetHFile(self): return self._h_file def GetCommandList(self): return sorted(self._command_map.values(), cmp=lambda x, y: cmp(x.name, y.name)) def GetTypeMap(self): return self._type_map