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

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

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