/ libxml2 / gentest.py
gentest.py
   1  #!/usr/bin/python -u
   2  #
   3  # generate a tester program for the API
   4  #
   5  import sys
   6  import os
   7  import string
   8  try:
   9      import libxml2
  10  except:
  11      print "libxml2 python bindings not available, skipping testapi.c generation"
  12      sys.exit(0)
  13  
  14  if len(sys.argv) > 1:
  15      srcPref = sys.argv[1] + '/'
  16  else:
  17      srcPref = ''
  18  
  19  #
  20  # Modules we want to skip in API test
  21  #
  22  skipped_modules = [ "SAX", "xlink", "threads", "globals",
  23    "xmlmemory", "xmlversion", "xmlexports",
  24    #deprecated
  25    "DOCBparser",
  26  ]
  27  
  28  #
  29  # defines for each module
  30  #
  31  modules_defines = {
  32      "HTMLparser": "LIBXML_HTML_ENABLED",
  33      "catalog": "LIBXML_CATALOG_ENABLED",
  34      "xmlreader": "LIBXML_READER_ENABLED",
  35      "relaxng": "LIBXML_SCHEMAS_ENABLED",
  36      "schemasInternals": "LIBXML_SCHEMAS_ENABLED",
  37      "xmlschemas": "LIBXML_SCHEMAS_ENABLED",
  38      "xmlschemastypes": "LIBXML_SCHEMAS_ENABLED",
  39      "xpath": "LIBXML_XPATH_ENABLED",
  40      "xpathInternals": "LIBXML_XPATH_ENABLED",
  41      "xinclude": "LIBXML_XINCLUDE_ENABLED",
  42      "xpointer": "LIBXML_XPTR_ENABLED",
  43      "xmlregexp" : "LIBXML_REGEXP_ENABLED",
  44      "xmlautomata" : "LIBXML_AUTOMATA_ENABLED",
  45      "xmlsave" : "LIBXML_OUTPUT_ENABLED",
  46      "DOCBparser" : "LIBXML_DOCB_ENABLED",
  47      "xmlmodule" : "LIBXML_MODULES_ENABLED",
  48      "pattern" : "LIBXML_PATTERN_ENABLED",
  49      "schematron" : "LIBXML_SCHEMATRON_ENABLED",
  50  }
  51  
  52  #
  53  # defines for specific functions
  54  #
  55  function_defines = {
  56      "htmlDefaultSAXHandlerInit": "LIBXML_HTML_ENABLED",
  57      "xmlSAX2EndElement" : "LIBXML_SAX1_ENABLED",
  58      "xmlSAX2StartElement" : "LIBXML_SAX1_ENABLED",
  59      "xmlSAXDefaultVersion" : "LIBXML_SAX1_ENABLED",
  60      "UTF8Toisolat1" : "LIBXML_OUTPUT_ENABLED",
  61      "xmlCleanupPredefinedEntities": "LIBXML_LEGACY_ENABLED",
  62      "xmlInitializePredefinedEntities": "LIBXML_LEGACY_ENABLED",
  63      "xmlSetFeature": "LIBXML_LEGACY_ENABLED",
  64      "xmlGetFeature": "LIBXML_LEGACY_ENABLED",
  65      "xmlGetFeaturesList": "LIBXML_LEGACY_ENABLED",
  66      "xmlIOParseDTD": "LIBXML_VALID_ENABLED",
  67      "xmlParseDTD": "LIBXML_VALID_ENABLED",
  68      "xmlParseDoc": "LIBXML_SAX1_ENABLED",
  69      "xmlParseMemory": "LIBXML_SAX1_ENABLED",
  70      "xmlRecoverDoc": "LIBXML_SAX1_ENABLED",
  71      "xmlParseFile": "LIBXML_SAX1_ENABLED",
  72      "xmlRecoverFile": "LIBXML_SAX1_ENABLED",
  73      "xmlRecoverMemory": "LIBXML_SAX1_ENABLED",
  74      "xmlSAXParseFileWithData": "LIBXML_SAX1_ENABLED",
  75      "xmlSAXParseMemory": "LIBXML_SAX1_ENABLED",
  76      "xmlSAXUserParseMemory": "LIBXML_SAX1_ENABLED",
  77      "xmlSAXParseDoc": "LIBXML_SAX1_ENABLED",
  78      "xmlSAXParseDTD": "LIBXML_SAX1_ENABLED",
  79      "xmlSAXUserParseFile": "LIBXML_SAX1_ENABLED",
  80      "xmlParseEntity": "LIBXML_SAX1_ENABLED",
  81      "xmlParseExternalEntity": "LIBXML_SAX1_ENABLED",
  82      "xmlSAXParseMemoryWithData": "LIBXML_SAX1_ENABLED",
  83      "xmlParseBalancedChunkMemory": "LIBXML_SAX1_ENABLED",
  84      "xmlParseBalancedChunkMemoryRecover": "LIBXML_SAX1_ENABLED",
  85      "xmlSetupParserForBuffer": "LIBXML_SAX1_ENABLED",
  86      "xmlStopParser": "LIBXML_PUSH_ENABLED",
  87      "xmlAttrSerializeTxtContent": "LIBXML_OUTPUT_ENABLED",
  88      "xmlSAXParseFile": "LIBXML_SAX1_ENABLED",
  89      "xmlSAXParseEntity": "LIBXML_SAX1_ENABLED",
  90      "xmlNewTextChild": "LIBXML_TREE_ENABLED",
  91      "xmlNewDocRawNode": "LIBXML_TREE_ENABLED",
  92      "xmlNewProp": "LIBXML_TREE_ENABLED",
  93      "xmlReconciliateNs": "LIBXML_TREE_ENABLED",
  94      "xmlValidateNCName": "LIBXML_TREE_ENABLED",
  95      "xmlValidateNMToken": "LIBXML_TREE_ENABLED",
  96      "xmlValidateName": "LIBXML_TREE_ENABLED",
  97      "xmlNewChild": "LIBXML_TREE_ENABLED",
  98      "xmlValidateQName": "LIBXML_TREE_ENABLED",
  99      "xmlSprintfElementContent": "LIBXML_OUTPUT_ENABLED",
 100      "xmlValidGetPotentialChildren" : "LIBXML_VALID_ENABLED",
 101      "xmlValidGetValidElements" : "LIBXML_VALID_ENABLED",
 102      "docbDefaultSAXHandlerInit" : "LIBXML_DOCB_ENABLED",
 103      "xmlTextReaderPreservePattern" : "LIBXML_PATTERN_ENABLED",
 104  }
 105  
 106  #
 107  # Some functions really need to be skipped for the tests.
 108  #
 109  skipped_functions = [
 110  # block on I/O
 111  "xmlFdRead", "xmlReadFd", "xmlCtxtReadFd",
 112  "htmlFdRead", "htmlReadFd", "htmlCtxtReadFd",
 113  "xmlReaderNewFd", "xmlReaderForFd",
 114  "xmlIORead", "xmlReadIO", "xmlCtxtReadIO",
 115  "htmlIORead", "htmlReadIO", "htmlCtxtReadIO",
 116  "xmlReaderNewIO", "xmlBufferDump", "xmlNanoFTPConnect",
 117  "xmlNanoFTPConnectTo", "xmlNanoHTTPMethod", "xmlNanoHTTPMethodRedir",
 118  # Complex I/O APIs
 119  "xmlCreateIOParserCtxt", "xmlParserInputBufferCreateIO",
 120  "xmlRegisterInputCallbacks", "xmlReaderForIO",
 121  "xmlOutputBufferCreateIO", "xmlRegisterOutputCallbacks",
 122  "xmlSaveToIO", "xmlIOHTTPOpenW",
 123  # library state cleanup, generate false leak informations and other
 124  # troubles, heavillyb tested otherwise.
 125  "xmlCleanupParser", "xmlRelaxNGCleanupTypes", "xmlSetListDoc",
 126  "xmlSetTreeDoc", "xmlUnlinkNode",
 127  # hard to avoid leaks in the tests
 128  "xmlStrcat", "xmlStrncat", "xmlCatalogAddLocal", "xmlNewTextWriterDoc",
 129  "xmlXPathNewValueTree", "xmlXPathWrapString",
 130  # unimplemented
 131  "xmlTextReaderReadInnerXml", "xmlTextReaderReadOuterXml",
 132  "xmlTextReaderReadString",
 133  # destructor
 134  "xmlListDelete", "xmlOutputBufferClose", "xmlNanoFTPClose", "xmlNanoHTTPClose",
 135  # deprecated
 136  "xmlCatalogGetPublic", "xmlCatalogGetSystem", "xmlEncodeEntities",
 137  "xmlNewGlobalNs", "xmlHandleEntity", "xmlNamespaceParseNCName",
 138  "xmlNamespaceParseNSDef", "xmlNamespaceParseQName",
 139  "xmlParseNamespace", "xmlParseQuotedString", "xmlParserHandleReference",
 140  "xmlScanName",
 141  "xmlDecodeEntities", 
 142  # allocators
 143  "xmlMemFree",
 144  # verbosity
 145  "xmlCatalogSetDebug", "xmlShellPrintXPathError", "xmlShellPrintNode",
 146  # Internal functions, no user space should really call them
 147  "xmlParseAttribute", "xmlParseAttributeListDecl", "xmlParseName",
 148  "xmlParseNmtoken", "xmlParseEntityValue", "xmlParseAttValue",
 149  "xmlParseSystemLiteral", "xmlParsePubidLiteral", "xmlParseCharData",
 150  "xmlParseExternalID", "xmlParseComment", "xmlParsePITarget", "xmlParsePI",
 151  "xmlParseNotationDecl", "xmlParseEntityDecl", "xmlParseDefaultDecl",
 152  "xmlParseNotationType", "xmlParseEnumerationType", "xmlParseEnumeratedType",
 153  "xmlParseAttributeType", "xmlParseAttributeListDecl",
 154  "xmlParseElementMixedContentDecl", "xmlParseElementChildrenContentDecl",
 155  "xmlParseElementContentDecl", "xmlParseElementDecl", "xmlParseMarkupDecl",
 156  "xmlParseCharRef", "xmlParseEntityRef", "xmlParseReference",
 157  "xmlParsePEReference", "xmlParseDocTypeDecl", "xmlParseAttribute",
 158  "xmlParseStartTag", "xmlParseEndTag", "xmlParseCDSect", "xmlParseContent",
 159  "xmlParseElement", "xmlParseVersionNum", "xmlParseVersionInfo",
 160  "xmlParseEncName", "xmlParseEncodingDecl", "xmlParseSDDecl",
 161  "xmlParseXMLDecl", "xmlParseTextDecl", "xmlParseMisc",
 162  "xmlParseExternalSubset", "xmlParserHandlePEReference",
 163  "xmlSkipBlankChars",
 164  ]
 165  
 166  #
 167  # These functions have side effects on the global state
 168  # and hence generate errors on memory allocation tests
 169  #
 170  skipped_memcheck = [ "xmlLoadCatalog", "xmlAddEncodingAlias",
 171     "xmlSchemaInitTypes", "xmlNanoFTPProxy", "xmlNanoFTPScanProxy",
 172     "xmlNanoHTTPScanProxy", "xmlResetLastError", "xmlCatalogConvert",
 173     "xmlCatalogRemove", "xmlLoadCatalogs", "xmlCleanupCharEncodingHandlers",
 174     "xmlInitCharEncodingHandlers", "xmlCatalogCleanup",
 175     "xmlSchemaGetBuiltInType",
 176     "htmlParseFile", "htmlCtxtReadFile", # loads the catalogs
 177     "xmlTextReaderSchemaValidate", "xmlSchemaCleanupTypes", # initialize the schemas type system
 178     "xmlCatalogResolve", "xmlIOParseDTD" # loads the catalogs
 179  ]
 180  
 181  #
 182  # Extra code needed for some test cases
 183  #
 184  extra_pre_call = {
 185     "xmlSAXUserParseFile": """
 186  #ifdef LIBXML_SAX1_ENABLED
 187          if (sax == (xmlSAXHandlerPtr)&xmlDefaultSAXHandler) user_data = NULL;
 188  #endif
 189  """,
 190     "xmlSAXUserParseMemory": """
 191  #ifdef LIBXML_SAX1_ENABLED
 192          if (sax == (xmlSAXHandlerPtr)&xmlDefaultSAXHandler) user_data = NULL;
 193  #endif
 194  """,
 195     "xmlParseBalancedChunkMemory": """
 196  #ifdef LIBXML_SAX1_ENABLED
 197          if (sax == (xmlSAXHandlerPtr)&xmlDefaultSAXHandler) user_data = NULL;
 198  #endif
 199  """,
 200     "xmlParseBalancedChunkMemoryRecover": """
 201  #ifdef LIBXML_SAX1_ENABLED
 202          if (sax == (xmlSAXHandlerPtr)&xmlDefaultSAXHandler) user_data = NULL;
 203  #endif
 204  """,
 205     "xmlParserInputBufferCreateFd":
 206         "if (fd >= 0) fd = -1;",
 207  }
 208  extra_post_call = {
 209     "xmlAddChild": 
 210         "if (ret_val == NULL) { xmlFreeNode(cur) ; cur = NULL ; }",
 211     "xmlAddEntity":
 212         "if (ret_val != NULL) { xmlFreeNode(ret_val) ; ret_val = NULL; }",
 213     "xmlAddChildList": 
 214         "if (ret_val == NULL) { xmlFreeNodeList(cur) ; cur = NULL ; }",
 215     "xmlAddSibling":
 216         "if (ret_val == NULL) { xmlFreeNode(elem) ; elem = NULL ; }",
 217     "xmlAddNextSibling":
 218         "if (ret_val == NULL) { xmlFreeNode(elem) ; elem = NULL ; }",
 219     "xmlAddPrevSibling": 
 220         "if (ret_val == NULL) { xmlFreeNode(elem) ; elem = NULL ; }",
 221     "xmlDocSetRootElement": 
 222         "if (doc == NULL) { xmlFreeNode(root) ; root = NULL ; }",
 223     "xmlReplaceNode": 
 224         """if (cur != NULL) {
 225                xmlUnlinkNode(cur);
 226                xmlFreeNode(cur) ; cur = NULL ; }
 227            if (old != NULL) {
 228                xmlUnlinkNode(old);
 229                xmlFreeNode(old) ; old = NULL ; }
 230  	  ret_val = NULL;""",
 231     "xmlTextMerge": 
 232         """if ((first != NULL) && (first->type != XML_TEXT_NODE)) {
 233                xmlUnlinkNode(second);
 234                xmlFreeNode(second) ; second = NULL ; }""",
 235     "xmlBuildQName": 
 236         """if ((ret_val != NULL) && (ret_val != ncname) &&
 237                (ret_val != prefix) && (ret_val != memory))
 238                xmlFree(ret_val);
 239  	  ret_val = NULL;""",
 240     "xmlNewDocElementContent":
 241         """xmlFreeDocElementContent(doc, ret_val); ret_val = NULL;""",
 242     "xmlDictReference": "xmlDictFree(dict);",
 243     # Functions which deallocates one of their parameters
 244     "xmlXPathConvertBoolean": """val = NULL;""",
 245     "xmlXPathConvertNumber": """val = NULL;""",
 246     "xmlXPathConvertString": """val = NULL;""",
 247     "xmlSaveFileTo": """buf = NULL;""",
 248     "xmlSaveFormatFileTo": """buf = NULL;""",
 249     "xmlIOParseDTD": "input = NULL;",
 250     "xmlRemoveProp": "cur = NULL;",
 251     "xmlNewNs": "if ((node == NULL) && (ret_val != NULL)) xmlFreeNs(ret_val);",
 252     "xmlCopyNamespace": "if (ret_val != NULL) xmlFreeNs(ret_val);",
 253     "xmlCopyNamespaceList": "if (ret_val != NULL) xmlFreeNsList(ret_val);",
 254     "xmlNewTextWriter": "if (ret_val != NULL) out = NULL;",
 255     "xmlNewTextWriterPushParser": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;} if (ret_val != NULL) ctxt = NULL;",
 256     "xmlNewIOInputStream": "if (ret_val != NULL) input = NULL;",
 257     "htmlParseChunk": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;}",
 258     "htmlParseDocument": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;}",
 259     "xmlParseDocument": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;}",
 260     "xmlParseChunk": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;}",
 261     "xmlParseExtParsedEnt": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;}",
 262     "xmlDOMWrapAdoptNode": "if ((node != NULL) && (node->parent == NULL)) {xmlUnlinkNode(node);xmlFreeNode(node);node = NULL;}",
 263     "xmlBufferSetAllocationScheme": "if ((buf != NULL) && (scheme == XML_BUFFER_ALLOC_IMMUTABLE) && (buf->content != NULL) && (buf->content != static_buf_content)) { xmlFree(buf->content); buf->content = NULL;}"
 264  }
 265  
 266  #
 267  # Arguments that should use strlen() instead of gen_int()
 268  #
 269  use_strlen = {
 270      "htmlCreateMemoryParserCtxt": {"size": "buffer"},
 271      "htmlCreatePushParserCtxt": {"size": "chunk"},
 272      "htmlCtxtReadMemory": {"size": "buffer"},
 273      "htmlParseChunk": {"size": "chunk"},
 274      "htmlReadMemory": {"size": "buffer"},
 275      "xmlDictExists": {"len": "name"},
 276      "xmlDictLookup": {"len": "name"},
 277      "xmlCreatePushParserCtxt": {"size": "chunk"},
 278      "xmlCtxtReadMemory": {"size": "buffer"},
 279      "xmlCtxtResetPush": {"size": "chunk"},
 280      "xmlParseChunk": {"size": "chunk"},
 281      "xmlParseInNodeContext": {"datalen": "data"},
 282      "xmlParseMemory": {"size": "buffer"},
 283      "xmlReadMemory": {"size": "buffer"},
 284      "xmlRecoverMemory": {"size": "buffer"},
 285      "xmlSAXParseMemory": {"size": "buffer"},
 286      "xmlSAXParseMemoryWithData": {"size": "buffer"},
 287      "xmlSAXUserParseMemory": {"size": "buffer"},
 288      "xmlCreateMemoryParserCtxt": {"size": "buffer"},
 289      "xmlRelaxNGNewMemParserCtxt": {"size": "buffer"},
 290      "xmlBufferAdd": {"len": "str"},
 291      "xmlBufferAddHead": {"len": "str"},
 292      "xmlBuildQName": {"len": "memory"},
 293      "xmlNewCDataBlock": {"len": "content"},
 294      "xmlNewDocTextLen": {"len": "content"},
 295      "xmlNewTextLen": {"len": "content"},
 296      "xmlNodeAddContentLen": {"len": "content"},
 297      "xmlNodeSetContentLen": {"len": "content"},
 298      "xmlStringLenGetNodeList": {"len": "value"},
 299      "xmlTextConcat": {"len": "content"},
 300      "xmlOutputBufferWrite": {"len": "buf", "-reorder-length": True},
 301      "xmlParserInputBufferCreateMem": {"size": "mem"},
 302      "xmlParserInputBufferCreateStatic": {"size": "mem"},
 303      "xmlParserInputBufferPush": {"len": "buf", "-reorder-length": True},
 304      "xmlReaderForMemory": {"size": "buffer"},
 305      "xmlReaderNewMemory": {"size": "buffer"},
 306      "xmlSchemaNewMemParserCtxt": {"size": "buffer"},
 307      "xmlCharStrndup": {"len": "cur"},
 308      "xmlStrncatNew": {"len": "str2"},
 309      "xmlStrncmp": {"len": "str2"},
 310      "xmlStrndup": {"len": "cur"},
 311      "xmlStrsub": {"len": "str", "-compute-length": "start"},
 312      "xmlTextWriterWriteBase64": {"len": "data", "-compute-length": "start"},
 313      "xmlTextWriterWriteBinHex": {"len": "data", "-compute-length": "start"},
 314      "xmlTextWriterWriteRawLen": {"len": "content"},
 315  }
 316  
 317  modules = []
 318  
 319  def is_skipped_module(name):
 320      for mod in skipped_modules:
 321          if mod == name:
 322  	    return 1
 323      return 0
 324  
 325  def is_skipped_function(name):
 326      for fun in skipped_functions:
 327          if fun == name:
 328  	    return 1
 329      # Do not test destructors
 330      if string.find(name, 'Free') != -1:
 331          return 1
 332      return 0
 333  
 334  def is_skipped_memcheck(name):
 335      for fun in skipped_memcheck:
 336          if fun == name:
 337  	    return 1
 338      return 0
 339  
 340  missing_types = {}
 341  def add_missing_type(name, func):
 342      try:
 343          list = missing_types[name]
 344  	list.append(func)
 345      except:
 346          missing_types[name] = [func]
 347  
 348  generated_param_types = []
 349  def add_generated_param_type(name):
 350      generated_param_types.append(name)
 351  
 352  generated_return_types = []
 353  def add_generated_return_type(name):
 354      generated_return_types.append(name)
 355  
 356  missing_functions = {}
 357  missing_functions_nr = 0
 358  def add_missing_functions(name, module):
 359      global missing_functions_nr
 360  
 361      missing_functions_nr = missing_functions_nr + 1
 362      try:
 363          list = missing_functions[module]
 364  	list.append(name)
 365      except:
 366          missing_functions[module] = [name]
 367  
 368  #
 369  # Provide the type generators and destructors for the parameters
 370  #
 371  
 372  def type_convert(str, name, info, module, function, pos):
 373  #    res = string.replace(str, "    ", " ")
 374  #    res = string.replace(str, "   ", " ")
 375  #    res = string.replace(str, "  ", " ")
 376      res = string.replace(str, " *", "_ptr")
 377  #    res = string.replace(str, "*", "_ptr")
 378      res = string.replace(res, " ", "_")
 379      if res == 'const_char_ptr':
 380          if string.find(name, "file") != -1 or \
 381             string.find(name, "uri") != -1 or \
 382             string.find(name, "URI") != -1 or \
 383             string.find(info, "filename") != -1 or \
 384             string.find(info, "URI") != -1 or \
 385             string.find(info, "URL") != -1:
 386  	    if string.find(function, "Save") != -1 or \
 387  	       string.find(function, "Create") != -1 or \
 388  	       string.find(function, "Write") != -1 or \
 389  	       string.find(function, "Fetch") != -1:
 390  	        return('fileoutput')
 391  	    return('filepath')
 392      if res == 'void_ptr':
 393          if module == 'nanoftp' and name == 'ctx':
 394  	    return('xmlNanoFTPCtxtPtr')
 395          if function == 'xmlNanoFTPNewCtxt' or \
 396  	   function == 'xmlNanoFTPConnectTo' or \
 397  	   function == 'xmlNanoFTPOpen':
 398  	    return('xmlNanoFTPCtxtPtr')
 399          if module == 'nanohttp' and name == 'ctx':
 400  	    return('xmlNanoHTTPCtxtPtr')
 401  	if function == 'xmlNanoHTTPMethod' or \
 402  	   function == 'xmlNanoHTTPMethodRedir' or \
 403  	   function == 'xmlNanoHTTPOpen' or \
 404  	   function == 'xmlNanoHTTPOpenRedir':
 405  	    return('xmlNanoHTTPCtxtPtr');
 406          if function == 'xmlIOHTTPOpen':
 407  	    return('xmlNanoHTTPCtxtPtr')
 408  	if string.find(name, "data") != -1:
 409  	    return('userdata')
 410  	if string.find(name, "user") != -1:
 411  	    return('userdata')
 412      if res == 'xmlDoc_ptr':
 413          res = 'xmlDocPtr'
 414      if res == 'xmlNode_ptr':
 415          res = 'xmlNodePtr'
 416      if res == 'xmlDict_ptr':
 417          res = 'xmlDictPtr'
 418      if res == 'xmlNodePtr' and pos != 0:
 419          if (function == 'xmlAddChild' and pos == 2) or \
 420  	   (function == 'xmlAddChildList' and pos == 2) or \
 421             (function == 'xmlAddNextSibling' and pos == 2) or \
 422             (function == 'xmlAddSibling' and pos == 2) or \
 423             (function == 'xmlDocSetRootElement' and pos == 2) or \
 424             (function == 'xmlReplaceNode' and pos == 2) or \
 425             (function == 'xmlTextMerge') or \
 426  	   (function == 'xmlAddPrevSibling' and pos == 2):
 427  	    return('xmlNodePtr_in');
 428      if res == 'const xmlBufferPtr':
 429          res = 'xmlBufferPtr'
 430      if res == 'xmlChar_ptr' and name == 'name' and \
 431         string.find(function, "EatName") != -1:
 432          return('eaten_name')
 433      if res == 'void_ptr*':
 434          res = 'void_ptr_ptr'
 435      if res == 'char_ptr*':
 436          res = 'char_ptr_ptr'
 437      if res == 'xmlChar_ptr*':
 438          res = 'xmlChar_ptr_ptr'
 439      if res == 'const_xmlChar_ptr*':
 440          res = 'const_xmlChar_ptr_ptr'
 441      if res == 'const_char_ptr*':
 442          res = 'const_char_ptr_ptr'
 443      if res == 'FILE_ptr' and module == 'debugXML':
 444          res = 'debug_FILE_ptr';
 445      if res == 'int' and name == 'options':
 446          if module == 'parser' or module == 'xmlreader':
 447  	    res = 'parseroptions'
 448  
 449      return res
 450  
 451  known_param_types = []
 452  
 453  def is_known_param_type(name, rtype):
 454      global test
 455      for type in known_param_types:
 456          if type == name:
 457  	    return 1
 458      for type in generated_param_types:
 459          if type == name:
 460  	    return 1
 461  
 462      if name[-3:] == 'Ptr' or name[-4:] == '_ptr':
 463          if rtype[0:6] == 'const ':
 464  	    crtype = rtype[6:]
 465  	else:
 466  	    crtype = rtype
 467  
 468          define = 0
 469  	if modules_defines.has_key(module):
 470  	    test.write("#ifdef %s\n" % (modules_defines[module]))
 471  	    define = 1
 472          test.write("""
 473  #define gen_nb_%s 1
 474  static %s gen_%s(int no ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) {
 475      return(NULL);
 476  }
 477  static void des_%s(int no ATTRIBUTE_UNUSED, %s val ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) {
 478  }
 479  """ % (name, crtype, name, name, rtype))
 480          if define == 1:
 481  	    test.write("#endif\n\n")
 482          add_generated_param_type(name)
 483          return 1
 484  
 485      return 0
 486  
 487  #
 488  # Provide the type destructors for the return values
 489  #
 490  
 491  known_return_types = []
 492  
 493  def is_known_return_type(name):
 494      for type in known_return_types:
 495          if type == name:
 496  	    return 1
 497      return 0
 498  
 499  #
 500  # Copy the beginning of the C test program result
 501  #
 502  
 503  try:
 504      input = open("testapi.c", "r")
 505  except:
 506      input = open(srcPref + "testapi.c", "r")
 507  test = open('testapi.c.new', 'w')
 508  
 509  def compare_and_save():
 510      global test
 511  
 512      test.close()
 513      try:
 514          input = open("testapi.c", "r").read()
 515      except:
 516          input = ''
 517      test = open('testapi.c.new', "r").read()
 518      if input != test:
 519          try:
 520              os.system("rm testapi.c; mv testapi.c.new testapi.c")
 521          except:
 522  	    os.system("mv testapi.c.new testapi.c")
 523          print("Updated testapi.c")
 524      else:
 525          print("Generated testapi.c is identical")
 526  
 527  line = input.readline()
 528  while line != "":
 529      if line == "/* CUT HERE: everything below that line is generated */\n":
 530          break;
 531      if line[0:15] == "#define gen_nb_":
 532          type = string.split(line[15:])[0]
 533  	known_param_types.append(type)
 534      if line[0:19] == "static void desret_":
 535          type = string.split(line[19:], '(')[0]
 536  	known_return_types.append(type)
 537      test.write(line)
 538      line = input.readline()
 539  input.close()
 540  
 541  if line == "":
 542      print "Could not find the CUT marker in testapi.c skipping generation"
 543      test.close()
 544      sys.exit(0)
 545  
 546  print("Scanned testapi.c: found %d parameters types and %d return types\n" % (
 547        len(known_param_types), len(known_return_types)))
 548  test.write("/* CUT HERE: everything below that line is generated */\n")
 549  
 550  
 551  #
 552  # Open the input API description
 553  #
 554  doc = libxml2.readFile(srcPref + 'doc/libxml2-api.xml', None, 0)
 555  if doc == None:
 556      print "Failed to load doc/libxml2-api.xml"
 557      sys.exit(1)
 558  ctxt = doc.xpathNewContext()
 559  
 560  #
 561  # Generate a list of all function parameters and select only
 562  # those used in the api tests
 563  #
 564  argtypes = {}
 565  args = ctxt.xpathEval("/api/symbols/function/arg")
 566  for arg in args:
 567      mod = arg.xpathEval('string(../@file)')
 568      func = arg.xpathEval('string(../@name)')
 569      if (mod not in skipped_modules) and (func not in skipped_functions):
 570  	type = arg.xpathEval('string(@type)')
 571  	if not argtypes.has_key(type):
 572  	    argtypes[type] = func
 573  
 574  # similarly for return types
 575  rettypes = {}
 576  rets = ctxt.xpathEval("/api/symbols/function/return")
 577  for ret in rets:
 578      mod = ret.xpathEval('string(../@file)')
 579      func = ret.xpathEval('string(../@name)')
 580      if (mod not in skipped_modules) and (func not in skipped_functions):
 581          type = ret.xpathEval('string(@type)')
 582  	if not rettypes.has_key(type):
 583  	    rettypes[type] = func
 584  
 585  #
 586  # Generate constructors and return type handling for all enums
 587  # which are used as function parameters
 588  #
 589  enums = ctxt.xpathEval("/api/symbols/typedef[@type='enum']")
 590  for enum in enums:
 591      module = enum.xpathEval('string(@file)')
 592      name = enum.xpathEval('string(@name)')
 593      #
 594      # Skip any enums which are not in our filtered lists
 595      #
 596      if (name == None) or ((name not in argtypes) and (name not in rettypes)):
 597          continue;
 598      define = 0
 599  
 600      if argtypes.has_key(name) and is_known_param_type(name, name) == 0:
 601  	values = ctxt.xpathEval("/api/symbols/enum[@type='%s']" % name)
 602  	i = 0
 603  	vals = []
 604  	for value in values:
 605  	    vname = value.xpathEval('string(@name)')
 606  	    if vname == None:
 607  		continue;
 608  	    i = i + 1
 609  	    if i >= 5:
 610  		break;
 611  	    vals.append(vname)
 612  	if vals == []:
 613  	    print "Didn't find any value for enum %s" % (name)
 614  	    continue
 615  	if modules_defines.has_key(module):
 616  	    test.write("#ifdef %s\n" % (modules_defines[module]))
 617  	    define = 1
 618  	test.write("#define gen_nb_%s %d\n" % (name, len(vals)))
 619  	test.write("""static %s gen_%s(int no, int nr ATTRIBUTE_UNUSED) {\n""" %
 620  	           (name, name))
 621  	i = 1
 622  	for value in vals:
 623  	    test.write("    if (no == %d) return(%s);\n" % (i, value))
 624  	    i = i + 1
 625  	test.write("""    return(0);
 626  }
 627  
 628  static void des_%s(int no ATTRIBUTE_UNUSED, %s val ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) {
 629  }
 630  
 631  """ % (name, name));
 632  	known_param_types.append(name)
 633  
 634      if (is_known_return_type(name) == 0) and (name in rettypes):
 635  	if define == 0 and modules_defines.has_key(module):
 636  	    test.write("#ifdef %s\n" % (modules_defines[module]))
 637  	    define = 1
 638          test.write("""static void desret_%s(%s val ATTRIBUTE_UNUSED) {
 639  }
 640  
 641  """ % (name, name))
 642  	known_return_types.append(name)
 643      if define == 1:
 644          test.write("#endif\n\n")
 645  
 646  #
 647  # Load the interfaces
 648  # 
 649  headers = ctxt.xpathEval("/api/files/file")
 650  for file in headers:
 651      name = file.xpathEval('string(@name)')
 652      if (name == None) or (name == ''):
 653          continue
 654  
 655      #
 656      # Some module may be skipped because they don't really consists
 657      # of user callable APIs
 658      #
 659      if is_skipped_module(name):
 660          continue
 661  
 662      #
 663      # do not test deprecated APIs
 664      #
 665      desc = file.xpathEval('string(description)')
 666      if string.find(desc, 'DEPRECATED') != -1:
 667          print "Skipping deprecated interface %s" % name
 668  	continue;
 669  
 670      test.write("#include <libxml/%s.h>\n" % name)
 671      modules.append(name)
 672          
 673  #
 674  # Generate the callers signatures
 675  # 
 676  for module in modules:
 677      test.write("static int test_%s(void);\n" % module);
 678  
 679  #
 680  # Generate the top caller
 681  # 
 682  
 683  test.write("""
 684  /**
 685   * testlibxml2:
 686   *
 687   * Main entry point of the tester for the full libxml2 module,
 688   * it calls all the tester entry point for each module.
 689   *
 690   * Returns the number of error found
 691   */
 692  static int
 693  testlibxml2(void)
 694  {
 695      int test_ret = 0;
 696  
 697  """)
 698  
 699  for module in modules:
 700      test.write("    test_ret += test_%s();\n" % module)
 701  
 702  test.write("""
 703      printf("Total: %d functions, %d tests, %d errors\\n",
 704             function_tests, call_tests, test_ret);
 705      return(test_ret);
 706  }
 707  
 708  """)
 709  
 710  #
 711  # How to handle a function
 712  # 
 713  nb_tests = 0
 714  
 715  def generate_test(module, node):
 716      global test
 717      global nb_tests
 718      nb_cond = 0
 719      no_gen = 0
 720  
 721      name = node.xpathEval('string(@name)')
 722      if is_skipped_function(name):
 723          return
 724  
 725      #
 726      # check we know how to handle the args and return values
 727      # and store the informations for the generation
 728      #
 729      try:
 730  	args = node.xpathEval("arg")
 731      except:
 732          args = []
 733      t_args = []
 734      n = 0
 735      for arg in args:
 736          n = n + 1
 737          rtype = arg.xpathEval("string(@type)")
 738  	if rtype == 'void':
 739  	    break;
 740  	info = arg.xpathEval("string(@info)")
 741  	nam = arg.xpathEval("string(@name)")
 742          type = type_convert(rtype, nam, info, module, name, n)
 743  	if is_known_param_type(type, rtype) == 0:
 744  	    add_missing_type(type, name);
 745  	    no_gen = 1
 746          if (type[-3:] == 'Ptr' or type[-4:] == '_ptr') and \
 747  	    rtype[0:6] == 'const ':
 748  	    crtype = rtype[6:]
 749  	else:
 750  	    crtype = rtype
 751  	t_args.append((nam, type, rtype, crtype, info))
 752      
 753      try:
 754  	rets = node.xpathEval("return")
 755      except:
 756          rets = []
 757      t_ret = None
 758      for ret in rets:
 759          rtype = ret.xpathEval("string(@type)")
 760  	info = ret.xpathEval("string(@info)")
 761          type = type_convert(rtype, 'return', info, module, name, 0)
 762  	if rtype == 'void':
 763  	    break
 764  	if is_known_return_type(type) == 0:
 765  	    add_missing_type(type, name);
 766  	    no_gen = 1
 767  	t_ret = (type, rtype, info)
 768  	break
 769  
 770      test.write("""
 771  static int
 772  test_%s(void) {
 773      int test_ret = 0;
 774  
 775  """ % (name))
 776  
 777      if no_gen == 1:
 778          add_missing_functions(name, module)
 779  	test.write("""
 780      /* missing type support */
 781      return(test_ret);
 782  }
 783  
 784  """)
 785          return
 786  
 787      try:
 788  	conds = node.xpathEval("cond")
 789  	for cond in conds:
 790  	    test.write("#if %s\n" % (cond.get_content()))
 791  	    nb_cond = nb_cond + 1
 792      except:
 793          pass
 794  
 795      define = 0
 796      if function_defines.has_key(name):
 797          test.write("#ifdef %s\n" % (function_defines[name]))
 798  	define = 1
 799      
 800      # Declare the memory usage counter
 801      no_mem = is_skipped_memcheck(name)
 802      if no_mem == 0:
 803  	test.write("    int mem_base;\n");
 804  
 805      # Declare the return value
 806      if t_ret != None:
 807          test.write("    %s ret_val;\n" % (t_ret[1]))
 808  
 809      # Declare the arguments
 810      for arg in t_args:
 811          (nam, type, rtype, crtype, info) = arg;
 812          # add declaration
 813          test.write("    %s %s; /* %s */\n" % (crtype, nam, info))
 814          if name in use_strlen and nam in use_strlen[name]:
 815              continue
 816          test.write("    int n_%s;\n" % (nam))
 817      test.write("\n")
 818  
 819      # Cascade loop on of each argument list of values
 820      for arg in t_args:
 821          (nam, type, rtype, crtype, info) = arg
 822          if name in use_strlen and nam in use_strlen[name]:
 823              continue
 824          #
 825          test.write("    for (n_%s = 0;n_%s < gen_nb_%s;n_%s++) {\n" % (
 826                     nam, nam, type, nam))
 827      
 828      # log the memory usage
 829      if no_mem == 0:
 830  	test.write("        mem_base = xmlMemBlocks();\n");
 831  
 832      # prepare the call
 833      i = 0
 834      swap_statement = None
 835      for arg in t_args:
 836          (nam, type, rtype, crtype, info) = arg
 837          #
 838          if name in use_strlen and nam in use_strlen[name]:
 839              buffer_arg = use_strlen[name][nam]
 840              statement = "        %s = %s ? strlen((const char *)%s) : 0;\n" % (nam, buffer_arg, buffer_arg)
 841              if "-reorder-length" in use_strlen[name]:
 842                  swap_statement = {buffer_arg: statement}
 843              else:
 844                  test.write(statement)
 845                  if "-compute-length" in use_strlen[name]:
 846                      start_arg = use_strlen[name]["-compute-length"]
 847                      test.write("        %s = ((%s > 0) && (%s > %s)) ? (%s - %s) : 0;\n" % (
 848                                 nam, nam, nam, start_arg, nam, start_arg))
 849          else:
 850              test.write("        %s = gen_%s(n_%s, %d);\n" % (nam, type, nam, i))
 851          if swap_statement and nam in swap_statement:
 852              test.write(swap_statement[nam])
 853              swap_statement = None
 854          i = i + 1
 855  
 856      # do the call, and clanup the result
 857      if extra_pre_call.has_key(name):
 858  	test.write("        %s\n"% (extra_pre_call[name]))
 859      if t_ret != None:
 860  	test.write("\n        ret_val = %s(" % (name))
 861  	need = 0
 862  	for arg in t_args:
 863  	    (nam, type, rtype, crtype, info) = arg
 864  	    if need:
 865  	        test.write(", ")
 866  	    else:
 867  	        need = 1
 868  	    if rtype != crtype:
 869  	        test.write("(%s)" % rtype)
 870  	    test.write("%s" % nam);
 871  	test.write(");\n")
 872  	if extra_post_call.has_key(name):
 873  	    test.write("        %s\n"% (extra_post_call[name]))
 874  	test.write("        desret_%s(ret_val);\n" % t_ret[0])
 875      else:
 876  	test.write("\n        %s(" % (name));
 877  	need = 0;
 878  	for arg in t_args:
 879  	    (nam, type, rtype, crtype, info) = arg;
 880  	    if need:
 881  	        test.write(", ")
 882  	    else:
 883  	        need = 1
 884  	    if rtype != crtype:
 885  	        test.write("(%s)" % rtype)
 886  	    test.write("%s" % nam)
 887  	test.write(");\n")
 888  	if extra_post_call.has_key(name):
 889  	    test.write("        %s\n"% (extra_post_call[name]))
 890  
 891      test.write("        call_tests++;\n");
 892  
 893      # Free the arguments
 894      i = 0
 895      for arg in t_args:
 896          (nam, type, rtype, crtype, info) = arg;
 897          if name in use_strlen and nam in use_strlen[name]:
 898              i = i + 1
 899              continue
 900          # This is a hack to prevent generating a destructor for the
 901          # 'input' argument in xmlTextReaderSetup.  There should be
 902          # a better, more generic way to do this!
 903          if string.find(info, 'destroy') == -1:
 904              test.write("        des_%s(n_%s, " % (type, nam))
 905              if rtype != crtype:
 906                  test.write("(%s)" % rtype)
 907              test.write("%s, %d);\n" % (nam, i))
 908          i = i + 1
 909  
 910      test.write("        xmlResetLastError();\n");
 911      # Check the memory usage
 912      if no_mem == 0:
 913          test.write("""        if (mem_base != xmlMemBlocks()) {
 914              printf("Leak of %%d blocks found in %s",
 915  	           xmlMemBlocks() - mem_base);
 916  	    test_ret++;
 917  """ % (name));
 918          for arg in t_args:
 919              (nam, type, rtype, crtype, info) = arg
 920              if name in use_strlen and nam in use_strlen[name]:
 921                  continue
 922              test.write("""            printf(" %%d", n_%s);\n""" % (nam))
 923          test.write("""            printf("\\n");\n""")
 924          test.write("        }\n")
 925  
 926      for arg in t_args:
 927          if name in use_strlen and arg[0] in use_strlen[name]:
 928              continue
 929          test.write("    }\n")
 930  
 931      test.write("    function_tests++;\n")
 932      #
 933      # end of conditional
 934      #
 935      while nb_cond > 0:
 936          test.write("#endif\n")
 937  	nb_cond = nb_cond -1
 938      if define == 1:
 939          test.write("#endif\n")
 940  
 941      nb_tests = nb_tests + 1;
 942  
 943      test.write("""
 944      return(test_ret);
 945  }
 946  
 947  """)
 948      
 949  #
 950  # Generate all module callers
 951  #
 952  for module in modules:
 953      # gather all the functions exported by that module
 954      try:
 955  	functions = ctxt.xpathEval("/api/symbols/function[@file='%s']" % (module))
 956      except:
 957          print "Failed to gather functions from module %s" % (module)
 958  	continue;
 959  
 960      # iterate over all functions in the module generating the test
 961      i = 0
 962      nb_tests_old = nb_tests
 963      for function in functions:
 964          i = i + 1
 965          generate_test(module, function);
 966  
 967      # header
 968      test.write("""static int
 969  test_%s(void) {
 970      int test_ret = 0;
 971  
 972      if (quiet == 0) printf("Testing %s : %d of %d functions ...\\n");
 973  """ % (module, module, nb_tests - nb_tests_old, i))
 974  
 975      # iterate over all functions in the module generating the call
 976      for function in functions:
 977          name = function.xpathEval('string(@name)')
 978  	if is_skipped_function(name):
 979  	    continue
 980  	test.write("    test_ret += test_%s();\n" % (name))
 981  
 982      # footer
 983      test.write("""
 984      if (test_ret != 0)
 985  	printf("Module %s: %%d errors\\n", test_ret);
 986      return(test_ret);
 987  }
 988  """ % (module))
 989  
 990  #
 991  # Generate direct module caller
 992  #
 993  test.write("""static int
 994  test_module(const char *module) {
 995  """);
 996  for module in modules:
 997      test.write("""    if (!strcmp(module, "%s")) return(test_%s());\n""" % (
 998          module, module))
 999  test.write("""    return(0);
1000  }
1001  """);
1002  
1003  print "Generated test for %d modules and %d functions" %(len(modules), nb_tests)
1004  
1005  compare_and_save()
1006  
1007  missing_list = []
1008  for missing in missing_types.keys():
1009      if missing == 'va_list' or missing == '...':
1010          continue;
1011  
1012      n = len(missing_types[missing])
1013      missing_list.append((n, missing))
1014  
1015  def compare_missing(a, b):
1016      return b[0] - a[0]
1017  
1018  missing_list.sort(compare_missing)
1019  print "Missing support for %d functions and %d types see missing.lst" % (missing_functions_nr, len(missing_list))
1020  lst = open("missing.lst", "w")
1021  lst.write("Missing support for %d types" % (len(missing_list)))
1022  lst.write("\n")
1023  for miss in missing_list:
1024      lst.write("%s: %d :" % (miss[1], miss[0]))
1025      i = 0
1026      for n in missing_types[miss[1]]:
1027          i = i + 1
1028          if i > 5:
1029  	    lst.write(" ...")
1030  	    break
1031  	lst.write(" %s" % (n))
1032      lst.write("\n")
1033  lst.write("\n")
1034  lst.write("\n")
1035  lst.write("Missing support per module");
1036  for module in missing_functions.keys():
1037      lst.write("module %s:\n   %s\n" % (module, missing_functions[module]))
1038  
1039  lst.close()
1040  
1041