File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libxml2 / runsuite.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 01:22:20 2013 UTC (11 years ago) by misho
Branches: libxml2, MAIN
CVS tags: v2_8_0p0, v2_8_0, HEAD
2.8.0

    1: /*
    2:  * runsuite.c: C program to run libxml2 againts published testsuites 
    3:  *
    4:  * See Copyright for the status of this software.
    5:  *
    6:  * daniel@veillard.com
    7:  */
    8: 
    9: #ifdef HAVE_CONFIG_H
   10: #include "libxml.h"
   11: #else
   12: #include <stdio.h>
   13: #endif
   14: 
   15: #if !defined(_WIN32) || defined(__CYGWIN__)
   16: #include <unistd.h>
   17: #endif
   18: #include <string.h>
   19: #include <sys/types.h>
   20: #include <sys/stat.h>
   21: #include <fcntl.h>
   22: 
   23: #include <libxml/parser.h>
   24: #include <libxml/parserInternals.h>
   25: #include <libxml/tree.h>
   26: #include <libxml/uri.h>
   27: #if defined(LIBXML_SCHEMAS_ENABLED) && defined(LIBXML_XPATH_ENABLED)
   28: #include <libxml/xmlreader.h>
   29: 
   30: #include <libxml/xpath.h>
   31: #include <libxml/xpathInternals.h>
   32: 
   33: #include <libxml/relaxng.h>
   34: #include <libxml/xmlschemas.h>
   35: #include <libxml/xmlschemastypes.h>
   36: 
   37: #define LOGFILE "runsuite.log"
   38: static FILE *logfile = NULL;
   39: static int verbose = 0;
   40: 
   41: #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__)
   42: #define vsnprintf _vsnprintf
   43: #define snprintf _snprintf
   44: #endif
   45: 
   46: /************************************************************************
   47:  *									*
   48:  *		File name and path utilities				*
   49:  *									*
   50:  ************************************************************************/
   51: 
   52: static int checkTestFile(const char *filename) {
   53:     struct stat buf;
   54: 
   55:     if (stat(filename, &buf) == -1)
   56:         return(0);
   57: 
   58: #if defined(_WIN32) && !defined(__CYGWIN__)
   59:     if (!(buf.st_mode & _S_IFREG))
   60:         return(0);
   61: #else
   62:     if (!S_ISREG(buf.st_mode))
   63:         return(0);
   64: #endif
   65: 
   66:     return(1);
   67: }
   68: 
   69: static xmlChar *composeDir(const xmlChar *dir, const xmlChar *path) {
   70:     char buf[500];
   71: 
   72:     if (dir == NULL) return(xmlStrdup(path));
   73:     if (path == NULL) return(NULL);
   74: 
   75:     snprintf(buf, 500, "%s/%s", (const char *) dir, (const char *) path);
   76:     return(xmlStrdup((const xmlChar *) buf));
   77: }
   78: 
   79: /************************************************************************
   80:  *									*
   81:  *		Libxml2 specific routines				*
   82:  *									*
   83:  ************************************************************************/
   84: 
   85: static int nb_tests = 0;
   86: static int nb_errors = 0;
   87: static int nb_internals = 0;
   88: static int nb_schematas = 0;
   89: static int nb_unimplemented = 0;
   90: static int nb_leaks = 0;
   91: static int extraMemoryFromResolver = 0;
   92: 
   93: static int
   94: fatalError(void) {
   95:     fprintf(stderr, "Exitting tests on fatal error\n");
   96:     exit(1);
   97: }
   98: 
   99: /*
  100:  * that's needed to implement <resource>
  101:  */
  102: #define MAX_ENTITIES 20
  103: static char *testEntitiesName[MAX_ENTITIES];
  104: static char *testEntitiesValue[MAX_ENTITIES];
  105: static int nb_entities = 0;
  106: static void resetEntities(void) {
  107:     int i;
  108: 
  109:     for (i = 0;i < nb_entities;i++) {
  110:         if (testEntitiesName[i] != NULL)
  111: 	    xmlFree(testEntitiesName[i]);
  112:         if (testEntitiesValue[i] != NULL)
  113: 	    xmlFree(testEntitiesValue[i]);
  114:     }
  115:     nb_entities = 0;
  116: }
  117: static int addEntity(char *name, char *content) {
  118:     if (nb_entities >= MAX_ENTITIES) {
  119: 	fprintf(stderr, "Too many entities defined\n");
  120: 	return(-1);
  121:     }
  122:     testEntitiesName[nb_entities] = name;
  123:     testEntitiesValue[nb_entities] = content;
  124:     nb_entities++;
  125:     return(0);
  126: }
  127: 
  128: /*
  129:  * We need to trap calls to the resolver to not account memory for the catalog
  130:  * which is shared to the current running test. We also don't want to have
  131:  * network downloads modifying tests.
  132:  */
  133: static xmlParserInputPtr 
  134: testExternalEntityLoader(const char *URL, const char *ID,
  135: 			 xmlParserCtxtPtr ctxt) {
  136:     xmlParserInputPtr ret;
  137:     int i;
  138: 
  139:     for (i = 0;i < nb_entities;i++) {
  140:         if (!strcmp(testEntitiesName[i], URL)) {
  141: 	    ret = xmlNewStringInputStream(ctxt,
  142: 	                (const xmlChar *) testEntitiesValue[i]);
  143: 	    if (ret != NULL) {
  144: 	        ret->filename = (const char *)
  145: 		                xmlStrdup((xmlChar *)testEntitiesName[i]);
  146: 	    }
  147: 	    return(ret);
  148: 	}
  149:     }
  150:     if (checkTestFile(URL)) {
  151: 	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
  152:     } else {
  153: 	int memused = xmlMemUsed();
  154: 	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
  155: 	extraMemoryFromResolver += xmlMemUsed() - memused;
  156:     }
  157: #if 0
  158:     if (ret == NULL) {
  159:         fprintf(stderr, "Failed to find resource %s\n", URL);
  160:     }
  161: #endif
  162:       
  163:     return(ret);
  164: }
  165: 
  166: /*
  167:  * Trapping the error messages at the generic level to grab the equivalent of
  168:  * stderr messages on CLI tools.
  169:  */
  170: static char testErrors[32769];
  171: static int testErrorsSize = 0;
  172: 
  173: static void test_log(const char *msg, ...) {
  174:     va_list args;
  175:     if (logfile != NULL) {
  176:         fprintf(logfile, "\n------------\n");
  177: 	va_start(args, msg);
  178: 	vfprintf(logfile, msg, args);
  179: 	va_end(args);
  180: 	fprintf(logfile, "%s", testErrors);
  181: 	testErrorsSize = 0; testErrors[0] = 0;
  182:     }
  183:     if (verbose) {
  184: 	va_start(args, msg);
  185: 	vfprintf(stderr, msg, args);
  186: 	va_end(args);
  187:     }
  188: }
  189: 
  190: static void
  191: testErrorHandler(void *ctx  ATTRIBUTE_UNUSED, const char *msg, ...) {
  192:     va_list args;
  193:     int res;
  194: 
  195:     if (testErrorsSize >= 32768)
  196:         return;
  197:     va_start(args, msg);
  198:     res = vsnprintf(&testErrors[testErrorsSize],
  199:                     32768 - testErrorsSize,
  200: 		    msg, args);
  201:     va_end(args);
  202:     if (testErrorsSize + res >= 32768) {
  203:         /* buffer is full */
  204: 	testErrorsSize = 32768;
  205: 	testErrors[testErrorsSize] = 0;
  206:     } else {
  207:         testErrorsSize += res;
  208:     }
  209:     testErrors[testErrorsSize] = 0;
  210: }
  211: 
  212: static xmlXPathContextPtr ctxtXPath;
  213: 
  214: static void
  215: initializeLibxml2(void) {
  216:     xmlGetWarningsDefaultValue = 0;
  217:     xmlPedanticParserDefault(0);
  218: 
  219:     xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
  220:     xmlInitParser();
  221:     xmlSetExternalEntityLoader(testExternalEntityLoader);
  222:     ctxtXPath = xmlXPathNewContext(NULL);
  223:     /*
  224:     * Deactivate the cache if created; otherwise we have to create/free it
  225:     * for every test, since it will confuse the memory leak detection.
  226:     * Note that normally this need not be done, since the cache is not
  227:     * created until set explicitely with xmlXPathContextSetCache();
  228:     * but for test purposes it is sometimes usefull to activate the
  229:     * cache by default for the whole library.
  230:     */
  231:     if (ctxtXPath->cache != NULL)
  232: 	xmlXPathContextSetCache(ctxtXPath, 0, -1, 0);
  233:     /* used as default nanemspace in xstc tests */
  234:     xmlXPathRegisterNs(ctxtXPath, BAD_CAST "ts", BAD_CAST "TestSuite");
  235:     xmlXPathRegisterNs(ctxtXPath, BAD_CAST "xlink",
  236:                        BAD_CAST "http://www.w3.org/1999/xlink");
  237:     xmlSetGenericErrorFunc(NULL, testErrorHandler);
  238: #ifdef LIBXML_SCHEMAS_ENABLED
  239:     xmlSchemaInitTypes();
  240:     xmlRelaxNGInitTypes();
  241: #endif
  242: }
  243: 
  244: static xmlNodePtr
  245: getNext(xmlNodePtr cur, const char *xpath) {
  246:     xmlNodePtr ret = NULL;
  247:     xmlXPathObjectPtr res;
  248:     xmlXPathCompExprPtr comp;
  249: 
  250:     if ((cur == NULL)  || (cur->doc == NULL) || (xpath == NULL))
  251:         return(NULL);
  252:     ctxtXPath->doc = cur->doc;
  253:     ctxtXPath->node = cur;
  254:     comp = xmlXPathCompile(BAD_CAST xpath);
  255:     if (comp == NULL) {
  256:         fprintf(stderr, "Failed to compile %s\n", xpath);
  257: 	return(NULL);
  258:     }
  259:     res = xmlXPathCompiledEval(comp, ctxtXPath);
  260:     xmlXPathFreeCompExpr(comp);
  261:     if (res == NULL)
  262:         return(NULL);
  263:     if ((res->type == XPATH_NODESET) &&
  264:         (res->nodesetval != NULL) &&
  265: 	(res->nodesetval->nodeNr > 0) &&
  266: 	(res->nodesetval->nodeTab != NULL))
  267: 	ret = res->nodesetval->nodeTab[0];
  268:     xmlXPathFreeObject(res);
  269:     return(ret);
  270: }
  271: 
  272: static xmlChar *
  273: getString(xmlNodePtr cur, const char *xpath) {
  274:     xmlChar *ret = NULL;
  275:     xmlXPathObjectPtr res;
  276:     xmlXPathCompExprPtr comp;
  277: 
  278:     if ((cur == NULL)  || (cur->doc == NULL) || (xpath == NULL))
  279:         return(NULL);
  280:     ctxtXPath->doc = cur->doc;
  281:     ctxtXPath->node = cur;
  282:     comp = xmlXPathCompile(BAD_CAST xpath);
  283:     if (comp == NULL) {
  284:         fprintf(stderr, "Failed to compile %s\n", xpath);
  285: 	return(NULL);
  286:     }
  287:     res = xmlXPathCompiledEval(comp, ctxtXPath);
  288:     xmlXPathFreeCompExpr(comp);
  289:     if (res == NULL)
  290:         return(NULL);
  291:     if (res->type == XPATH_STRING) {
  292:         ret = res->stringval;
  293: 	res->stringval = NULL;
  294:     }
  295:     xmlXPathFreeObject(res);
  296:     return(ret);
  297: }
  298: 
  299: /************************************************************************
  300:  *									*
  301:  *		Test test/xsdtest/xsdtestsuite.xml			*
  302:  *									*
  303:  ************************************************************************/
  304: 
  305: static int
  306: xsdIncorectTestCase(xmlNodePtr cur) {
  307:     xmlNodePtr test;
  308:     xmlBufferPtr buf;
  309:     xmlRelaxNGParserCtxtPtr pctxt;
  310:     xmlRelaxNGPtr rng = NULL;
  311:     int ret = 0, memt;
  312: 
  313:     cur = getNext(cur, "./incorrect[1]");
  314:     if (cur == NULL) {
  315:         return(0);
  316:     }
  317: 
  318:     test = getNext(cur, "./*");
  319:     if (test == NULL) {
  320:         test_log("Failed to find test in correct line %ld\n",
  321: 	        xmlGetLineNo(cur));
  322:         return(1);
  323:     }
  324: 
  325:     memt = xmlMemUsed();
  326:     extraMemoryFromResolver = 0;
  327:     /*
  328:      * dump the schemas to a buffer, then reparse it and compile the schemas
  329:      */
  330:     buf = xmlBufferCreate();
  331:     if (buf == NULL) {
  332:         fprintf(stderr, "out of memory !\n");
  333: 	fatalError();
  334:     }
  335:     xmlNodeDump(buf, test->doc, test, 0, 0);
  336:     pctxt = xmlRelaxNGNewMemParserCtxt((const char *)buf->content, buf->use);
  337:     xmlRelaxNGSetParserErrors(pctxt,
  338:          (xmlRelaxNGValidityErrorFunc) testErrorHandler,
  339:          (xmlRelaxNGValidityWarningFunc) testErrorHandler,
  340: 	 pctxt);
  341:     rng = xmlRelaxNGParse(pctxt);
  342:     xmlRelaxNGFreeParserCtxt(pctxt);
  343:     if (rng != NULL) {
  344: 	test_log("Failed to detect incorect RNG line %ld\n",
  345: 		    xmlGetLineNo(test));
  346:         ret = 1;
  347: 	goto done;
  348:     }
  349: 
  350: done:
  351:     if (buf != NULL)
  352: 	xmlBufferFree(buf);
  353:     if (rng != NULL)
  354:         xmlRelaxNGFree(rng);
  355:     xmlResetLastError();
  356:     if ((memt < xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
  357: 	test_log("Validation of tests starting line %ld leaked %d\n",
  358: 		xmlGetLineNo(cur), xmlMemUsed() - memt);
  359: 	nb_leaks++;
  360:     }
  361:     return(ret);
  362: }
  363: 
  364: static void
  365: installResources(xmlNodePtr tst, const xmlChar *base) {
  366:     xmlNodePtr test;
  367:     xmlBufferPtr buf;
  368:     xmlChar *name, *content, *res;
  369: 
  370:     buf = xmlBufferCreate();
  371:     if (buf == NULL) {
  372:         fprintf(stderr, "out of memory !\n");
  373: 	fatalError();
  374:     }
  375:     xmlNodeDump(buf, tst->doc, tst, 0, 0);
  376: 
  377:     while (tst != NULL) {
  378: 	test = getNext(tst, "./*");
  379: 	if (test != NULL) {
  380: 	    xmlBufferEmpty(buf);
  381: 	    xmlNodeDump(buf, test->doc, test, 0, 0);
  382: 	    name = getString(tst, "string(@name)");
  383: 	    content = xmlStrdup(buf->content);
  384: 	    if ((name != NULL) && (content != NULL)) {
  385: 	        res = composeDir(base, name);
  386: 		xmlFree(name);
  387: 	        addEntity((char *) res, (char *) content);
  388: 	    } else {
  389: 	        if (name != NULL) xmlFree(name);
  390: 	        if (content != NULL) xmlFree(content);
  391: 	    }
  392: 	}
  393: 	tst = getNext(tst, "following-sibling::resource[1]");
  394:     }
  395:     if (buf != NULL)
  396: 	xmlBufferFree(buf);
  397: }
  398: 
  399: static void
  400: installDirs(xmlNodePtr tst, const xmlChar *base) {
  401:     xmlNodePtr test;
  402:     xmlChar *name, *res;
  403: 
  404:     name = getString(tst, "string(@name)");
  405:     if (name == NULL)
  406:         return;
  407:     res = composeDir(base, name);
  408:     xmlFree(name);
  409:     if (res == NULL) {
  410: 	return;
  411:     }
  412:     /* Now process resources and subdir recursively */
  413:     test = getNext(tst, "./resource[1]");
  414:     if (test != NULL) {
  415:         installResources(test, res);
  416:     }
  417:     test = getNext(tst, "./dir[1]");
  418:     while (test != NULL) {
  419:         installDirs(test, res);
  420: 	test = getNext(test, "following-sibling::dir[1]");
  421:     }
  422:     xmlFree(res);
  423: }
  424: 
  425: static int 
  426: xsdTestCase(xmlNodePtr tst) {
  427:     xmlNodePtr test, tmp, cur;
  428:     xmlBufferPtr buf;
  429:     xmlDocPtr doc = NULL;
  430:     xmlRelaxNGParserCtxtPtr pctxt;
  431:     xmlRelaxNGValidCtxtPtr ctxt;
  432:     xmlRelaxNGPtr rng = NULL;
  433:     int ret = 0, mem, memt;
  434:     xmlChar *dtd;
  435: 
  436:     resetEntities();
  437:     testErrorsSize = 0; testErrors[0] = 0;
  438: 
  439:     tmp = getNext(tst, "./dir[1]");
  440:     if (tmp != NULL) {
  441:         installDirs(tmp, NULL);
  442:     }
  443:     tmp = getNext(tst, "./resource[1]");
  444:     if (tmp != NULL) {
  445:         installResources(tmp, NULL);
  446:     }
  447: 
  448:     cur = getNext(tst, "./correct[1]");
  449:     if (cur == NULL) {
  450:         return(xsdIncorectTestCase(tst));
  451:     }
  452:     
  453:     test = getNext(cur, "./*");
  454:     if (test == NULL) {
  455:         fprintf(stderr, "Failed to find test in correct line %ld\n",
  456: 	        xmlGetLineNo(cur));
  457:         return(1);
  458:     }
  459: 
  460:     memt = xmlMemUsed();
  461:     extraMemoryFromResolver = 0;
  462:     /*
  463:      * dump the schemas to a buffer, then reparse it and compile the schemas
  464:      */
  465:     buf = xmlBufferCreate();
  466:     if (buf == NULL) {
  467:         fprintf(stderr, "out of memory !\n");
  468: 	fatalError();
  469:     }
  470:     xmlNodeDump(buf, test->doc, test, 0, 0);
  471:     pctxt = xmlRelaxNGNewMemParserCtxt((const char *)buf->content, buf->use);
  472:     xmlRelaxNGSetParserErrors(pctxt,
  473:          (xmlRelaxNGValidityErrorFunc) testErrorHandler,
  474:          (xmlRelaxNGValidityWarningFunc) testErrorHandler,
  475: 	 pctxt);
  476:     rng = xmlRelaxNGParse(pctxt);
  477:     xmlRelaxNGFreeParserCtxt(pctxt);
  478:     if (extraMemoryFromResolver)
  479:         memt = 0;
  480: 
  481:     if (rng == NULL) {
  482:         test_log("Failed to parse RNGtest line %ld\n",
  483: 	        xmlGetLineNo(test));
  484: 	nb_errors++;
  485:         ret = 1;
  486: 	goto done;
  487:     }
  488:     /*
  489:      * now scan all the siblings of correct to process the <valid> tests
  490:      */
  491:     tmp = getNext(cur, "following-sibling::valid[1]");
  492:     while (tmp != NULL) {
  493: 	dtd = xmlGetProp(tmp, BAD_CAST "dtd");
  494: 	test = getNext(tmp, "./*");
  495: 	if (test == NULL) {
  496: 	    fprintf(stderr, "Failed to find test in <valid> line %ld\n",
  497: 		    xmlGetLineNo(tmp));
  498: 	    
  499: 	} else {
  500: 	    xmlBufferEmpty(buf);
  501: 	    if (dtd != NULL)
  502: 		xmlBufferAdd(buf, dtd, -1);
  503: 	    xmlNodeDump(buf, test->doc, test, 0, 0);
  504: 
  505: 	    /*
  506: 	     * We are ready to run the test
  507: 	     */
  508: 	    mem = xmlMemUsed();
  509: 	    extraMemoryFromResolver = 0;
  510:             doc = xmlReadMemory((const char *)buf->content, buf->use,
  511: 	                        "test", NULL, 0);
  512: 	    if (doc == NULL) {
  513: 		test_log("Failed to parse valid instance line %ld\n",
  514: 			xmlGetLineNo(tmp));
  515: 		nb_errors++;
  516: 	    } else {
  517: 		nb_tests++;
  518: 	        ctxt = xmlRelaxNGNewValidCtxt(rng);
  519: 		xmlRelaxNGSetValidErrors(ctxt,
  520: 		     (xmlRelaxNGValidityErrorFunc) testErrorHandler,
  521: 		     (xmlRelaxNGValidityWarningFunc) testErrorHandler,
  522: 		     ctxt);
  523: 		ret = xmlRelaxNGValidateDoc(ctxt, doc);
  524: 		xmlRelaxNGFreeValidCtxt(ctxt);
  525: 		if (ret > 0) {
  526: 		    test_log("Failed to validate valid instance line %ld\n",
  527: 				xmlGetLineNo(tmp));
  528: 		    nb_errors++;
  529: 		} else if (ret < 0) {
  530: 		    test_log("Internal error validating instance line %ld\n",
  531: 			    xmlGetLineNo(tmp));
  532: 		    nb_errors++;
  533: 		}
  534: 		xmlFreeDoc(doc);
  535: 	    }
  536: 	    xmlResetLastError();
  537: 	    if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
  538: 	        test_log("Validation of instance line %ld leaked %d\n",
  539: 		        xmlGetLineNo(tmp), xmlMemUsed() - mem);
  540: 		xmlMemoryDump();
  541: 	        nb_leaks++;
  542: 	    }
  543: 	}
  544: 	if (dtd != NULL)
  545: 	    xmlFree(dtd);
  546: 	tmp = getNext(tmp, "following-sibling::valid[1]");
  547:     }
  548:     /*
  549:      * now scan all the siblings of correct to process the <invalid> tests
  550:      */
  551:     tmp = getNext(cur, "following-sibling::invalid[1]");
  552:     while (tmp != NULL) {
  553: 	test = getNext(tmp, "./*");
  554: 	if (test == NULL) {
  555: 	    fprintf(stderr, "Failed to find test in <invalid> line %ld\n",
  556: 		    xmlGetLineNo(tmp));
  557: 	    
  558: 	} else {
  559: 	    xmlBufferEmpty(buf);
  560: 	    xmlNodeDump(buf, test->doc, test, 0, 0);
  561: 
  562: 	    /*
  563: 	     * We are ready to run the test
  564: 	     */
  565: 	    mem = xmlMemUsed();
  566: 	    extraMemoryFromResolver = 0;
  567:             doc = xmlReadMemory((const char *)buf->content, buf->use,
  568: 	                        "test", NULL, 0);
  569: 	    if (doc == NULL) {
  570: 		test_log("Failed to parse valid instance line %ld\n",
  571: 			xmlGetLineNo(tmp));
  572: 		nb_errors++;
  573: 	    } else {
  574: 		nb_tests++;
  575: 	        ctxt = xmlRelaxNGNewValidCtxt(rng);
  576: 		xmlRelaxNGSetValidErrors(ctxt,
  577: 		     (xmlRelaxNGValidityErrorFunc) testErrorHandler,
  578: 		     (xmlRelaxNGValidityWarningFunc) testErrorHandler,
  579: 		     ctxt);
  580: 		ret = xmlRelaxNGValidateDoc(ctxt, doc);
  581: 		xmlRelaxNGFreeValidCtxt(ctxt);
  582: 		if (ret == 0) {
  583: 		    test_log("Failed to detect invalid instance line %ld\n",
  584: 				xmlGetLineNo(tmp));
  585: 		    nb_errors++;
  586: 		} else if (ret < 0) {
  587: 		    test_log("Internal error validating instance line %ld\n",
  588: 			    xmlGetLineNo(tmp));
  589: 		    nb_errors++;
  590: 		}
  591: 		xmlFreeDoc(doc);
  592: 	    }
  593: 	    xmlResetLastError();
  594: 	    if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
  595: 	        test_log("Validation of instance line %ld leaked %d\n",
  596: 		        xmlGetLineNo(tmp), xmlMemUsed() - mem);
  597: 		xmlMemoryDump();
  598: 	        nb_leaks++;
  599: 	    }
  600: 	}
  601: 	tmp = getNext(tmp, "following-sibling::invalid[1]");
  602:     }
  603: 
  604: done:
  605:     if (buf != NULL)
  606: 	xmlBufferFree(buf);
  607:     if (rng != NULL)
  608:         xmlRelaxNGFree(rng);
  609:     xmlResetLastError();
  610:     if ((memt != xmlMemUsed()) && (memt != 0)) {
  611: 	test_log("Validation of tests starting line %ld leaked %d\n",
  612: 		xmlGetLineNo(cur), xmlMemUsed() - memt);
  613: 	nb_leaks++;
  614:     }
  615:     return(ret);
  616: }
  617: 
  618: static int 
  619: xsdTestSuite(xmlNodePtr cur) {
  620:     if (verbose) {
  621: 	xmlChar *doc = getString(cur, "string(documentation)");
  622: 
  623: 	if (doc != NULL) {
  624: 	    printf("Suite %s\n", doc);
  625: 	    xmlFree(doc);
  626: 	}
  627:     }
  628:     cur = getNext(cur, "./testCase[1]");
  629:     while (cur != NULL) {
  630:         xsdTestCase(cur);
  631: 	cur = getNext(cur, "following-sibling::testCase[1]");
  632:     }
  633:         
  634:     return(0);
  635: }
  636: 
  637: static int 
  638: xsdTest(void) {
  639:     xmlDocPtr doc;
  640:     xmlNodePtr cur;
  641:     const char *filename = "test/xsdtest/xsdtestsuite.xml";
  642:     int ret = 0;
  643: 
  644:     doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT);
  645:     if (doc == NULL) {
  646:         fprintf(stderr, "Failed to parse %s\n", filename);
  647: 	return(-1);
  648:     }
  649:     printf("## XML Schemas datatypes test suite from James Clark\n");
  650: 
  651:     cur = xmlDocGetRootElement(doc);
  652:     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
  653:         fprintf(stderr, "Unexpected format %s\n", filename);
  654: 	ret = -1;
  655: 	goto done;
  656:     }
  657: 
  658:     cur = getNext(cur, "./testSuite[1]");
  659:     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
  660:         fprintf(stderr, "Unexpected format %s\n", filename);
  661: 	ret = -1;
  662: 	goto done;
  663:     }
  664:     while (cur != NULL) {
  665:         xsdTestSuite(cur);
  666: 	cur = getNext(cur, "following-sibling::testSuite[1]");
  667:     }
  668: 
  669: done:
  670:     if (doc != NULL)
  671: 	xmlFreeDoc(doc);
  672:     return(ret);
  673: }
  674: 
  675: static int 
  676: rngTestSuite(xmlNodePtr cur) {
  677:     if (verbose) {
  678: 	xmlChar *doc = getString(cur, "string(documentation)");
  679: 
  680: 	if (doc != NULL) {
  681: 	    printf("Suite %s\n", doc);
  682: 	    xmlFree(doc);
  683: 	} else {
  684: 	    doc = getString(cur, "string(section)");
  685: 	    if (doc != NULL) {
  686: 		printf("Section %s\n", doc);
  687: 		xmlFree(doc);
  688: 	    }
  689: 	}
  690:     }
  691:     cur = getNext(cur, "./testSuite[1]");
  692:     while (cur != NULL) {
  693:         xsdTestSuite(cur);
  694: 	cur = getNext(cur, "following-sibling::testSuite[1]");
  695:     }
  696:         
  697:     return(0);
  698: }
  699: 
  700: static int 
  701: rngTest1(void) {
  702:     xmlDocPtr doc;
  703:     xmlNodePtr cur;
  704:     const char *filename = "test/relaxng/OASIS/spectest.xml";
  705:     int ret = 0;
  706: 
  707:     doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT);
  708:     if (doc == NULL) {
  709:         fprintf(stderr, "Failed to parse %s\n", filename);
  710: 	return(-1);
  711:     }
  712:     printf("## Relax NG test suite from James Clark\n");
  713: 
  714:     cur = xmlDocGetRootElement(doc);
  715:     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
  716:         fprintf(stderr, "Unexpected format %s\n", filename);
  717: 	ret = -1;
  718: 	goto done;
  719:     }
  720: 
  721:     cur = getNext(cur, "./testSuite[1]");
  722:     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
  723:         fprintf(stderr, "Unexpected format %s\n", filename);
  724: 	ret = -1;
  725: 	goto done;
  726:     }
  727:     while (cur != NULL) {
  728:         rngTestSuite(cur);
  729: 	cur = getNext(cur, "following-sibling::testSuite[1]");
  730:     }
  731: 
  732: done:
  733:     if (doc != NULL)
  734: 	xmlFreeDoc(doc);
  735:     return(ret);
  736: }
  737: 
  738: static int 
  739: rngTest2(void) {
  740:     xmlDocPtr doc;
  741:     xmlNodePtr cur;
  742:     const char *filename = "test/relaxng/testsuite.xml";
  743:     int ret = 0;
  744: 
  745:     doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT);
  746:     if (doc == NULL) {
  747:         fprintf(stderr, "Failed to parse %s\n", filename);
  748: 	return(-1);
  749:     }
  750:     printf("## Relax NG test suite for libxml2\n");
  751: 
  752:     cur = xmlDocGetRootElement(doc);
  753:     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
  754:         fprintf(stderr, "Unexpected format %s\n", filename);
  755: 	ret = -1;
  756: 	goto done;
  757:     }
  758: 
  759:     cur = getNext(cur, "./testSuite[1]");
  760:     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
  761:         fprintf(stderr, "Unexpected format %s\n", filename);
  762: 	ret = -1;
  763: 	goto done;
  764:     }
  765:     while (cur != NULL) {
  766:         xsdTestSuite(cur);
  767: 	cur = getNext(cur, "following-sibling::testSuite[1]");
  768:     }
  769: 
  770: done:
  771:     if (doc != NULL)
  772: 	xmlFreeDoc(doc);
  773:     return(ret);
  774: }
  775: 
  776: /************************************************************************
  777:  *									*
  778:  *		Schemas test suites from W3C/NIST/MS/Sun		*
  779:  *									*
  780:  ************************************************************************/
  781: 
  782: static int
  783: xstcTestInstance(xmlNodePtr cur, xmlSchemaPtr schemas,
  784:                  const xmlChar *spath, const char *base) {
  785:     xmlChar *href = NULL;
  786:     xmlChar *path = NULL;
  787:     xmlChar *validity = NULL;
  788:     xmlSchemaValidCtxtPtr ctxt = NULL;
  789:     xmlDocPtr doc = NULL;
  790:     int ret = 0, mem;
  791: 
  792:     xmlResetLastError();
  793:     testErrorsSize = 0; testErrors[0] = 0;
  794:     mem = xmlMemUsed();
  795:     href = getString(cur,
  796:                      "string(ts:instanceDocument/@xlink:href)");
  797:     if ((href == NULL) || (href[0] == 0)) {
  798: 	test_log("testGroup line %ld misses href for schemaDocument\n",
  799: 		    xmlGetLineNo(cur));
  800: 	ret = -1;
  801: 	goto done;
  802:     }
  803:     path = xmlBuildURI(href, BAD_CAST base);
  804:     if (path == NULL) {
  805: 	fprintf(stderr,
  806: 	        "Failed to build path to schemas testGroup line %ld : %s\n",
  807: 		xmlGetLineNo(cur), href);
  808: 	ret = -1;
  809: 	goto done;
  810:     }
  811:     if (checkTestFile((const char *) path) <= 0) {
  812: 	test_log("schemas for testGroup line %ld is missing: %s\n",
  813: 		xmlGetLineNo(cur), path);
  814: 	ret = -1;
  815: 	goto done;
  816:     }
  817:     validity = getString(cur,
  818:                          "string(ts:expected/@validity)");
  819:     if (validity == NULL) {
  820:         fprintf(stderr, "instanceDocument line %ld misses expected validity\n",
  821: 	        xmlGetLineNo(cur));
  822: 	ret = -1;
  823: 	goto done;
  824:     }
  825:     nb_tests++;
  826:     doc = xmlReadFile((const char *) path, NULL, XML_PARSE_NOENT);
  827:     if (doc == NULL) {
  828:         fprintf(stderr, "instance %s fails to parse\n", path);
  829: 	ret = -1;
  830: 	nb_errors++;
  831: 	goto done;
  832:     }
  833: 
  834:     ctxt = xmlSchemaNewValidCtxt(schemas);
  835:     xmlSchemaSetValidErrors(ctxt,
  836:          (xmlSchemaValidityErrorFunc) testErrorHandler,
  837:          (xmlSchemaValidityWarningFunc) testErrorHandler,
  838: 	 ctxt);
  839:     ret = xmlSchemaValidateDoc(ctxt, doc);
  840: 
  841:     if (xmlStrEqual(validity, BAD_CAST "valid")) {
  842: 	if (ret > 0) {
  843: 	    test_log("valid instance %s failed to validate against %s\n",
  844: 			path, spath);
  845: 	    nb_errors++;
  846: 	} else if (ret < 0) {
  847: 	    test_log("valid instance %s got internal error validating %s\n",
  848: 			path, spath);
  849: 	    nb_internals++;
  850: 	    nb_errors++;
  851: 	}
  852:     } else if (xmlStrEqual(validity, BAD_CAST "invalid")) {
  853: 	if (ret == 0) {
  854: 	    test_log("Failed to detect invalid instance %s against %s\n",
  855: 			path, spath);
  856: 	    nb_errors++;
  857: 	}
  858:     } else {
  859:         test_log("instanceDocument line %ld has unexpected validity value%s\n",
  860: 	        xmlGetLineNo(cur), validity);
  861: 	ret = -1;
  862: 	goto done;
  863:     }
  864: 
  865: done:
  866:     if (href != NULL) xmlFree(href);
  867:     if (path != NULL) xmlFree(path);
  868:     if (validity != NULL) xmlFree(validity);
  869:     if (ctxt != NULL) xmlSchemaFreeValidCtxt(ctxt);
  870:     if (doc != NULL) xmlFreeDoc(doc);
  871:     xmlResetLastError();
  872:     if (mem != xmlMemUsed()) {
  873: 	test_log("Validation of tests starting line %ld leaked %d\n",
  874: 		xmlGetLineNo(cur), xmlMemUsed() - mem);
  875: 	nb_leaks++;
  876:     }
  877:     return(ret);
  878: }
  879: 
  880: static int
  881: xstcTestGroup(xmlNodePtr cur, const char *base) {
  882:     xmlChar *href = NULL;
  883:     xmlChar *path = NULL;
  884:     xmlChar *validity = NULL;
  885:     xmlSchemaPtr schemas = NULL;
  886:     xmlSchemaParserCtxtPtr ctxt;
  887:     xmlNodePtr instance;
  888:     int ret = 0, mem;
  889: 
  890:     xmlResetLastError();
  891:     testErrorsSize = 0; testErrors[0] = 0;
  892:     mem = xmlMemUsed();
  893:     href = getString(cur,
  894:                      "string(ts:schemaTest/ts:schemaDocument/@xlink:href)");
  895:     if ((href == NULL) || (href[0] == 0)) {
  896:         test_log("testGroup line %ld misses href for schemaDocument\n",
  897: 		    xmlGetLineNo(cur));
  898: 	ret = -1;
  899: 	goto done;
  900:     }
  901:     path = xmlBuildURI(href, BAD_CAST base);
  902:     if (path == NULL) {
  903: 	test_log("Failed to build path to schemas testGroup line %ld : %s\n",
  904: 		xmlGetLineNo(cur), href);
  905: 	ret = -1;
  906: 	goto done;
  907:     }
  908:     if (checkTestFile((const char *) path) <= 0) {
  909: 	test_log("schemas for testGroup line %ld is missing: %s\n",
  910: 		xmlGetLineNo(cur), path);
  911: 	ret = -1;
  912: 	goto done;
  913:     }
  914:     validity = getString(cur,
  915:                          "string(ts:schemaTest/ts:expected/@validity)");
  916:     if (validity == NULL) {
  917:         test_log("testGroup line %ld misses expected validity\n",
  918: 	        xmlGetLineNo(cur));
  919: 	ret = -1;
  920: 	goto done;
  921:     }
  922:     nb_tests++;
  923:     if (xmlStrEqual(validity, BAD_CAST "valid")) {
  924:         nb_schematas++;
  925: 	ctxt = xmlSchemaNewParserCtxt((const char *) path);
  926: 	xmlSchemaSetParserErrors(ctxt,
  927: 	     (xmlSchemaValidityErrorFunc) testErrorHandler,
  928: 	     (xmlSchemaValidityWarningFunc) testErrorHandler,
  929: 	     ctxt);
  930: 	schemas = xmlSchemaParse(ctxt);
  931: 	xmlSchemaFreeParserCtxt(ctxt);
  932: 	if (schemas == NULL) {
  933: 	    test_log("valid schemas %s failed to parse\n",
  934: 			path);
  935: 	    ret = 1;
  936: 	    nb_errors++;
  937: 	}
  938: 	if ((ret == 0) && (strstr(testErrors, "nimplemented") != NULL)) {
  939: 	    test_log("valid schemas %s hit an unimplemented block\n",
  940: 			path);
  941: 	    ret = 1;
  942: 	    nb_unimplemented++;
  943: 	    nb_errors++;
  944: 	}
  945: 	instance = getNext(cur, "./ts:instanceTest[1]");
  946: 	while (instance != NULL) {
  947: 	    if (schemas != NULL) {
  948: 		xstcTestInstance(instance, schemas, path, base);		
  949: 	    } else {
  950: 		/*
  951: 		* We'll automatically mark the instances as failed
  952: 		* if the schema was broken.
  953: 		*/
  954: 		nb_errors++;
  955: 	    }
  956: 	    instance = getNext(instance,
  957: 		"following-sibling::ts:instanceTest[1]");
  958: 	}
  959:     } else if (xmlStrEqual(validity, BAD_CAST "invalid")) {
  960:         nb_schematas++;
  961: 	ctxt = xmlSchemaNewParserCtxt((const char *) path);
  962: 	xmlSchemaSetParserErrors(ctxt,
  963: 	     (xmlSchemaValidityErrorFunc) testErrorHandler,
  964: 	     (xmlSchemaValidityWarningFunc) testErrorHandler,
  965: 	     ctxt);
  966: 	schemas = xmlSchemaParse(ctxt);
  967: 	xmlSchemaFreeParserCtxt(ctxt);
  968: 	if (schemas != NULL) {
  969: 	    test_log("Failed to detect error in schemas %s\n",
  970: 			path);
  971: 	    nb_errors++;
  972: 	    ret = 1;
  973: 	}
  974: 	if ((ret == 0) && (strstr(testErrors, "nimplemented") != NULL)) {
  975: 	    nb_unimplemented++;
  976: 	    test_log("invalid schemas %s hit an unimplemented block\n",
  977: 			path);
  978: 	    ret = 1;
  979: 	    nb_errors++;
  980: 	}
  981:     } else {
  982:         test_log("testGroup line %ld misses unexpected validity value%s\n",
  983: 	        xmlGetLineNo(cur), validity);
  984: 	ret = -1;
  985: 	goto done;
  986:     }
  987: 
  988: done:
  989:     if (href != NULL) xmlFree(href);
  990:     if (path != NULL) xmlFree(path);
  991:     if (validity != NULL) xmlFree(validity);
  992:     if (schemas != NULL) xmlSchemaFree(schemas);
  993:     xmlResetLastError();
  994:     if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
  995: 	test_log("Processing test line %ld %s leaked %d\n",
  996: 		xmlGetLineNo(cur), path, xmlMemUsed() - mem);
  997: 	nb_leaks++;
  998:     }
  999:     return(ret);
 1000: }
 1001: 
 1002: static int
 1003: xstcMetadata(const char *metadata, const char *base) {
 1004:     xmlDocPtr doc;
 1005:     xmlNodePtr cur;
 1006:     xmlChar *contributor;
 1007:     xmlChar *name;
 1008:     int ret = 0;
 1009: 
 1010:     doc = xmlReadFile(metadata, NULL, XML_PARSE_NOENT);
 1011:     if (doc == NULL) {
 1012:         fprintf(stderr, "Failed to parse %s\n", metadata);
 1013: 	return(-1);
 1014:     }
 1015: 
 1016:     cur = xmlDocGetRootElement(doc);
 1017:     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSet"))) {
 1018:         fprintf(stderr, "Unexpected format %s\n", metadata);
 1019: 	return(-1);
 1020:     }
 1021:     contributor = xmlGetProp(cur, BAD_CAST "contributor");
 1022:     if (contributor == NULL) {
 1023:         contributor = xmlStrdup(BAD_CAST "Unknown");
 1024:     }
 1025:     name = xmlGetProp(cur, BAD_CAST "name");
 1026:     if (name == NULL) {
 1027:         name = xmlStrdup(BAD_CAST "Unknown");
 1028:     }
 1029:     printf("## %s test suite for Schemas version %s\n", contributor, name);
 1030:     xmlFree(contributor);
 1031:     xmlFree(name);
 1032: 
 1033:     cur = getNext(cur, "./ts:testGroup[1]");
 1034:     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testGroup"))) {
 1035:         fprintf(stderr, "Unexpected format %s\n", metadata);
 1036: 	ret = -1;
 1037: 	goto done;
 1038:     }
 1039:     while (cur != NULL) {
 1040:         xstcTestGroup(cur, base);
 1041: 	cur = getNext(cur, "following-sibling::ts:testGroup[1]");
 1042:     }
 1043: 
 1044: done:
 1045:     xmlFreeDoc(doc);
 1046:     return(ret);
 1047: }
 1048: 
 1049: /************************************************************************
 1050:  *									*
 1051:  *		The driver for the tests				*
 1052:  *									*
 1053:  ************************************************************************/
 1054: 
 1055: int
 1056: main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
 1057:     int ret = 0;
 1058:     int old_errors, old_tests, old_leaks;
 1059: 
 1060:     logfile = fopen(LOGFILE, "w");
 1061:     if (logfile == NULL) {
 1062:         fprintf(stderr,
 1063: 	        "Could not open the log file, running in verbose mode\n");
 1064: 	verbose = 1;
 1065:     }
 1066:     initializeLibxml2();
 1067: 
 1068:     if ((argc >= 2) && (!strcmp(argv[1], "-v")))
 1069:         verbose = 1;
 1070: 
 1071: 
 1072:     old_errors = nb_errors;
 1073:     old_tests = nb_tests;
 1074:     old_leaks = nb_leaks;
 1075:     xsdTest();
 1076:     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
 1077: 	printf("Ran %d tests, no errors\n", nb_tests - old_tests);
 1078:     else
 1079: 	printf("Ran %d tests, %d errors, %d leaks\n",
 1080: 	       nb_tests - old_tests,
 1081: 	       nb_errors - old_errors,
 1082: 	       nb_leaks - old_leaks);
 1083:     old_errors = nb_errors;
 1084:     old_tests = nb_tests;
 1085:     old_leaks = nb_leaks;
 1086:     rngTest1();
 1087:     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
 1088: 	printf("Ran %d tests, no errors\n", nb_tests - old_tests);
 1089:     else
 1090: 	printf("Ran %d tests, %d errors, %d leaks\n",
 1091: 	       nb_tests - old_tests,
 1092: 	       nb_errors - old_errors,
 1093: 	       nb_leaks - old_leaks);
 1094:     old_errors = nb_errors;
 1095:     old_tests = nb_tests;
 1096:     old_leaks = nb_leaks;
 1097:     rngTest2();
 1098:     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
 1099: 	printf("Ran %d tests, no errors\n", nb_tests - old_tests);
 1100:     else
 1101: 	printf("Ran %d tests, %d errors, %d leaks\n",
 1102: 	       nb_tests - old_tests,
 1103: 	       nb_errors - old_errors,
 1104: 	       nb_leaks - old_leaks);
 1105:     old_errors = nb_errors;
 1106:     old_tests = nb_tests;
 1107:     old_leaks = nb_leaks;
 1108:     nb_internals = 0;
 1109:     nb_schematas = 0;
 1110:     xstcMetadata("xstc/Tests/Metadata/NISTXMLSchemaDatatypes.testSet",
 1111: 		 "xstc/Tests/Metadata/");
 1112:     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
 1113: 	printf("Ran %d tests (%d schemata), no errors\n",
 1114: 	       nb_tests - old_tests, nb_schematas);
 1115:     else
 1116: 	printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n",
 1117: 	       nb_tests - old_tests,
 1118: 	       nb_schematas,
 1119: 	       nb_errors - old_errors,
 1120: 	       nb_internals,
 1121: 	       nb_leaks - old_leaks);
 1122:     old_errors = nb_errors;
 1123:     old_tests = nb_tests;
 1124:     old_leaks = nb_leaks;
 1125:     nb_internals = 0;
 1126:     nb_schematas = 0;
 1127:     xstcMetadata("xstc/Tests/Metadata/SunXMLSchema1-0-20020116.testSet",
 1128: 		 "xstc/Tests/");
 1129:     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
 1130: 	printf("Ran %d tests (%d schemata), no errors\n",
 1131: 	       nb_tests - old_tests, nb_schematas);
 1132:     else
 1133: 	printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n",
 1134: 	       nb_tests - old_tests,
 1135: 	       nb_schematas,
 1136: 	       nb_errors - old_errors,
 1137: 	       nb_internals,
 1138: 	       nb_leaks - old_leaks);
 1139:     old_errors = nb_errors;
 1140:     old_tests = nb_tests;
 1141:     old_leaks = nb_leaks;
 1142:     nb_internals = 0;
 1143:     nb_schematas = 0;
 1144:     xstcMetadata("xstc/Tests/Metadata/MSXMLSchema1-0-20020116.testSet",
 1145: 		 "xstc/Tests/");
 1146:     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
 1147: 	printf("Ran %d tests (%d schemata), no errors\n",
 1148: 	       nb_tests - old_tests, nb_schematas);
 1149:     else
 1150: 	printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n",
 1151: 	       nb_tests - old_tests,
 1152: 	       nb_schematas,
 1153: 	       nb_errors - old_errors,
 1154: 	       nb_internals,
 1155: 	       nb_leaks - old_leaks);
 1156: 
 1157:     if ((nb_errors == 0) && (nb_leaks == 0)) {
 1158:         ret = 0;
 1159: 	printf("Total %d tests, no errors\n",
 1160: 	       nb_tests);
 1161:     } else {
 1162:         ret = 1;
 1163: 	printf("Total %d tests, %d errors, %d leaks\n",
 1164: 	       nb_tests, nb_errors, nb_leaks);
 1165:     }
 1166:     xmlXPathFreeContext(ctxtXPath);
 1167:     xmlCleanupParser();
 1168:     xmlMemoryDump();
 1169: 
 1170:     if (logfile != NULL)
 1171:         fclose(logfile);
 1172:     return(ret);
 1173: }
 1174: #else /* !SCHEMAS */
 1175: int
 1176: main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
 1177:     fprintf(stderr, "runsuite requires support for schemas and xpath in libxml2\n");
 1178: }
 1179: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>