Annotation of embedaddon/libxml2/runxmlconf.c, revision 1.1.1.2

1.1       misho       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: #ifdef LIBXML_XPATH_ENABLED
                     16: 
                     17: #if !defined(_WIN32) || defined(__CYGWIN__)
                     18: #include <unistd.h>
                     19: #endif
                     20: #include <string.h>
                     21: #include <sys/types.h>
                     22: #include <sys/stat.h>
                     23: #include <fcntl.h>
                     24: 
                     25: #include <libxml/parser.h>
                     26: #include <libxml/parserInternals.h>
                     27: #include <libxml/tree.h>
                     28: #include <libxml/uri.h>
                     29: #include <libxml/xmlreader.h>
                     30: 
                     31: #include <libxml/xpath.h>
                     32: #include <libxml/xpathInternals.h>
                     33: 
                     34: #define LOGFILE "runxmlconf.log"
                     35: static FILE *logfile = NULL;
                     36: static int verbose = 0;
                     37: 
                     38: #define NB_EXPECTED_ERRORS 15
                     39: 
1.1.1.2 ! misho      40: #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__)
1.1       misho      41: #define vsnprintf _vsnprintf
                     42: #define snprintf _snprintf
                     43: #endif
                     44: 
                     45: const char *skipped_tests[] = {
                     46: /* http://lists.w3.org/Archives/Public/public-xml-testsuite/2008Jul/0000.html */
                     47:     "rmt-ns10-035",
                     48:     NULL
                     49: };
                     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_skipped = 0;
                     91: static int nb_tests = 0;
                     92: static int nb_errors = 0;
                     93: static int nb_leaks = 0;
                     94: 
                     95: /*
                     96:  * We need to trap calls to the resolver to not account memory for the catalog
                     97:  * and not rely on any external resources.
                     98:  */
                     99: static xmlParserInputPtr
                    100: testExternalEntityLoader(const char *URL, const char *ID ATTRIBUTE_UNUSED,
                    101:                         xmlParserCtxtPtr ctxt) {
                    102:     xmlParserInputPtr ret;
                    103: 
                    104:     ret = xmlNewInputFromFile(ctxt, (const char *) URL);
                    105: 
                    106:     return(ret);
                    107: }
                    108: 
                    109: /*
                    110:  * Trapping the error messages at the generic level to grab the equivalent of
                    111:  * stderr messages on CLI tools.
                    112:  */
                    113: static char testErrors[32769];
                    114: static int testErrorsSize = 0;
                    115: static int nbError = 0;
                    116: static int nbFatal = 0;
                    117: 
                    118: static void test_log(const char *msg, ...) {
                    119:     va_list args;
                    120:     if (logfile != NULL) {
                    121:         fprintf(logfile, "\n------------\n");
                    122:        va_start(args, msg);
                    123:        vfprintf(logfile, msg, args);
                    124:        va_end(args);
                    125:        fprintf(logfile, "%s", testErrors);
                    126:        testErrorsSize = 0; testErrors[0] = 0;
                    127:     }
                    128:     if (verbose) {
                    129:        va_start(args, msg);
                    130:        vfprintf(stderr, msg, args);
                    131:        va_end(args);
                    132:     }
                    133: }
                    134: 
                    135: static void
                    136: testErrorHandler(void *userData ATTRIBUTE_UNUSED, xmlErrorPtr error) {
                    137:     int res;
                    138: 
                    139:     if (testErrorsSize >= 32768)
                    140:         return;
                    141:     res = snprintf(&testErrors[testErrorsSize],
                    142:                     32768 - testErrorsSize,
                    143:                   "%s:%d: %s\n", (error->file ? error->file : "entity"),
                    144:                   error->line, error->message);
                    145:     if (error->level == XML_ERR_FATAL)
                    146:         nbFatal++;
                    147:     else if (error->level == XML_ERR_ERROR)
                    148:         nbError++;
                    149:     if (testErrorsSize + res >= 32768) {
                    150:         /* buffer is full */
                    151:        testErrorsSize = 32768;
                    152:        testErrors[testErrorsSize] = 0;
                    153:     } else {
                    154:         testErrorsSize += res;
                    155:     }
                    156:     testErrors[testErrorsSize] = 0;
                    157: }
                    158: 
                    159: static xmlXPathContextPtr ctxtXPath;
                    160: 
                    161: static void
                    162: initializeLibxml2(void) {
                    163:     xmlGetWarningsDefaultValue = 0;
                    164:     xmlPedanticParserDefault(0);
                    165: 
                    166:     xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
                    167:     xmlInitParser();
                    168:     xmlSetExternalEntityLoader(testExternalEntityLoader);
                    169:     ctxtXPath = xmlXPathNewContext(NULL);
                    170:     /*
                    171:     * Deactivate the cache if created; otherwise we have to create/free it
                    172:     * for every test, since it will confuse the memory leak detection.
                    173:     * Note that normally this need not be done, since the cache is not
                    174:     * created until set explicitely with xmlXPathContextSetCache();
                    175:     * but for test purposes it is sometimes usefull to activate the
                    176:     * cache by default for the whole library.
                    177:     */
                    178:     if (ctxtXPath->cache != NULL)
                    179:        xmlXPathContextSetCache(ctxtXPath, 0, -1, 0);
                    180:     xmlSetStructuredErrorFunc(NULL, testErrorHandler);
                    181: }
                    182: 
                    183: /************************************************************************
                    184:  *                                                                     *
                    185:  *             Run the xmlconf test if found                           *
                    186:  *                                                                     *
                    187:  ************************************************************************/
                    188: 
                    189: static int
                    190: xmlconfTestInvalid(const char *id, const char *filename, int options) {
                    191:     xmlDocPtr doc;
                    192:     xmlParserCtxtPtr ctxt;
                    193:     int ret = 1;
                    194: 
                    195:     ctxt = xmlNewParserCtxt();
                    196:     if (ctxt == NULL) {
                    197:         test_log("test %s : %s out of memory\n",
                    198:                 id, filename);
                    199:         return(0);
                    200:     }
                    201:     doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
                    202:     if (doc == NULL) {
                    203:         test_log("test %s : %s invalid document turned not well-formed too\n",
                    204:                 id, filename);
                    205:     } else {
                    206:     /* invalidity should be reported both in the context and in the document */
                    207:         if ((ctxt->valid != 0) || (doc->properties & XML_DOC_DTDVALID)) {
                    208:            test_log("test %s : %s failed to detect invalid document\n",
                    209:                     id, filename);
                    210:            nb_errors++;
                    211:            ret = 0;
                    212:        }
                    213:        xmlFreeDoc(doc);
                    214:     }
                    215:     xmlFreeParserCtxt(ctxt);
                    216:     return(ret);
                    217: }
                    218: 
                    219: static int
                    220: xmlconfTestValid(const char *id, const char *filename, int options) {
                    221:     xmlDocPtr doc;
                    222:     xmlParserCtxtPtr ctxt;
                    223:     int ret = 1;
                    224: 
                    225:     ctxt = xmlNewParserCtxt();
                    226:     if (ctxt == NULL) {
                    227:         test_log("test %s : %s out of memory\n",
                    228:                 id, filename);
                    229:         return(0);
                    230:     }
                    231:     doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
                    232:     if (doc == NULL) {
                    233:         test_log("test %s : %s failed to parse a valid document\n",
                    234:                 id, filename);
                    235:         nb_errors++;
                    236:        ret = 0;
                    237:     } else {
                    238:     /* validity should be reported both in the context and in the document */
                    239:         if ((ctxt->valid == 0) || ((doc->properties & XML_DOC_DTDVALID) == 0)) {
                    240:            test_log("test %s : %s failed to validate a valid document\n",
                    241:                     id, filename);
                    242:            nb_errors++;
                    243:            ret = 0;
                    244:        }
                    245:        xmlFreeDoc(doc);
                    246:     }
                    247:     xmlFreeParserCtxt(ctxt);
                    248:     return(ret);
                    249: }
                    250: 
                    251: static int
                    252: xmlconfTestNotNSWF(const char *id, const char *filename, int options) {
                    253:     xmlDocPtr doc;
                    254:     int ret = 1;
                    255: 
                    256:     /*
                    257:      * In case of Namespace errors, libxml2 will still parse the document
                    258:      * but log a Namesapce error.
                    259:      */
                    260:     doc = xmlReadFile(filename, NULL, options);
                    261:     if (doc == NULL) {
                    262:         test_log("test %s : %s failed to parse the XML\n",
                    263:                 id, filename);
                    264:         nb_errors++;
                    265:        ret = 0;
                    266:     } else {
                    267:        if ((xmlLastError.code == XML_ERR_OK) ||
                    268:            (xmlLastError.domain != XML_FROM_NAMESPACE)) {
                    269:            test_log("test %s : %s failed to detect namespace error\n",
                    270:                     id, filename);
                    271:            nb_errors++;
                    272:            ret = 0;
                    273:        }
                    274:        xmlFreeDoc(doc);
                    275:     }
                    276:     return(ret);
                    277: }
                    278: 
                    279: static int
                    280: xmlconfTestNotWF(const char *id, const char *filename, int options) {
                    281:     xmlDocPtr doc;
                    282:     int ret = 1;
                    283: 
                    284:     doc = xmlReadFile(filename, NULL, options);
                    285:     if (doc != NULL) {
                    286:         test_log("test %s : %s failed to detect not well formedness\n",
                    287:                 id, filename);
                    288:         nb_errors++;
                    289:        xmlFreeDoc(doc);
                    290:        ret = 0;
                    291:     }
                    292:     return(ret);
                    293: }
                    294: 
                    295: static int
                    296: xmlconfTestItem(xmlDocPtr doc, xmlNodePtr cur) {
                    297:     int ret = -1;
                    298:     xmlChar *type = NULL;
                    299:     xmlChar *filename = NULL;
                    300:     xmlChar *uri = NULL;
                    301:     xmlChar *base = NULL;
                    302:     xmlChar *id = NULL;
                    303:     xmlChar *rec = NULL;
                    304:     xmlChar *version = NULL;
                    305:     xmlChar *entities = NULL;
                    306:     xmlChar *edition = NULL;
                    307:     int options = 0;
                    308:     int nstest = 0;
                    309:     int mem, final;
                    310:     int i;
                    311: 
                    312:     testErrorsSize = 0; testErrors[0] = 0;
                    313:     nbError = 0;
                    314:     nbFatal = 0;
                    315:     id = xmlGetProp(cur, BAD_CAST "ID");
                    316:     if (id == NULL) {
                    317:         test_log("test missing ID, line %ld\n", xmlGetLineNo(cur));
                    318:        goto error;
                    319:     }
                    320:     for (i = 0;skipped_tests[i] != NULL;i++) {
                    321:         if (!strcmp(skipped_tests[i], (char *) id)) {
                    322:            test_log("Skipping test %s from skipped list\n", (char *) id);
                    323:            ret = 0;
                    324:            nb_skipped++;
                    325:            goto error;
                    326:        }
                    327:     }
                    328:     type = xmlGetProp(cur, BAD_CAST "TYPE");
                    329:     if (type == NULL) {
                    330:         test_log("test %s missing TYPE\n", (char *) id);
                    331:        goto error;
                    332:     }
                    333:     uri = xmlGetProp(cur, BAD_CAST "URI");
                    334:     if (uri == NULL) {
                    335:         test_log("test %s missing URI\n", (char *) id);
                    336:        goto error;
                    337:     }
                    338:     base = xmlNodeGetBase(doc, cur);
                    339:     filename = composeDir(base, uri);
                    340:     if (!checkTestFile((char *) filename)) {
                    341:         test_log("test %s missing file %s \n", id,
                    342:                 (filename ? (char *)filename : "NULL"));
                    343:        goto error;
                    344:     }
                    345: 
                    346:     version = xmlGetProp(cur, BAD_CAST "VERSION");
                    347: 
                    348:     entities = xmlGetProp(cur, BAD_CAST "ENTITIES");
                    349:     if (!xmlStrEqual(entities, BAD_CAST "none")) {
                    350:         options |= XML_PARSE_DTDLOAD;
                    351:         options |= XML_PARSE_NOENT;
                    352:     }
                    353:     rec = xmlGetProp(cur, BAD_CAST "RECOMMENDATION");
                    354:     if ((rec == NULL) ||
                    355:         (xmlStrEqual(rec, BAD_CAST "XML1.0")) ||
                    356:        (xmlStrEqual(rec, BAD_CAST "XML1.0-errata2e")) ||
                    357:        (xmlStrEqual(rec, BAD_CAST "XML1.0-errata3e")) ||
                    358:        (xmlStrEqual(rec, BAD_CAST "XML1.0-errata4e"))) {
                    359:        if ((version != NULL) && (!xmlStrEqual(version, BAD_CAST "1.0"))) {
                    360:            test_log("Skipping test %s for %s\n", (char *) id,
                    361:                     (char *) version);
                    362:            ret = 0;
                    363:            nb_skipped++;
                    364:            goto error;
                    365:        }
                    366:        ret = 1;
                    367:     } else if ((xmlStrEqual(rec, BAD_CAST "NS1.0")) ||
                    368:               (xmlStrEqual(rec, BAD_CAST "NS1.0-errata1e"))) {
                    369:        ret = 1;
                    370:        nstest = 1;
                    371:     } else {
                    372:         test_log("Skipping test %s for REC %s\n", (char *) id, (char *) rec);
                    373:        ret = 0;
                    374:        nb_skipped++;
                    375:        goto error;
                    376:     }
                    377:     edition = xmlGetProp(cur, BAD_CAST "EDITION");
                    378:     if ((edition != NULL) && (xmlStrchr(edition, '5') == NULL)) {
                    379:         /* test limited to all versions before 5th */
                    380:        options |= XML_PARSE_OLD10;
                    381:     }
                    382: 
                    383:     /*
                    384:      * Reset errors and check memory usage before the test
                    385:      */
                    386:     xmlResetLastError();
                    387:     testErrorsSize = 0; testErrors[0] = 0;
                    388:     mem = xmlMemUsed();
                    389: 
                    390:     if (xmlStrEqual(type, BAD_CAST "not-wf")) {
                    391:         if (nstest == 0)
                    392:            xmlconfTestNotWF((char *) id, (char *) filename, options);
                    393:         else 
                    394:            xmlconfTestNotNSWF((char *) id, (char *) filename, options);
                    395:     } else if (xmlStrEqual(type, BAD_CAST "valid")) {
                    396:         options |= XML_PARSE_DTDVALID;
                    397:        xmlconfTestValid((char *) id, (char *) filename, options);
                    398:     } else if (xmlStrEqual(type, BAD_CAST "invalid")) {
                    399:         options |= XML_PARSE_DTDVALID;
                    400:        xmlconfTestInvalid((char *) id, (char *) filename, options);
                    401:     } else if (xmlStrEqual(type, BAD_CAST "error")) {
                    402:         test_log("Skipping error test %s \n", (char *) id);
                    403:        ret = 0;
                    404:        nb_skipped++;
                    405:        goto error;
                    406:     } else {
                    407:         test_log("test %s unknown TYPE value %s\n", (char *) id, (char *)type);
                    408:        ret = -1;
                    409:        goto error;
                    410:     }
                    411: 
                    412:     /*
                    413:      * Reset errors and check memory usage after the test
                    414:      */
                    415:     xmlResetLastError();
                    416:     final = xmlMemUsed();
                    417:     if (final > mem) {
                    418:         test_log("test %s : %s leaked %d bytes\n",
                    419:                 id, filename, final - mem);
                    420:         nb_leaks++;
                    421:        xmlMemDisplayLast(logfile, final - mem);
                    422:     }
                    423:     nb_tests++;
                    424: 
                    425: error:
                    426:     if (type != NULL)
                    427:         xmlFree(type);
                    428:     if (entities != NULL)
                    429:         xmlFree(entities);
                    430:     if (edition != NULL)
                    431:         xmlFree(edition);
                    432:     if (version != NULL)
                    433:         xmlFree(version);
                    434:     if (filename != NULL)
                    435:         xmlFree(filename);
                    436:     if (uri != NULL)
                    437:         xmlFree(uri);
                    438:     if (base != NULL)
                    439:         xmlFree(base);
                    440:     if (id != NULL)
                    441:         xmlFree(id);
                    442:     if (rec != NULL)
                    443:         xmlFree(rec);
                    444:     return(ret);
                    445: }
                    446: 
                    447: static int
                    448: xmlconfTestCases(xmlDocPtr doc, xmlNodePtr cur, int level) {
                    449:     xmlChar *profile;
                    450:     int ret = 0;
                    451:     int tests = 0;
                    452:     int output = 0;
                    453: 
                    454:     if (level == 1) {
                    455:        profile = xmlGetProp(cur, BAD_CAST "PROFILE");
                    456:        if (profile != NULL) {
                    457:            output = 1;
                    458:            level++;
                    459:            printf("Test cases: %s\n", (char *) profile);
                    460:            xmlFree(profile);
                    461:        }
                    462:     }
                    463:     cur = cur->children;
                    464:     while (cur != NULL) {
                    465:         /* look only at elements we ignore everything else */
                    466:         if (cur->type == XML_ELEMENT_NODE) {
                    467:            if (xmlStrEqual(cur->name, BAD_CAST "TESTCASES")) {
                    468:                ret += xmlconfTestCases(doc, cur, level);
                    469:            } else if (xmlStrEqual(cur->name, BAD_CAST "TEST")) {
                    470:                if (xmlconfTestItem(doc, cur) >= 0)
                    471:                    ret++;
                    472:                tests++;
                    473:            } else {
                    474:                fprintf(stderr, "Unhandled element %s\n", (char *)cur->name);
                    475:            }
                    476:        }
                    477:         cur = cur->next;
                    478:     }
                    479:     if (output == 1) {
                    480:        if (tests > 0)
                    481:            printf("Test cases: %d tests\n", tests);
                    482:     }
                    483:     return(ret);
                    484: }
                    485: 
                    486: static int
                    487: xmlconfTestSuite(xmlDocPtr doc, xmlNodePtr cur) {
                    488:     xmlChar *profile;
                    489:     int ret = 0;
                    490: 
                    491:     profile = xmlGetProp(cur, BAD_CAST "PROFILE");
                    492:     if (profile != NULL) {
                    493:         printf("Test suite: %s\n", (char *) profile);
                    494:        xmlFree(profile);
                    495:     } else
                    496:         printf("Test suite\n");
                    497:     cur = cur->children;
                    498:     while (cur != NULL) {
                    499:         /* look only at elements we ignore everything else */
                    500:         if (cur->type == XML_ELEMENT_NODE) {
                    501:            if (xmlStrEqual(cur->name, BAD_CAST "TESTCASES")) {
                    502:                ret += xmlconfTestCases(doc, cur, 1);
                    503:            } else {
                    504:                fprintf(stderr, "Unhandled element %s\n", (char *)cur->name);
                    505:            }
                    506:        }
                    507:         cur = cur->next;
                    508:     }
                    509:     return(ret);
                    510: }
                    511: 
                    512: static void
                    513: xmlconfInfo(void) {
                    514:     fprintf(stderr, "  you need to fetch and extract the\n");
                    515:     fprintf(stderr, "  latest XML Conformance Test Suites\n");
1.1.1.2 ! misho     516:     fprintf(stderr, "  http://www.w3.org/XML/Test/xmlts20080827.tar.gz\n");
1.1       misho     517:     fprintf(stderr, "  see http://www.w3.org/XML/Test/ for informations\n");
                    518: }
                    519: 
                    520: static int
                    521: xmlconfTest(void) {
                    522:     const char *confxml = "xmlconf/xmlconf.xml";
                    523:     xmlDocPtr doc;
                    524:     xmlNodePtr cur;
                    525:     int ret = 0;
                    526: 
                    527:     if (!checkTestFile(confxml)) {
                    528:         fprintf(stderr, "%s is missing \n", confxml);
                    529:        xmlconfInfo();
                    530:        return(-1);
                    531:     }
                    532:     doc = xmlReadFile(confxml, NULL, XML_PARSE_NOENT);
                    533:     if (doc == NULL) {
                    534:         fprintf(stderr, "%s is corrupted \n", confxml);
                    535:        xmlconfInfo();
                    536:        return(-1);
                    537:     }
                    538: 
                    539:     cur = xmlDocGetRootElement(doc);
                    540:     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "TESTSUITE"))) {
                    541:         fprintf(stderr, "Unexpected format %s\n", confxml);
                    542:        xmlconfInfo();
                    543:        ret = -1;
                    544:     } else {
                    545:         ret = xmlconfTestSuite(doc, cur);
                    546:     }
                    547:     xmlFreeDoc(doc);
                    548:     return(ret);
                    549: }
                    550: 
                    551: /************************************************************************
                    552:  *                                                                     *
                    553:  *             The driver for the tests                                *
                    554:  *                                                                     *
                    555:  ************************************************************************/
                    556: 
                    557: int
                    558: main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
                    559:     int ret = 0;
                    560:     int old_errors, old_tests, old_leaks;
                    561: 
                    562:     logfile = fopen(LOGFILE, "w");
                    563:     if (logfile == NULL) {
                    564:         fprintf(stderr,
                    565:                "Could not open the log file, running in verbose mode\n");
                    566:        verbose = 1;
                    567:     }
                    568:     initializeLibxml2();
                    569: 
                    570:     if ((argc >= 2) && (!strcmp(argv[1], "-v")))
                    571:         verbose = 1;
                    572: 
                    573: 
                    574:     old_errors = nb_errors;
                    575:     old_tests = nb_tests;
                    576:     old_leaks = nb_leaks;
                    577:     xmlconfTest();
                    578:     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
                    579:        printf("Ran %d tests, no errors\n", nb_tests - old_tests);
                    580:     else
                    581:        printf("Ran %d tests, %d errors, %d leaks\n",
                    582:               nb_tests - old_tests,
                    583:               nb_errors - old_errors,
                    584:               nb_leaks - old_leaks);
                    585:     if ((nb_errors == 0) && (nb_leaks == 0)) {
                    586:         ret = 0;
                    587:        printf("Total %d tests, no errors\n",
                    588:               nb_tests);
                    589:     } else {
                    590:        ret = 1;
                    591:        printf("Total %d tests, %d errors, %d leaks\n",
                    592:               nb_tests, nb_errors, nb_leaks);
                    593:        printf("See %s for detailed output\n", LOGFILE);
                    594:        if ((nb_leaks == 0) && (nb_errors == NB_EXPECTED_ERRORS)) {
                    595:            printf("%d errors were expected\n", nb_errors);
                    596:            ret = 0;
                    597:        }
                    598:     }
                    599:     xmlXPathFreeContext(ctxtXPath);
                    600:     xmlCleanupParser();
                    601:     xmlMemoryDump();
                    602: 
                    603:     if (logfile != NULL)
                    604:         fclose(logfile);
                    605:     return(ret);
                    606: }
                    607: 
                    608: #else /* ! LIBXML_XPATH_ENABLED */
                    609: #include <stdio.h>
                    610: int
                    611: main(int argc, char **argv) {
                    612:     fprintf(stderr, "%s need XPath support\n", argv[0]);
                    613: }
                    614: #endif

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