#!/usr/bin/env python3 # # generate a test program for the API # import xml.etree.ElementTree as etree import os import re import sys import xmlmod # Globals dtors = { 'htmlDoc *': 'xmlFreeDoc', 'htmlParserCtxt *': 'htmlFreeParserCtxt', 'xmlAutomata *': 'xmlFreeAutomata', 'xmlBuffer *': 'xmlBufferFree', 'xmlCatalog *': 'xmlFreeCatalog', 'xmlChar *': 'xmlFree', 'xmlDOMWrapCtxt *': 'xmlDOMWrapFreeCtxt', 'xmlDict *': 'xmlDictFree', 'xmlDoc *': 'xmlFreeDoc', 'xmlDtd *': 'xmlFreeDtd', 'xmlEntitiesTable *': 'xmlFreeEntitiesTable', 'xmlElementContent *': 'xmlFreeElementContent', 'xmlEnumeration *': 'xmlFreeEnumeration', 'xmlList *': 'xmlListDelete', 'xmlModule *': 'xmlModuleFree', 'xmlMutex *': 'xmlFreeMutex', 'xmlNode *': 'xmlFreeNode', 'xmlNodeSet *': 'xmlXPathFreeNodeSet', 'xmlNs *': 'xmlFreeNs', 'xmlOutputBuffer *': 'xmlOutputBufferClose', 'xmlParserCtxt *': 'xmlFreeParserCtxt', 'xmlParserInputBuffer *': 'xmlFreeParserInputBuffer', 'xmlParserInput *': 'xmlFreeInputStream', 'xmlRMutex *': 'xmlFreeRMutex', 'xmlRelaxNGValidCtxt *': 'xmlRelaxNGFreeValidCtxt', 'xmlSaveCtxt *': 'xmlSaveClose', 'xmlSchemaFacet *': 'xmlSchemaFreeFacet', 'xmlSchemaVal *': 'xmlSchemaFreeValue', 'xmlSchemaValidCtxt *': 'xmlSchemaFreeValidCtxt', 'xmlTextWriter *': 'xmlFreeTextWriter', 'xmlURI *': 'xmlFreeURI', 'xmlValidCtxt *': 'xmlFreeValidCtxt', 'xmlXPathContext *': 'xmlXPathFreeContext', 'xmlXPathParserContext *': 'xmlXPathFreeParserContext', 'xmlXPathObject *': 'xmlXPathFreeObject', } blockList = { # init/cleanup 'xmlCleanupParser': True, 'xmlInitParser': True, # arg must be non-NULL 'xmlMemStrdupLoc': True, 'xmlMemoryStrdup': True, # Returns void pointer which must be freed 'xmlMallocAtomicLoc': True, 'xmlMallocLoc': True, 'xmlMemMalloc': True, 'xmlMemRealloc': True, 'xmlReallocLoc': True, # Would reset the error handler 'xmlSetStructuredErrorFunc': True, # Prints errors 'xmlCatalogGetPublic': True, 'xmlCatalogGetSystem': True, 'xmlDebugDumpDTD': True, 'xmlDebugDumpDocument': True, 'xmlDebugDumpNode': True, 'xmlDebugDumpString': True, 'xmlParserError': True, 'xmlParserWarning': True, 'xmlParserValidityError': True, 'xmlParserValidityWarning': True, # Internal parser unctions, ctxt must be non-NULL 'xmlParseAttribute': True, 'xmlParseAttributeListDecl': True, 'xmlParseAttributeType': True, 'xmlParseCDSect': True, 'xmlParseCharData': True, 'xmlParseCharRef': True, 'xmlParseComment': True, 'xmlParseDefaultDecl': True, 'xmlParseDocTypeDecl': True, 'xmlParseEndTag': True, 'xmlParseElement': True, 'xmlParseElementChildrenContentDecl': True, 'xmlParseElementContentDecl': True, 'xmlParseElementDecl': True, 'xmlParseElementMixedContentDecl': True, 'xmlParseEncName': True, 'xmlParseEncodingDecl': True, 'xmlParseEntityDecl': True, 'xmlParseEntityValue': True, 'xmlParseEnumeratedType': True, 'xmlParseEnumerationType': True, 'xmlParseExternalID': True, 'xmlParseExternalSubset': True, 'xmlParseMarkupDecl': True, 'xmlParseMisc': True, 'xmlParseName': True, 'xmlParseNmtoken': True, 'xmlParseNotationDecl': True, 'xmlParseNotationType': True, 'xmlParsePEReference': True, 'xmlParsePI': True, 'xmlParsePITarget': True, 'xmlParsePubidLiteral': True, 'xmlParseReference': True, 'xmlParseSDDecl': True, 'xmlParseStartTag': True, 'xmlParseSystemLiteral': True, 'xmlParseTextDecl': True, 'xmlParseVersionInfo': True, 'xmlParseVersionNum': True, 'xmlParseXMLDecl': True, 'xmlParserHandlePEReference': True, 'xmlSkipBlankChars': True, # reads from stdin 'htmlReadFd': True, 'xmlReadFd': True, 'xmlReaderForFd': True, } # Parse document if len(sys.argv) > 1: buildDir = sys.argv[1] else: buildDir = '.' xmlDocDir = buildDir + '/doc/xml' filenames = {} functions = {} for file in os.listdir(xmlDocDir): if not file.endswith('_8h.xml'): continue doc = etree.parse(xmlDocDir + '/' + file) compound = doc.find('compounddef') module = compound.find('compoundname').text if not module.endswith('.h'): continue module = module[:-2] for section in compound.findall('sectiondef'): if section.get('kind') != 'func': continue for func in section.findall('memberdef'): name = func.find('name').text if name in blockList: continue module1, module2 = xmlmod.findModules(module, name) cargs = [] for arg in func.findall('param'): atype = etree.tostring(arg.find('type'), method='text', encoding='unicode').rstrip() if atype == 'void': continue if re.search(r'(Ptr|\*)$', atype): cargs.append('NULL') else: cargs.append('0') mfunc = functions.get(module1) if mfunc is None: mfunc = {} functions[module1] = mfunc mmfunc = mfunc.get(module2) if mmfunc is None: mmfunc = {} mfunc[module2] = mmfunc code = f'{name}({', '.join(cargs)})' rtype = etree.tostring(func.find('type'), method='text', encoding='unicode').rstrip() dtor = dtors.get(rtype) if dtor is not None: code = f'{dtor}({code})' elif rtype == 'xmlHashTable *': code = f'xmlHashFree({code}, NULL)' mmfunc[name] = f' {code};\n' # Write output test = open('testapi.c', 'w') test.write("""/* * testapi.c: libxml2 API tester program. * * Automatically generated by gentest.py * * See Copyright for the status of this software. */ /* Disable deprecation warnings */ #define XML_DEPRECATED #include "libxml.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void ignoreError(void *userData ATTRIBUTE_UNUSED, const xmlError *error ATTRIBUTE_UNUSED) { } int main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { xmlInitParser(); xmlSetStructuredErrorFunc(NULL, ignoreError); """) for module1 in sorted(functions.keys()): mfunc = functions[module1] if module1 != '': test.write(f'#ifdef LIBXML_{module1}_ENABLED\n') for module2 in sorted(mfunc.keys()): mmfunc = mfunc[module2] if module2 != '': test.write(f'#ifdef LIBXML_{module2}_ENABLED\n') for name in sorted(mmfunc.keys()): test.write(mmfunc[name]) if module2 != '': test.write(f'#endif /* LIBXML_{module2}_ENABLED */\n') if module1 != '': test.write(f'#endif /* LIBXML_{module1}_ENABLED */\n') test.write('\n') test.write(""" xmlCleanupParser(); return 0; } """)