| /* | 
 |  * 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__) | 
 |  | 
 | #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 |