text: /*
   * runsuite.c: C program to run libxml2 againts published testsuites 
   *
   * See Copyright for the status of this software.
   *
   * daniel@veillard.com
   */
  
  #ifdef HAVE_CONFIG_H
  #include "libxml.h"
  #else
  #include <stdio.h>
  #endif
  
  #if !defined(_WIN32) || defined(__CYGWIN__)
  #include <unistd.h>
  #endif
  #include <string.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <fcntl.h>
  
  #include <libxml/parser.h>
  #include <libxml/parserInternals.h>
  #include <libxml/tree.h>
  #include <libxml/uri.h>
  #if defined(LIBXML_SCHEMAS_ENABLED) && defined(LIBXML_XPATH_ENABLED)
  #include <libxml/xmlreader.h>
  
  #include <libxml/xpath.h>
  #include <libxml/xpathInternals.h>
  
  #include <libxml/relaxng.h>
  #include <libxml/xmlschemas.h>
  #include <libxml/xmlschemastypes.h>
  
  #define LOGFILE "runsuite.log"
  static FILE *logfile = NULL;
  static int verbose = 0;
  
  #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__)
  #define vsnprintf _vsnprintf
  #define snprintf _snprintf
  #endif
  
  /************************************************************************
   *									*
   *		File name and path utilities				*
   *									*
   ************************************************************************/
  
  static int checkTestFile(const char *filename) {
      struct stat buf;
  
      if (stat(filename, &buf) == -1)
          return(0);
  
  #if defined(_WIN32) && !defined(__CYGWIN__)
      if (!(buf.st_mode & _S_IFREG))
          return(0);
  #else
      if (!S_ISREG(buf.st_mode))
          return(0);
  #endif
  
      return(1);
  }
  
  static xmlChar *composeDir(const xmlChar *dir, const xmlChar *path) {
      char buf[500];
  
      if (dir == NULL) return(xmlStrdup(path));
      if (path == NULL) return(NULL);
  
      snprintf(buf, 500, "%s/%s", (const char *) dir, (const char *) path);
      return(xmlStrdup((const xmlChar *) buf));
  }
  
  /************************************************************************
   *									*
   *		Libxml2 specific routines				*
   *									*
   ************************************************************************/
  
  static int nb_tests = 0;
  static int nb_errors = 0;
  static int nb_internals = 0;
  static int nb_schematas = 0;
  static int nb_unimplemented = 0;
  static int nb_leaks = 0;
  static int extraMemoryFromResolver = 0;
  
  static int
  fatalError(void) {
      fprintf(stderr, "Exitting tests on fatal error\n");
      exit(1);
  }
  
  /*
   * that's needed to implement <resource>
   */
  #define MAX_ENTITIES 20
  static char *testEntitiesName[MAX_ENTITIES];
  static char *testEntitiesValue[MAX_ENTITIES];
  static int nb_entities = 0;
  static void resetEntities(void) {
      int i;
  
      for (i = 0;i < nb_entities;i++) {
          if (testEntitiesName[i] != NULL)
  	    xmlFree(testEntitiesName[i]);
          if (testEntitiesValue[i] != NULL)
  	    xmlFree(testEntitiesValue[i]);
      }
      nb_entities = 0;
  }
  static int addEntity(char *name, char *content) {
      if (nb_entities >= MAX_ENTITIES) {
  	fprintf(stderr, "Too many entities defined\n");
  	return(-1);
      }
      testEntitiesName[nb_entities] = name;
      testEntitiesValue[nb_entities] = content;
      nb_entities++;
      return(0);
  }
  
  /*
   * We need to trap calls to the resolver to not account memory for the catalog
   * which is shared to the current running test. We also don't want to have
   * network downloads modifying tests.
   */
  static xmlParserInputPtr 
  testExternalEntityLoader(const char *URL, const char *ID,
  			 xmlParserCtxtPtr ctxt) {
      xmlParserInputPtr ret;
      int i;
  
      for (i = 0;i < nb_entities;i++) {
          if (!strcmp(testEntitiesName[i], URL)) {
  	    ret = xmlNewStringInputStream(ctxt,
  	                (const xmlChar *) testEntitiesValue[i]);
  	    if (ret != NULL) {
  	        ret->filename = (const char *)
  		                xmlStrdup((xmlChar *)testEntitiesName[i]);
  	    }
  	    return(ret);
  	}
      }
      if (checkTestFile(URL)) {
  	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
      } else {
  	int memused = xmlMemUsed();
  	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
  	extraMemoryFromResolver += xmlMemUsed() - memused;
      }
  #if 0
      if (ret == NULL) {
          fprintf(stderr, "Failed to find resource %s\n", URL);
      }
  #endif
        
      return(ret);
  }
  
  /*
   * Trapping the error messages at the generic level to grab the equivalent of
   * stderr messages on CLI tools.
   */
  static char testErrors[32769];
  static int testErrorsSize = 0;
  
  static void test_log(const char *msg, ...) {
      va_list args;
      if (logfile != NULL) {
          fprintf(logfile, "\n------------\n");
  	va_start(args, msg);
  	vfprintf(logfile, msg, args);
  	va_end(args);
  	fprintf(logfile, "%s", testErrors);
  	testErrorsSize = 0; testErrors[0] = 0;
      }
      if (verbose) {
  	va_start(args, msg);
  	vfprintf(stderr, msg, args);
  	va_end(args);
      }
  }
  
  static void
  testErrorHandler(void *ctx  ATTRIBUTE_UNUSED, const char *msg, ...) {
      va_list args;
      int res;
  
      if (testErrorsSize >= 32768)
          return;
      va_start(args, msg);
      res = vsnprintf(&testErrors[testErrorsSize],
                      32768 - testErrorsSize,
  		    msg, args);
      va_end(args);
      if (testErrorsSize + res >= 32768) {
          /* buffer is full */
  	testErrorsSize = 32768;
  	testErrors[testErrorsSize] = 0;
      } else {
          testErrorsSize += res;
      }
      testErrors[testErrorsSize] = 0;
  }
  
  static xmlXPathContextPtr ctxtXPath;
  
  static void
  initializeLibxml2(void) {
      xmlGetWarningsDefaultValue = 0;
      xmlPedanticParserDefault(0);
  
      xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
      xmlInitParser();
      xmlSetExternalEntityLoader(testExternalEntityLoader);
      ctxtXPath = xmlXPathNewContext(NULL);
      /*
      * Deactivate the cache if created; otherwise we have to create/free it
      * for every test, since it will confuse the memory leak detection.
      * Note that normally this need not be done, since the cache is not
      * created until set explicitely with xmlXPathContextSetCache();
      * but for test purposes it is sometimes usefull to activate the
      * cache by default for the whole library.
      */
      if (ctxtXPath->cache != NULL)
  	xmlXPathContextSetCache(ctxtXPath, 0, -1, 0);
      /* used as default nanemspace in xstc tests */
      xmlXPathRegisterNs(ctxtXPath, BAD_CAST "ts", BAD_CAST "TestSuite");
      xmlXPathRegisterNs(ctxtXPath, BAD_CAST "xlink",
                         BAD_CAST "http://www.w3.org/1999/xlink");
      xmlSetGenericErrorFunc(NULL, testErrorHandler);
  #ifdef LIBXML_SCHEMAS_ENABLED
      xmlSchemaInitTypes();
      xmlRelaxNGInitTypes();
  #endif
  }
  
  static xmlNodePtr
  getNext(xmlNodePtr cur, const char *xpath) {
      xmlNodePtr ret = NULL;
      xmlXPathObjectPtr res;
      xmlXPathCompExprPtr comp;
  
      if ((cur == NULL)  || (cur->doc == NULL) || (xpath == NULL))
          return(NULL);
      ctxtXPath->doc = cur->doc;
      ctxtXPath->node = cur;
      comp = xmlXPathCompile(BAD_CAST xpath);
      if (comp == NULL) {
          fprintf(stderr, "Failed to compile %s\n", xpath);
  	return(NULL);
      }
      res = xmlXPathCompiledEval(comp, ctxtXPath);
      xmlXPathFreeCompExpr(comp);
      if (res == NULL)
          return(NULL);
      if ((res->type == XPATH_NODESET) &&
          (res->nodesetval != NULL) &&
  	(res->nodesetval->nodeNr > 0) &&
  	(res->nodesetval->nodeTab != NULL))
  	ret = res->nodesetval->nodeTab[0];
      xmlXPathFreeObject(res);
      return(ret);
  }
  
  static xmlChar *
  getString(xmlNodePtr cur, const char *xpath) {
      xmlChar *ret = NULL;
      xmlXPathObjectPtr res;
      xmlXPathCompExprPtr comp;
  
      if ((cur == NULL)  || (cur->doc == NULL) || (xpath == NULL))
          return(NULL);
      ctxtXPath->doc = cur->doc;
      ctxtXPath->node = cur;
      comp = xmlXPathCompile(BAD_CAST xpath);
      if (comp == NULL) {
          fprintf(stderr, "Failed to compile %s\n", xpath);
  	return(NULL);
      }
      res = xmlXPathCompiledEval(comp, ctxtXPath);
      xmlXPathFreeCompExpr(comp);
      if (res == NULL)
          return(NULL);
      if (res->type == XPATH_STRING) {
          ret = res->stringval;
  	res->stringval = NULL;
      }
      xmlXPathFreeObject(res);
      return(ret);
  }
  
  /************************************************************************
   *									*
   *		Test test/xsdtest/xsdtestsuite.xml			*
   *									*
   ************************************************************************/
  
  static int
  xsdIncorectTestCase(xmlNodePtr cur) {
      xmlNodePtr test;
      xmlBufferPtr buf;
      xmlRelaxNGParserCtxtPtr pctxt;
      xmlRelaxNGPtr rng = NULL;
      int ret = 0, memt;
  
      cur = getNext(cur, "./incorrect[1]");
      if (cur == NULL) {
          return(0);
      }
  
      test = getNext(cur, "./*");
      if (test == NULL) {
          test_log("Failed to find test in correct line %ld\n",
  	        xmlGetLineNo(cur));
          return(1);
      }
  
      memt = xmlMemUsed();
      extraMemoryFromResolver = 0;
      /*
       * dump the schemas to a buffer, then reparse it and compile the schemas
       */
      buf = xmlBufferCreate();
      if (buf == NULL) {
          fprintf(stderr, "out of memory !\n");
  	fatalError();
      }
      xmlNodeDump(buf, test->doc, test, 0, 0);
      pctxt = xmlRelaxNGNewMemParserCtxt((const char *)buf->content, buf->use);
      xmlRelaxNGSetParserErrors(pctxt,
           (xmlRelaxNGValidityErrorFunc) testErrorHandler,
           (xmlRelaxNGValidityWarningFunc) testErrorHandler,
  	 pctxt);
      rng = xmlRelaxNGParse(pctxt);
      xmlRelaxNGFreeParserCtxt(pctxt);
      if (rng != NULL) {
  	test_log("Failed to detect incorect RNG line %ld\n",
  		    xmlGetLineNo(test));
          ret = 1;
  	goto done;
      }
  
  done:
      if (buf != NULL)
  	xmlBufferFree(buf);
      if (rng != NULL)
          xmlRelaxNGFree(rng);
      xmlResetLastError();
      if ((memt < xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
  	test_log("Validation of tests starting line %ld leaked %d\n",
  		xmlGetLineNo(cur), xmlMemUsed() - memt);
  	nb_leaks++;
      }
      return(ret);
  }
  
  static void
  installResources(xmlNodePtr tst, const xmlChar *base) {
      xmlNodePtr test;
      xmlBufferPtr buf;
      xmlChar *name, *content, *res;
  
      buf = xmlBufferCreate();
      if (buf == NULL) {
          fprintf(stderr, "out of memory !\n");
  	fatalError();
      }
      xmlNodeDump(buf, tst->doc, tst, 0, 0);
  
      while (tst != NULL) {
  	test = getNext(tst, "./*");
  	if (test != NULL) {
  	    xmlBufferEmpty(buf);
  	    xmlNodeDump(buf, test->doc, test, 0, 0);
  	    name = getString(tst, "string(@name)");
  	    content = xmlStrdup(buf->content);
  	    if ((name != NULL) && (content != NULL)) {
  	        res = composeDir(base, name);
  		xmlFree(name);
  	        addEntity((char *) res, (char *) content);
  	    } else {
  	        if (name != NULL) xmlFree(name);
  	        if (content != NULL) xmlFree(content);
  	    }
  	}
  	tst = getNext(tst, "following-sibling::resource[1]");
      }
      if (buf != NULL)
  	xmlBufferFree(buf);
  }
  
  static void
  installDirs(xmlNodePtr tst, const xmlChar *base) {
      xmlNodePtr test;
      xmlChar *name, *res;
  
      name = getString(tst, "string(@name)");
      if (name == NULL)
          return;
      res = composeDir(base, name);
      xmlFree(name);
      if (res == NULL) {
  	return;
      }
      /* Now process resources and subdir recursively */
      test = getNext(tst, "./resource[1]");
      if (test != NULL) {
          installResources(test, res);
      }
      test = getNext(tst, "./dir[1]");
      while (test != NULL) {
          installDirs(test, res);
  	test = getNext(test, "following-sibling::dir[1]");
      }
      xmlFree(res);
  }
  
  static int 
  xsdTestCase(xmlNodePtr tst) {
      xmlNodePtr test, tmp, cur;
      xmlBufferPtr buf;
      xmlDocPtr doc = NULL;
      xmlRelaxNGParserCtxtPtr pctxt;
      xmlRelaxNGValidCtxtPtr ctxt;
      xmlRelaxNGPtr rng = NULL;
      int ret = 0, mem, memt;
      xmlChar *dtd;
  
      resetEntities();
      testErrorsSize = 0; testErrors[0] = 0;
  
      tmp = getNext(tst, "./dir[1]");
      if (tmp != NULL) {
          installDirs(tmp, NULL);
      }
      tmp = getNext(tst, "./resource[1]");
      if (tmp != NULL) {
          installResources(tmp, NULL);
      }
  
      cur = getNext(tst, "./correct[1]");
      if (cur == NULL) {
          return(xsdIncorectTestCase(tst));
      }
      
      test = getNext(cur, "./*");
      if (test == NULL) {
          fprintf(stderr, "Failed to find test in correct line %ld\n",
  	        xmlGetLineNo(cur));
          return(1);
      }
  
      memt = xmlMemUsed();
      extraMemoryFromResolver = 0;
      /*
       * dump the schemas to a buffer, then reparse it and compile the schemas
       */
      buf = xmlBufferCreate();
      if (buf == NULL) {
          fprintf(stderr, "out of memory !\n");
  	fatalError();
      }
      xmlNodeDump(buf, test->doc, test, 0, 0);
      pctxt = xmlRelaxNGNewMemParserCtxt((const char *)buf->content, buf->use);
      xmlRelaxNGSetParserErrors(pctxt,
           (xmlRelaxNGValidityErrorFunc) testErrorHandler,
           (xmlRelaxNGValidityWarningFunc) testErrorHandler,
  	 pctxt);
      rng = xmlRelaxNGParse(pctxt);
      xmlRelaxNGFreeParserCtxt(pctxt);
      if (extraMemoryFromResolver)
          memt = 0;
  
      if (rng == NULL) {
          test_log("Failed to parse RNGtest line %ld\n",
  	        xmlGetLineNo(test));
  	nb_errors++;
          ret = 1;
  	goto done;
      }
      /*
       * now scan all the siblings of correct to process the <valid> tests
       */
      tmp = getNext(cur, "following-sibling::valid[1]");
      while (tmp != NULL) {
  	dtd = xmlGetProp(tmp, BAD_CAST "dtd");
  	test = getNext(tmp, "./*");
  	if (test == NULL) {
  	    fprintf(stderr, "Failed to find test in <valid> line %ld\n",
  		    xmlGetLineNo(tmp));
  	    
  	} else {
  	    xmlBufferEmpty(buf);
  	    if (dtd != NULL)
  		xmlBufferAdd(buf, dtd, -1);
  	    xmlNodeDump(buf, test->doc, test, 0, 0);
  
  	    /*
  	     * We are ready to run the test
  	     */
  	    mem = xmlMemUsed();
  	    extraMemoryFromResolver = 0;
              doc = xmlReadMemory((const char *)buf->content, buf->use,
  	                        "test", NULL, 0);
  	    if (doc == NULL) {
  		test_log("Failed to parse valid instance line %ld\n",
  			xmlGetLineNo(tmp));
  		nb_errors++;
  	    } else {
  		nb_tests++;
  	        ctxt = xmlRelaxNGNewValidCtxt(rng);
  		xmlRelaxNGSetValidErrors(ctxt,
  		     (xmlRelaxNGValidityErrorFunc) testErrorHandler,
  		     (xmlRelaxNGValidityWarningFunc) testErrorHandler,
  		     ctxt);
  		ret = xmlRelaxNGValidateDoc(ctxt, doc);
  		xmlRelaxNGFreeValidCtxt(ctxt);
  		if (ret > 0) {
  		    test_log("Failed to validate valid instance line %ld\n",
  				xmlGetLineNo(tmp));
  		    nb_errors++;
  		} else if (ret < 0) {
  		    test_log("Internal error validating instance line %ld\n",
  			    xmlGetLineNo(tmp));
  		    nb_errors++;
  		}
  		xmlFreeDoc(doc);
  	    }
  	    xmlResetLastError();
  	    if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
  	        test_log("Validation of instance line %ld leaked %d\n",
  		        xmlGetLineNo(tmp), xmlMemUsed() - mem);
  		xmlMemoryDump();
  	        nb_leaks++;
  	    }
  	}
  	if (dtd != NULL)
  	    xmlFree(dtd);
  	tmp = getNext(tmp, "following-sibling::valid[1]");
      }
      /*
       * now scan all the siblings of correct to process the <invalid> tests
       */
      tmp = getNext(cur, "following-sibling::invalid[1]");
      while (tmp != NULL) {
  	test = getNext(tmp, "./*");
  	if (test == NULL) {
  	    fprintf(stderr, "Failed to find test in <invalid> line %ld\n",
  		    xmlGetLineNo(tmp));
  	    
  	} else {
  	    xmlBufferEmpty(buf);
  	    xmlNodeDump(buf, test->doc, test, 0, 0);
  
  	    /*
  	     * We are ready to run the test
  	     */
  	    mem = xmlMemUsed();
  	    extraMemoryFromResolver = 0;
              doc = xmlReadMemory((const char *)buf->content, buf->use,
  	                        "test", NULL, 0);
  	    if (doc == NULL) {
  		test_log("Failed to parse valid instance line %ld\n",
  			xmlGetLineNo(tmp));
  		nb_errors++;
  	    } else {
  		nb_tests++;
  	        ctxt = xmlRelaxNGNewValidCtxt(rng);
  		xmlRelaxNGSetValidErrors(ctxt,
  		     (xmlRelaxNGValidityErrorFunc) testErrorHandler,
  		     (xmlRelaxNGValidityWarningFunc) testErrorHandler,
  		     ctxt);
  		ret = xmlRelaxNGValidateDoc(ctxt, doc);
  		xmlRelaxNGFreeValidCtxt(ctxt);
  		if (ret == 0) {
  		    test_log("Failed to detect invalid instance line %ld\n",
  				xmlGetLineNo(tmp));
  		    nb_errors++;
  		} else if (ret < 0) {
  		    test_log("Internal error validating instance line %ld\n",
  			    xmlGetLineNo(tmp));
  		    nb_errors++;
  		}
  		xmlFreeDoc(doc);
  	    }
  	    xmlResetLastError();
  	    if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
  	        test_log("Validation of instance line %ld leaked %d\n",
  		        xmlGetLineNo(tmp), xmlMemUsed() - mem);
  		xmlMemoryDump();
  	        nb_leaks++;
  	    }
  	}
  	tmp = getNext(tmp, "following-sibling::invalid[1]");
      }
  
  done:
      if (buf != NULL)
  	xmlBufferFree(buf);
      if (rng != NULL)
          xmlRelaxNGFree(rng);
      xmlResetLastError();
      if ((memt != xmlMemUsed()) && (memt != 0)) {
  	test_log("Validation of tests starting line %ld leaked %d\n",
  		xmlGetLineNo(cur), xmlMemUsed() - memt);
  	nb_leaks++;
      }
      return(ret);
  }
  
  static int 
  xsdTestSuite(xmlNodePtr cur) {
      if (verbose) {
  	xmlChar *doc = getString(cur, "string(documentation)");
  
  	if (doc != NULL) {
  	    printf("Suite %s\n", doc);
  	    xmlFree(doc);
  	}
      }
      cur = getNext(cur, "./testCase[1]");
      while (cur != NULL) {
          xsdTestCase(cur);
  	cur = getNext(cur, "following-sibling::testCase[1]");
      }
          
      return(0);
  }
  
  static int 
  xsdTest(void) {
      xmlDocPtr doc;
      xmlNodePtr cur;
      const char *filename = "test/xsdtest/xsdtestsuite.xml";
      int ret = 0;
  
      doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT);
      if (doc == NULL) {
          fprintf(stderr, "Failed to parse %s\n", filename);
  	return(-1);
      }
      printf("## XML Schemas datatypes test suite from James Clark\n");
  
      cur = xmlDocGetRootElement(doc);
      if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
          fprintf(stderr, "Unexpected format %s\n", filename);
  	ret = -1;
  	goto done;
      }
  
      cur = getNext(cur, "./testSuite[1]");
      if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
          fprintf(stderr, "Unexpected format %s\n", filename);
  	ret = -1;
  	goto done;
      }
      while (cur != NULL) {
          xsdTestSuite(cur);
  	cur = getNext(cur, "following-sibling::testSuite[1]");
      }
  
  done:
      if (doc != NULL)
  	xmlFreeDoc(doc);
      return(ret);
  }
  
  static int 
  rngTestSuite(xmlNodePtr cur) {
      if (verbose) {
  	xmlChar *doc = getString(cur, "string(documentation)");
  
  	if (doc != NULL) {
  	    printf("Suite %s\n", doc);
  	    xmlFree(doc);
  	} else {
  	    doc = getString(cur, "string(section)");
  	    if (doc != NULL) {
  		printf("Section %s\n", doc);
  		xmlFree(doc);
  	    }
  	}
      }
      cur = getNext(cur, "./testSuite[1]");
      while (cur != NULL) {
          xsdTestSuite(cur);
  	cur = getNext(cur, "following-sibling::testSuite[1]");
      }
          
      return(0);
  }
  
  static int 
  rngTest1(void) {
      xmlDocPtr doc;
      xmlNodePtr cur;
      const char *filename = "test/relaxng/OASIS/spectest.xml";
      int ret = 0;
  
      doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT);
      if (doc == NULL) {
          fprintf(stderr, "Failed to parse %s\n", filename);
  	return(-1);
      }
      printf("## Relax NG test suite from James Clark\n");
  
      cur = xmlDocGetRootElement(doc);
      if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
          fprintf(stderr, "Unexpected format %s\n", filename);
  	ret = -1;
  	goto done;
      }
  
      cur = getNext(cur, "./testSuite[1]");
      if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
          fprintf(stderr, "Unexpected format %s\n", filename);
  	ret = -1;
  	goto done;
      }
      while (cur != NULL) {
          rngTestSuite(cur);
  	cur = getNext(cur, "following-sibling::testSuite[1]");
      }
  
  done:
      if (doc != NULL)
  	xmlFreeDoc(doc);
      return(ret);
  }
  
  static int 
  rngTest2(void) {
      xmlDocPtr doc;
      xmlNodePtr cur;
      const char *filename = "test/relaxng/testsuite.xml";
      int ret = 0;
  
      doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT);
      if (doc == NULL) {
          fprintf(stderr, "Failed to parse %s\n", filename);
  	return(-1);
      }
      printf("## Relax NG test suite for libxml2\n");
  
      cur = xmlDocGetRootElement(doc);
      if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
          fprintf(stderr, "Unexpected format %s\n", filename);
  	ret = -1;
  	goto done;
      }
  
      cur = getNext(cur, "./testSuite[1]");
      if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
          fprintf(stderr, "Unexpected format %s\n", filename);
  	ret = -1;
  	goto done;
      }
      while (cur != NULL) {
          xsdTestSuite(cur);
  	cur = getNext(cur, "following-sibling::testSuite[1]");
      }
  
  done:
      if (doc != NULL)
  	xmlFreeDoc(doc);
      return(ret);
  }
  
  /************************************************************************
   *									*
   *		Schemas test suites from W3C/NIST/MS/Sun		*
   *									*
   ************************************************************************/
  
  static int
  xstcTestInstance(xmlNodePtr cur, xmlSchemaPtr schemas,
                   const xmlChar *spath, const char *base) {
      xmlChar *href = NULL;
      xmlChar *path = NULL;
      xmlChar *validity = NULL;
      xmlSchemaValidCtxtPtr ctxt = NULL;
      xmlDocPtr doc = NULL;
      int ret = 0, mem;
  
      xmlResetLastError();
      testErrorsSize = 0; testErrors[0] = 0;
      mem = xmlMemUsed();
      href = getString(cur,
                       "string(ts:instanceDocument/@xlink:href)");
      if ((href == NULL) || (href[0] == 0)) {
  	test_log("testGroup line %ld misses href for schemaDocument\n",
  		    xmlGetLineNo(cur));
  	ret = -1;
  	goto done;
      }
      path = xmlBuildURI(href, BAD_CAST base);
      if (path == NULL) {
  	fprintf(stderr,
  	        "Failed to build path to schemas testGroup line %ld : %s\n",
  		xmlGetLineNo(cur), href);
  	ret = -1;
  	goto done;
      }
      if (checkTestFile((const char *) path) <= 0) {
  	test_log("schemas for testGroup line %ld is missing: %s\n",
  		xmlGetLineNo(cur), path);
  	ret = -1;
  	goto done;
      }
      validity = getString(cur,
                           "string(ts:expected/@validity)");
      if (validity == NULL) {
          fprintf(stderr, "instanceDocument line %ld misses expected validity\n",
  	        xmlGetLineNo(cur));
  	ret = -1;
  	goto done;
      }
      nb_tests++;
      doc = xmlReadFile((const char *) path, NULL, XML_PARSE_NOENT);
      if (doc == NULL) {
          fprintf(stderr, "instance %s fails to parse\n", path);
  	ret = -1;
  	nb_errors++;
  	goto done;
      }
  
      ctxt = xmlSchemaNewValidCtxt(schemas);
      xmlSchemaSetValidErrors(ctxt,
           (xmlSchemaValidityErrorFunc) testErrorHandler,
           (xmlSchemaValidityWarningFunc) testErrorHandler,
  	 ctxt);
      ret = xmlSchemaValidateDoc(ctxt, doc);
  
      if (xmlStrEqual(validity, BAD_CAST "valid")) {
  	if (ret > 0) {
  	    test_log("valid instance %s failed to validate against %s\n",
  			path, spath);
  	    nb_errors++;
  	} else if (ret < 0) {
  	    test_log("valid instance %s got internal error validating %s\n",
  			path, spath);
  	    nb_internals++;
  	    nb_errors++;
  	}
      } else if (xmlStrEqual(validity, BAD_CAST "invalid")) {
  	if (ret == 0) {
  	    test_log("Failed to detect invalid instance %s against %s\n",
  			path, spath);
  	    nb_errors++;
  	}
      } else {
          test_log("instanceDocument line %ld has unexpected validity value%s\n",
  	        xmlGetLineNo(cur), validity);
  	ret = -1;
  	goto done;
      }
  
  done:
      if (href != NULL) xmlFree(href);
      if (path != NULL) xmlFree(path);
      if (validity != NULL) xmlFree(validity);
      if (ctxt != NULL) xmlSchemaFreeValidCtxt(ctxt);
      if (doc != NULL) xmlFreeDoc(doc);
      xmlResetLastError();
      if (mem != xmlMemUsed()) {
  	test_log("Validation of tests starting line %ld leaked %d\n",
  		xmlGetLineNo(cur), xmlMemUsed() - mem);
  	nb_leaks++;
      }
      return(ret);
  }
  
  static int
  xstcTestGroup(xmlNodePtr cur, const char *base) {
      xmlChar *href = NULL;
      xmlChar *path = NULL;
      xmlChar *validity = NULL;
      xmlSchemaPtr schemas = NULL;
      xmlSchemaParserCtxtPtr ctxt;
      xmlNodePtr instance;
      int ret = 0, mem;
  
      xmlResetLastError();
      testErrorsSize = 0; testErrors[0] = 0;
      mem = xmlMemUsed();
      href = getString(cur,
                       "string(ts:schemaTest/ts:schemaDocument/@xlink:href)");
      if ((href == NULL) || (href[0] == 0)) {
          test_log("testGroup line %ld misses href for schemaDocument\n",
  		    xmlGetLineNo(cur));
  	ret = -1;
  	goto done;
      }
      path = xmlBuildURI(href, BAD_CAST base);
      if (path == NULL) {
  	test_log("Failed to build path to schemas testGroup line %ld : %s\n",
  		xmlGetLineNo(cur), href);
  	ret = -1;
  	goto done;
      }
      if (checkTestFile((const char *) path) <= 0) {
  	test_log("schemas for testGroup line %ld is missing: %s\n",
  		xmlGetLineNo(cur), path);
  	ret = -1;
  	goto done;
      }
      validity = getString(cur,
                           "string(ts:schemaTest/ts:expected/@validity)");
      if (validity == NULL) {
          test_log("testGroup line %ld misses expected validity\n",
  	        xmlGetLineNo(cur));
  	ret = -1;
  	goto done;
      }
      nb_tests++;
      if (xmlStrEqual(validity, BAD_CAST "valid")) {
          nb_schematas++;
  	ctxt = xmlSchemaNewParserCtxt((const char *) path);
  	xmlSchemaSetParserErrors(ctxt,
  	     (xmlSchemaValidityErrorFunc) testErrorHandler,
  	     (xmlSchemaValidityWarningFunc) testErrorHandler,
  	     ctxt);
  	schemas = xmlSchemaParse(ctxt);
  	xmlSchemaFreeParserCtxt(ctxt);
  	if (schemas == NULL) {
  	    test_log("valid schemas %s failed to parse\n",
  			path);
  	    ret = 1;
  	    nb_errors++;
  	}
  	if ((ret == 0) && (strstr(testErrors, "nimplemented") != NULL)) {
  	    test_log("valid schemas %s hit an unimplemented block\n",
  			path);
  	    ret = 1;
  	    nb_unimplemented++;
  	    nb_errors++;
  	}
  	instance = getNext(cur, "./ts:instanceTest[1]");
  	while (instance != NULL) {
  	    if (schemas != NULL) {
  		xstcTestInstance(instance, schemas, path, base);		
  	    } else {
  		/*
  		* We'll automatically mark the instances as failed
  		* if the schema was broken.
  		*/
  		nb_errors++;
  	    }
  	    instance = getNext(instance,
  		"following-sibling::ts:instanceTest[1]");
  	}
      } else if (xmlStrEqual(validity, BAD_CAST "invalid")) {
          nb_schematas++;
  	ctxt = xmlSchemaNewParserCtxt((const char *) path);
  	xmlSchemaSetParserErrors(ctxt,
  	     (xmlSchemaValidityErrorFunc) testErrorHandler,
  	     (xmlSchemaValidityWarningFunc) testErrorHandler,
  	     ctxt);
  	schemas = xmlSchemaParse(ctxt);
  	xmlSchemaFreeParserCtxt(ctxt);
  	if (schemas != NULL) {
  	    test_log("Failed to detect error in schemas %s\n",
  			path);
  	    nb_errors++;
  	    ret = 1;
  	}
  	if ((ret == 0) && (strstr(testErrors, "nimplemented") != NULL)) {
  	    nb_unimplemented++;
  	    test_log("invalid schemas %s hit an unimplemented block\n",
  			path);
  	    ret = 1;
  	    nb_errors++;
  	}
      } else {
          test_log("testGroup line %ld misses unexpected validity value%s\n",
  	        xmlGetLineNo(cur), validity);
  	ret = -1;
  	goto done;
      }
  
  done:
      if (href != NULL) xmlFree(href);
      if (path != NULL) xmlFree(path);
      if (validity != NULL) xmlFree(validity);
      if (schemas != NULL) xmlSchemaFree(schemas);
      xmlResetLastError();
      if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
  	test_log("Processing test line %ld %s leaked %d\n",
  		xmlGetLineNo(cur), path, xmlMemUsed() - mem);
  	nb_leaks++;
      }
      return(ret);
  }
  
  static int
  xstcMetadata(const char *metadata, const char *base) {
      xmlDocPtr doc;
      xmlNodePtr cur;
      xmlChar *contributor;
      xmlChar *name;
      int ret = 0;
  
      doc = xmlReadFile(metadata, NULL, XML_PARSE_NOENT);
      if (doc == NULL) {
          fprintf(stderr, "Failed to parse %s\n", metadata);
  	return(-1);
      }
  
      cur = xmlDocGetRootElement(doc);
      if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSet"))) {
          fprintf(stderr, "Unexpected format %s\n", metadata);
  	return(-1);
      }
      contributor = xmlGetProp(cur, BAD_CAST "contributor");
      if (contributor == NULL) {
          contributor = xmlStrdup(BAD_CAST "Unknown");
      }
      name = xmlGetProp(cur, BAD_CAST "name");
      if (name == NULL) {
          name = xmlStrdup(BAD_CAST "Unknown");
      }
      printf("## %s test suite for Schemas version %s\n", contributor, name);
      xmlFree(contributor);
      xmlFree(name);
  
      cur = getNext(cur, "./ts:testGroup[1]");
      if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testGroup"))) {
          fprintf(stderr, "Unexpected format %s\n", metadata);
  	ret = -1;
  	goto done;
      }
      while (cur != NULL) {
          xstcTestGroup(cur, base);
  	cur = getNext(cur, "following-sibling::ts:testGroup[1]");
      }
  
  done:
      xmlFreeDoc(doc);
      return(ret);
  }
  
  /************************************************************************
   *									*
   *		The driver for the tests				*
   *									*
   ************************************************************************/
  
  int
  main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
      int ret = 0;
      int old_errors, old_tests, old_leaks;
  
      logfile = fopen(LOGFILE, "w");
      if (logfile == NULL) {
          fprintf(stderr,
  	        "Could not open the log file, running in verbose mode\n");
  	verbose = 1;
      }
      initializeLibxml2();
  
      if ((argc >= 2) && (!strcmp(argv[1], "-v")))
          verbose = 1;
  
  
      old_errors = nb_errors;
      old_tests = nb_tests;
      old_leaks = nb_leaks;
      xsdTest();
      if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
  	printf("Ran %d tests, no errors\n", nb_tests - old_tests);
      else
  	printf("Ran %d tests, %d errors, %d leaks\n",
  	       nb_tests - old_tests,
  	       nb_errors - old_errors,
  	       nb_leaks - old_leaks);
      old_errors = nb_errors;
      old_tests = nb_tests;
      old_leaks = nb_leaks;
      rngTest1();
      if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
  	printf("Ran %d tests, no errors\n", nb_tests - old_tests);
      else
  	printf("Ran %d tests, %d errors, %d leaks\n",
  	       nb_tests - old_tests,
  	       nb_errors - old_errors,
  	       nb_leaks - old_leaks);
      old_errors = nb_errors;
      old_tests = nb_tests;
      old_leaks = nb_leaks;
      rngTest2();
      if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
  	printf("Ran %d tests, no errors\n", nb_tests - old_tests);
      else
  	printf("Ran %d tests, %d errors, %d leaks\n",
  	       nb_tests - old_tests,
  	       nb_errors - old_errors,
  	       nb_leaks - old_leaks);
      old_errors = nb_errors;
      old_tests = nb_tests;
      old_leaks = nb_leaks;
      nb_internals = 0;
      nb_schematas = 0;
      xstcMetadata("xstc/Tests/Metadata/NISTXMLSchemaDatatypes.testSet",
  		 "xstc/Tests/Metadata/");
      if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
  	printf("Ran %d tests (%d schemata), no errors\n",
  	       nb_tests - old_tests, nb_schematas);
      else
  	printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n",
  	       nb_tests - old_tests,
  	       nb_schematas,
  	       nb_errors - old_errors,
  	       nb_internals,
  	       nb_leaks - old_leaks);
      old_errors = nb_errors;
      old_tests = nb_tests;
      old_leaks = nb_leaks;
      nb_internals = 0;
      nb_schematas = 0;
      xstcMetadata("xstc/Tests/Metadata/SunXMLSchema1-0-20020116.testSet",
  		 "xstc/Tests/");
      if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
  	printf("Ran %d tests (%d schemata), no errors\n",
  	       nb_tests - old_tests, nb_schematas);
      else
  	printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n",
  	       nb_tests - old_tests,
  	       nb_schematas,
  	       nb_errors - old_errors,
  	       nb_internals,
  	       nb_leaks - old_leaks);
      old_errors = nb_errors;
      old_tests = nb_tests;
      old_leaks = nb_leaks;
      nb_internals = 0;
      nb_schematas = 0;
      xstcMetadata("xstc/Tests/Metadata/MSXMLSchema1-0-20020116.testSet",
  		 "xstc/Tests/");
      if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
  	printf("Ran %d tests (%d schemata), no errors\n",
  	       nb_tests - old_tests, nb_schematas);
      else
  	printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n",
  	       nb_tests - old_tests,
  	       nb_schematas,
  	       nb_errors - old_errors,
  	       nb_internals,
  	       nb_leaks - old_leaks);
  
      if ((nb_errors == 0) && (nb_leaks == 0)) {
          ret = 0;
  	printf("Total %d tests, no errors\n",
  	       nb_tests);
      } else {
          ret = 1;
  	printf("Total %d tests, %d errors, %d leaks\n",
  	       nb_tests, nb_errors, nb_leaks);
      }
      xmlXPathFreeContext(ctxtXPath);
      xmlCleanupParser();
      xmlMemoryDump();
  
      if (logfile != NULL)
          fclose(logfile);
      return(ret);
  }
  #else /* !SCHEMAS */
  int
  main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
      fprintf(stderr, "runsuite requires support for schemas and xpath in libxml2\n");
  }
  #endif
  