File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libxml2 / runsuite.c
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:37:58 2012 UTC (12 years, 5 months ago) by misho
CVS tags: MAIN, HEAD
Initial revision

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

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