| #!/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 = [] |
| skip = False |
| for arg in func.findall('param'): |
| atype = etree.tostring(arg.find('type'), |
| method='text', encoding='unicode').rstrip() |
| if atype == 'void': |
| continue |
| if atype == 'va_list': |
| skip = True |
| break |
| if re.search(r'(Ptr|\*)$', atype): |
| cargs.append('NULL') |
| else: |
| cargs.append('0') |
| |
| if skip: |
| continue |
| |
| 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 <libxml/HTMLparser.h> |
| #include <libxml/HTMLtree.h> |
| #include <libxml/c14n.h> |
| #include <libxml/catalog.h> |
| #include <libxml/debugXML.h> |
| #include <libxml/parserInternals.h> |
| #include <libxml/pattern.h> |
| #include <libxml/relaxng.h> |
| #include <libxml/schematron.h> |
| #include <libxml/uri.h> |
| #include <libxml/xinclude.h> |
| #include <libxml/xlink.h> |
| #include <libxml/xmlmodule.h> |
| #include <libxml/xmlreader.h> |
| #include <libxml/xmlsave.h> |
| #include <libxml/xmlschemas.h> |
| #include <libxml/xmlschemastypes.h> |
| #include <libxml/xmlwriter.h> |
| #include <libxml/xpathInternals.h> |
| #include <libxml/xpointer.h> |
| |
| 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; |
| } |
| """) |