1/*
2 * runtest.c: C program to run libxml2 regression tests without
3 *            requiring make or Python, and reducing platform dependancies
4 *            to a strict minimum.
5 *
6 * To compile on Unixes:
7 * cc -o runtest `xml2-config --cflags` runtest.c `xml2-config --libs` -lpthread
8 *
9 * See Copyright for the status of this software.
10 *
11 * daniel@veillard.com
12 */
13
14#ifdef HAVE_CONFIG_H
15#include "libxml.h"
16#else
17#include <stdio.h>
18#endif
19
20#if !defined(_WIN32) || defined(__CYGWIN__)
21#include <unistd.h>
22#endif
23#include <string.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <fcntl.h>
27
28#include <libxml/parser.h>
29#include <libxml/tree.h>
30#include <libxml/uri.h>
31
32#ifdef LIBXML_OUTPUT_ENABLED
33#ifdef LIBXML_READER_ENABLED
34#include <libxml/xmlreader.h>
35#endif
36
37#ifdef LIBXML_XINCLUDE_ENABLED
38#include <libxml/xinclude.h>
39#endif
40
41#ifdef LIBXML_XPATH_ENABLED
42#include <libxml/xpath.h>
43#include <libxml/xpathInternals.h>
44#ifdef LIBXML_XPTR_ENABLED
45#include <libxml/xpointer.h>
46#endif
47#endif
48
49#ifdef LIBXML_SCHEMAS_ENABLED
50#include <libxml/relaxng.h>
51#include <libxml/xmlschemas.h>
52#include <libxml/xmlschemastypes.h>
53#endif
54
55#ifdef LIBXML_PATTERN_ENABLED
56#include <libxml/pattern.h>
57#endif
58
59#ifdef LIBXML_C14N_ENABLED
60#include <libxml/c14n.h>
61#endif
62
63#ifdef LIBXML_HTML_ENABLED
64#include <libxml/HTMLparser.h>
65#include <libxml/HTMLtree.h>
66
67/*
68 * pseudo flag for the unification of HTML and XML tests
69 */
70#define XML_PARSE_HTML 1 << 24
71#endif
72
73#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED)
74#include <libxml/globals.h>
75#include <libxml/threads.h>
76#include <libxml/parser.h>
77#include <libxml/catalog.h>
78#include <string.h>
79#endif
80
81/*
82 * O_BINARY is just for Windows compatibility - if it isn't defined
83 * on this system, avoid any compilation error
84 */
85#ifdef	O_BINARY
86#define RD_FLAGS	O_RDONLY | O_BINARY
87#else
88#define	RD_FLAGS	O_RDONLY
89#endif
90
91typedef int (*functest) (const char *filename, const char *result,
92                         const char *error, int options);
93
94typedef struct testDesc testDesc;
95typedef testDesc *testDescPtr;
96struct testDesc {
97    const char *desc; /* descripton of the test */
98    functest    func; /* function implementing the test */
99    const char *in;   /* glob to path for input files */
100    const char *out;  /* output directory */
101    const char *suffix;/* suffix for output files */
102    const char *err;  /* suffix for error output files */
103    int     options;  /* parser options for the test */
104};
105
106static int checkTestFile(const char *filename);
107
108#if defined(_WIN32) && !defined(__CYGWIN__)
109
110#include <windows.h>
111#include <io.h>
112
113typedef struct
114{
115      size_t gl_pathc;    /* Count of paths matched so far  */
116      char **gl_pathv;    /* List of matched pathnames.  */
117      size_t gl_offs;     /* Slots to reserve in 'gl_pathv'.  */
118} glob_t;
119
120#define GLOB_DOOFFS 0
121static int glob(const char *pattern, int flags,
122                int errfunc(const char *epath, int eerrno),
123                glob_t *pglob) {
124    glob_t *ret;
125    WIN32_FIND_DATA FindFileData;
126    HANDLE hFind;
127    unsigned int nb_paths = 0;
128    char directory[500];
129    int len;
130
131    if ((pattern == NULL) || (pglob == NULL)) return(-1);
132
133    strncpy(directory, pattern, 499);
134    for (len = strlen(directory);len >= 0;len--) {
135        if (directory[len] == '/') {
136	    len++;
137	    directory[len] = 0;
138	    break;
139	}
140    }
141    if (len <= 0)
142        len = 0;
143
144
145    ret = pglob;
146    memset(ret, 0, sizeof(glob_t));
147
148    hFind = FindFirstFileA(pattern, &FindFileData);
149    if (hFind == INVALID_HANDLE_VALUE)
150        return(0);
151    nb_paths = 20;
152    ret->gl_pathv = (char **) malloc(nb_paths * sizeof(char *));
153    if (ret->gl_pathv == NULL) {
154	FindClose(hFind);
155        return(-1);
156    }
157    strncpy(directory + len, FindFileData.cFileName, 499 - len);
158    ret->gl_pathv[ret->gl_pathc] = strdup(directory);
159    if (ret->gl_pathv[ret->gl_pathc] == NULL)
160        goto done;
161    ret->gl_pathc++;
162    while(FindNextFileA(hFind, &FindFileData)) {
163        if (FindFileData.cFileName[0] == '.')
164	    continue;
165        if (ret->gl_pathc + 2 > nb_paths) {
166            char **tmp = realloc(ret->gl_pathv, nb_paths * 2 * sizeof(char *));
167            if (tmp == NULL)
168                break;
169            ret->gl_pathv = tmp;
170            nb_paths *= 2;
171	}
172	strncpy(directory + len, FindFileData.cFileName, 499 - len);
173	ret->gl_pathv[ret->gl_pathc] = strdup(directory);
174        if (ret->gl_pathv[ret->gl_pathc] == NULL)
175            break;
176        ret->gl_pathc++;
177    }
178    ret->gl_pathv[ret->gl_pathc] = NULL;
179
180done:
181    FindClose(hFind);
182    return(0);
183}
184
185
186
187static void globfree(glob_t *pglob) {
188    unsigned int i;
189    if (pglob == NULL)
190        return;
191
192    for (i = 0;i < pglob->gl_pathc;i++) {
193         if (pglob->gl_pathv[i] != NULL)
194             free(pglob->gl_pathv[i]);
195    }
196}
197#define vsnprintf _vsnprintf
198#define snprintf _snprintf
199#else
200#include <glob.h>
201#endif
202
203/************************************************************************
204 *									*
205 *		Libxml2 specific routines				*
206 *									*
207 ************************************************************************/
208
209static int nb_tests = 0;
210static int nb_errors = 0;
211static int nb_leaks = 0;
212static int extraMemoryFromResolver = 0;
213
214static int
215fatalError(void) {
216    fprintf(stderr, "Exitting tests on fatal error\n");
217    exit(1);
218}
219
220/*
221 * We need to trap calls to the resolver to not account memory for the catalog
222 * which is shared to the current running test. We also don't want to have
223 * network downloads modifying tests.
224 */
225static xmlParserInputPtr
226testExternalEntityLoader(const char *URL, const char *ID,
227			 xmlParserCtxtPtr ctxt) {
228    xmlParserInputPtr ret;
229
230    if (checkTestFile(URL)) {
231	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
232    } else {
233	int memused = xmlMemUsed();
234	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
235	extraMemoryFromResolver += xmlMemUsed() - memused;
236    }
237
238    return(ret);
239}
240
241/*
242 * Trapping the error messages at the generic level to grab the equivalent of
243 * stderr messages on CLI tools.
244 */
245static char testErrors[32769];
246static int testErrorsSize = 0;
247
248static void XMLCDECL
249testErrorHandler(void *ctx  ATTRIBUTE_UNUSED, const char *msg, ...) {
250    va_list args;
251    int res;
252
253    if (testErrorsSize >= 32768)
254        return;
255    va_start(args, msg);
256    res = vsnprintf(&testErrors[testErrorsSize],
257                    32768 - testErrorsSize,
258		    msg, args);
259    va_end(args);
260    if (testErrorsSize + res >= 32768) {
261        /* buffer is full */
262	testErrorsSize = 32768;
263	testErrors[testErrorsSize] = 0;
264    } else {
265        testErrorsSize += res;
266    }
267    testErrors[testErrorsSize] = 0;
268}
269
270static void XMLCDECL
271channel(void *ctx  ATTRIBUTE_UNUSED, const char *msg, ...) {
272    va_list args;
273    int res;
274
275    if (testErrorsSize >= 32768)
276        return;
277    va_start(args, msg);
278    res = vsnprintf(&testErrors[testErrorsSize],
279                    32768 - testErrorsSize,
280		    msg, args);
281    va_end(args);
282    if (testErrorsSize + res >= 32768) {
283        /* buffer is full */
284	testErrorsSize = 32768;
285	testErrors[testErrorsSize] = 0;
286    } else {
287        testErrorsSize += res;
288    }
289    testErrors[testErrorsSize] = 0;
290}
291
292/**
293 * xmlParserPrintFileContext:
294 * @input:  an xmlParserInputPtr input
295 *
296 * Displays current context within the input content for error tracking
297 */
298
299static void
300xmlParserPrintFileContextInternal(xmlParserInputPtr input ,
301		xmlGenericErrorFunc chanl, void *data ) {
302    const xmlChar *cur, *base;
303    unsigned int n, col;	/* GCC warns if signed, because compared with sizeof() */
304    xmlChar  content[81]; /* space for 80 chars + line terminator */
305    xmlChar *ctnt;
306
307    if (input == NULL) return;
308    cur = input->cur;
309    base = input->base;
310    /* skip backwards over any end-of-lines */
311    while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) {
312	cur--;
313    }
314    n = 0;
315    /* search backwards for beginning-of-line (to max buff size) */
316    while ((n++ < (sizeof(content)-1)) && (cur > base) &&
317   (*(cur) != '\n') && (*(cur) != '\r'))
318        cur--;
319    if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;
320    /* calculate the error position in terms of the current position */
321    col = input->cur - cur;
322    /* search forward for end-of-line (to max buff size) */
323    n = 0;
324    ctnt = content;
325    /* copy selected text to our buffer */
326    while ((*cur != 0) && (*(cur) != '\n') &&
327   (*(cur) != '\r') && (n < sizeof(content)-1)) {
328		*ctnt++ = *cur++;
329	n++;
330    }
331    *ctnt = 0;
332    /* print out the selected text */
333    chanl(data ,"%s\n", content);
334    /* create blank line with problem pointer */
335    n = 0;
336    ctnt = content;
337    /* (leave buffer space for pointer + line terminator) */
338    while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) {
339	if (*(ctnt) != '\t')
340	    *(ctnt) = ' ';
341	ctnt++;
342    }
343    *ctnt++ = '^';
344    *ctnt = 0;
345    chanl(data ,"%s\n", content);
346}
347
348static void
349testStructuredErrorHandler(void *ctx  ATTRIBUTE_UNUSED, xmlErrorPtr err) {
350    char *file = NULL;
351    int line = 0;
352    int code = -1;
353    int domain;
354    void *data = NULL;
355    const char *str;
356    const xmlChar *name = NULL;
357    xmlNodePtr node;
358    xmlErrorLevel level;
359    xmlParserInputPtr input = NULL;
360    xmlParserInputPtr cur = NULL;
361    xmlParserCtxtPtr ctxt = NULL;
362
363    if (err == NULL)
364        return;
365
366    file = err->file;
367    line = err->line;
368    code = err->code;
369    domain = err->domain;
370    level = err->level;
371    node = err->node;
372    if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) ||
373        (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) ||
374	(domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) {
375	ctxt = err->ctxt;
376    }
377    str = err->message;
378
379    if (code == XML_ERR_OK)
380        return;
381
382    if ((node != NULL) && (node->type == XML_ELEMENT_NODE))
383        name = node->name;
384
385    /*
386     * Maintain the compatibility with the legacy error handling
387     */
388    if (ctxt != NULL) {
389        input = ctxt->input;
390        if ((input != NULL) && (input->filename == NULL) &&
391            (ctxt->inputNr > 1)) {
392            cur = input;
393            input = ctxt->inputTab[ctxt->inputNr - 2];
394        }
395        if (input != NULL) {
396            if (input->filename)
397                channel(data, "%s:%d: ", input->filename, input->line);
398            else if ((line != 0) && (domain == XML_FROM_PARSER))
399                channel(data, "Entity: line %d: ", input->line);
400        }
401    } else {
402        if (file != NULL)
403            channel(data, "%s:%d: ", file, line);
404        else if ((line != 0) && (domain == XML_FROM_PARSER))
405            channel(data, "Entity: line %d: ", line);
406    }
407    if (name != NULL) {
408        channel(data, "element %s: ", name);
409    }
410    if (code == XML_ERR_OK)
411        return;
412    switch (domain) {
413        case XML_FROM_PARSER:
414            channel(data, "parser ");
415            break;
416        case XML_FROM_NAMESPACE:
417            channel(data, "namespace ");
418            break;
419        case XML_FROM_DTD:
420        case XML_FROM_VALID:
421            channel(data, "validity ");
422            break;
423        case XML_FROM_HTML:
424            channel(data, "HTML parser ");
425            break;
426        case XML_FROM_MEMORY:
427            channel(data, "memory ");
428            break;
429        case XML_FROM_OUTPUT:
430            channel(data, "output ");
431            break;
432        case XML_FROM_IO:
433            channel(data, "I/O ");
434            break;
435        case XML_FROM_XINCLUDE:
436            channel(data, "XInclude ");
437            break;
438        case XML_FROM_XPATH:
439            channel(data, "XPath ");
440            break;
441        case XML_FROM_XPOINTER:
442            channel(data, "parser ");
443            break;
444        case XML_FROM_REGEXP:
445            channel(data, "regexp ");
446            break;
447        case XML_FROM_MODULE:
448            channel(data, "module ");
449            break;
450        case XML_FROM_SCHEMASV:
451            channel(data, "Schemas validity ");
452            break;
453        case XML_FROM_SCHEMASP:
454            channel(data, "Schemas parser ");
455            break;
456        case XML_FROM_RELAXNGP:
457            channel(data, "Relax-NG parser ");
458            break;
459        case XML_FROM_RELAXNGV:
460            channel(data, "Relax-NG validity ");
461            break;
462        case XML_FROM_CATALOG:
463            channel(data, "Catalog ");
464            break;
465        case XML_FROM_C14N:
466            channel(data, "C14N ");
467            break;
468        case XML_FROM_XSLT:
469            channel(data, "XSLT ");
470            break;
471        default:
472            break;
473    }
474    if (code == XML_ERR_OK)
475        return;
476    switch (level) {
477        case XML_ERR_NONE:
478            channel(data, ": ");
479            break;
480        case XML_ERR_WARNING:
481            channel(data, "warning : ");
482            break;
483        case XML_ERR_ERROR:
484            channel(data, "error : ");
485            break;
486        case XML_ERR_FATAL:
487            channel(data, "error : ");
488            break;
489    }
490    if (code == XML_ERR_OK)
491        return;
492    if (str != NULL) {
493        int len;
494	len = xmlStrlen((const xmlChar *)str);
495	if ((len > 0) && (str[len - 1] != '\n'))
496	    channel(data, "%s\n", str);
497	else
498	    channel(data, "%s", str);
499    } else {
500        channel(data, "%s\n", "out of memory error");
501    }
502    if (code == XML_ERR_OK)
503        return;
504
505    if (ctxt != NULL) {
506        xmlParserPrintFileContextInternal(input, channel, data);
507        if (cur != NULL) {
508            if (cur->filename)
509                channel(data, "%s:%d: \n", cur->filename, cur->line);
510            else if ((line != 0) && (domain == XML_FROM_PARSER))
511                channel(data, "Entity: line %d: \n", cur->line);
512            xmlParserPrintFileContextInternal(cur, channel, data);
513        }
514    }
515    if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) &&
516        (err->int1 < 100) &&
517	(err->int1 < xmlStrlen((const xmlChar *)err->str1))) {
518	xmlChar buf[150];
519	int i;
520
521	channel(data, "%s\n", err->str1);
522	for (i=0;i < err->int1;i++)
523	     buf[i] = ' ';
524	buf[i++] = '^';
525	buf[i] = 0;
526	channel(data, "%s\n", buf);
527    }
528}
529
530static void
531initializeLibxml2(void) {
532    xmlGetWarningsDefaultValue = 0;
533    xmlPedanticParserDefault(0);
534
535    xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
536    xmlInitParser();
537    xmlSetExternalEntityLoader(testExternalEntityLoader);
538    xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
539#ifdef LIBXML_SCHEMAS_ENABLED
540    xmlSchemaInitTypes();
541    xmlRelaxNGInitTypes();
542#endif
543}
544
545
546/************************************************************************
547 *									*
548 *		File name and path utilities				*
549 *									*
550 ************************************************************************/
551
552static const char *baseFilename(const char *filename) {
553    const char *cur;
554    if (filename == NULL)
555        return(NULL);
556    cur = &filename[strlen(filename)];
557    while ((cur > filename) && (*cur != '/'))
558        cur--;
559    if (*cur == '/')
560        return(cur + 1);
561    return(cur);
562}
563
564static char *resultFilename(const char *filename, const char *out,
565                            const char *suffix) {
566    const char *base;
567    char res[500];
568    char suffixbuff[500];
569
570/*************
571    if ((filename[0] == 't') && (filename[1] == 'e') &&
572        (filename[2] == 's') && (filename[3] == 't') &&
573	(filename[4] == '/'))
574	filename = &filename[5];
575 *************/
576
577    base = baseFilename(filename);
578    if (suffix == NULL)
579        suffix = ".tmp";
580    if (out == NULL)
581        out = "";
582
583    strncpy(suffixbuff,suffix,499);
584#ifdef VMS
585    if(strstr(base,".") && suffixbuff[0]=='.')
586      suffixbuff[0]='_';
587#endif
588
589    snprintf(res, 499, "%s%s%s", out, base, suffixbuff);
590    res[499] = 0;
591    return(strdup(res));
592}
593
594static int checkTestFile(const char *filename) {
595    struct stat buf;
596
597    if (stat(filename, &buf) == -1)
598        return(0);
599
600#if defined(_WIN32) && !defined(__CYGWIN__)
601    if (!(buf.st_mode & _S_IFREG))
602        return(0);
603#else
604    if (!S_ISREG(buf.st_mode))
605        return(0);
606#endif
607
608    return(1);
609}
610
611static int compareFiles(const char *r1, const char *r2) {
612    int res1, res2;
613    int fd1, fd2;
614    char bytes1[4096];
615    char bytes2[4096];
616
617    fd1 = open(r1, RD_FLAGS);
618    if (fd1 < 0)
619        return(-1);
620    fd2 = open(r2, RD_FLAGS);
621    if (fd2 < 0) {
622        close(fd1);
623        return(-1);
624    }
625    while (1) {
626        res1 = read(fd1, bytes1, 4096);
627        res2 = read(fd2, bytes2, 4096);
628	if ((res1 != res2) || (res1 < 0)) {
629	    close(fd1);
630	    close(fd2);
631	    return(1);
632	}
633	if (res1 == 0)
634	    break;
635	if (memcmp(bytes1, bytes2, res1) != 0) {
636	    close(fd1);
637	    close(fd2);
638	    return(1);
639	}
640    }
641    close(fd1);
642    close(fd2);
643    return(0);
644}
645
646static int compareFileMem(const char *filename, const char *mem, int size) {
647    int res;
648    int fd;
649    char bytes[4096];
650    int idx = 0;
651    struct stat info;
652
653    if (stat(filename, &info) < 0)
654	return(-1);
655    if (info.st_size != size)
656        return(-1);
657    fd = open(filename, RD_FLAGS);
658    if (fd < 0)
659        return(-1);
660    while (idx < size) {
661        res = read(fd, bytes, 4096);
662	if (res <= 0)
663	    break;
664	if (res + idx > size)
665	    break;
666	if (memcmp(bytes, &mem[idx], res) != 0) {
667	    int ix;
668	    for (ix=0; ix<res; ix++)
669		if (bytes[ix] != mem[idx+ix])
670			break;
671	    fprintf(stderr,"Compare error at position %d\n", idx+ix);
672	    close(fd);
673	    return(1);
674	}
675	idx += res;
676    }
677    close(fd);
678    return(idx != size);
679}
680
681static int loadMem(const char *filename, const char **mem, int *size) {
682    int fd, res;
683    struct stat info;
684    char *base;
685    int siz = 0;
686    if (stat(filename, &info) < 0)
687	return(-1);
688    base = malloc(info.st_size + 1);
689    if (base == NULL)
690	return(-1);
691    if ((fd = open(filename, RD_FLAGS)) < 0) {
692        free(base);
693	return(-1);
694    }
695    while ((res = read(fd, &base[siz], info.st_size - siz)) > 0) {
696        siz += res;
697    }
698    close(fd);
699#if !defined(_WIN32)
700    if (siz != info.st_size) {
701        free(base);
702	return(-1);
703    }
704#endif
705    base[siz] = 0;
706    *mem = base;
707    *size = siz;
708    return(0);
709}
710
711static int unloadMem(const char *mem) {
712    free((char *)mem);
713    return(0);
714}
715
716/************************************************************************
717 *									*
718 *		Tests implementations					*
719 *									*
720 ************************************************************************/
721
722/************************************************************************
723 *									*
724 *		Parse to SAX based tests				*
725 *									*
726 ************************************************************************/
727
728static FILE *SAXdebug = NULL;
729
730/*
731 * empty SAX block
732 */
733static xmlSAXHandler emptySAXHandlerStruct = {
734    NULL, /* internalSubset */
735    NULL, /* isStandalone */
736    NULL, /* hasInternalSubset */
737    NULL, /* hasExternalSubset */
738    NULL, /* resolveEntity */
739    NULL, /* getEntity */
740    NULL, /* entityDecl */
741    NULL, /* notationDecl */
742    NULL, /* attributeDecl */
743    NULL, /* elementDecl */
744    NULL, /* unparsedEntityDecl */
745    NULL, /* setDocumentLocator */
746    NULL, /* startDocument */
747    NULL, /* endDocument */
748    NULL, /* startElement */
749    NULL, /* endElement */
750    NULL, /* reference */
751    NULL, /* characters */
752    NULL, /* ignorableWhitespace */
753    NULL, /* processingInstruction */
754    NULL, /* comment */
755    NULL, /* xmlParserWarning */
756    NULL, /* xmlParserError */
757    NULL, /* xmlParserError */
758    NULL, /* getParameterEntity */
759    NULL, /* cdataBlock; */
760    NULL, /* externalSubset; */
761    1,
762    NULL,
763    NULL, /* startElementNs */
764    NULL, /* endElementNs */
765    NULL  /* xmlStructuredErrorFunc */
766};
767
768static xmlSAXHandlerPtr emptySAXHandler = &emptySAXHandlerStruct;
769static int callbacks = 0;
770static int quiet = 0;
771
772/**
773 * isStandaloneDebug:
774 * @ctxt:  An XML parser context
775 *
776 * Is this document tagged standalone ?
777 *
778 * Returns 1 if true
779 */
780static int
781isStandaloneDebug(void *ctx ATTRIBUTE_UNUSED)
782{
783    callbacks++;
784    if (quiet)
785	return(0);
786    fprintf(SAXdebug, "SAX.isStandalone()\n");
787    return(0);
788}
789
790/**
791 * hasInternalSubsetDebug:
792 * @ctxt:  An XML parser context
793 *
794 * Does this document has an internal subset
795 *
796 * Returns 1 if true
797 */
798static int
799hasInternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED)
800{
801    callbacks++;
802    if (quiet)
803	return(0);
804    fprintf(SAXdebug, "SAX.hasInternalSubset()\n");
805    return(0);
806}
807
808/**
809 * hasExternalSubsetDebug:
810 * @ctxt:  An XML parser context
811 *
812 * Does this document has an external subset
813 *
814 * Returns 1 if true
815 */
816static int
817hasExternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED)
818{
819    callbacks++;
820    if (quiet)
821	return(0);
822    fprintf(SAXdebug, "SAX.hasExternalSubset()\n");
823    return(0);
824}
825
826/**
827 * internalSubsetDebug:
828 * @ctxt:  An XML parser context
829 *
830 * Does this document has an internal subset
831 */
832static void
833internalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
834	       const xmlChar *ExternalID, const xmlChar *SystemID)
835{
836    callbacks++;
837    if (quiet)
838	return;
839    fprintf(SAXdebug, "SAX.internalSubset(%s,", name);
840    if (ExternalID == NULL)
841	fprintf(SAXdebug, " ,");
842    else
843	fprintf(SAXdebug, " %s,", ExternalID);
844    if (SystemID == NULL)
845	fprintf(SAXdebug, " )\n");
846    else
847	fprintf(SAXdebug, " %s)\n", SystemID);
848}
849
850/**
851 * externalSubsetDebug:
852 * @ctxt:  An XML parser context
853 *
854 * Does this document has an external subset
855 */
856static void
857externalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
858	       const xmlChar *ExternalID, const xmlChar *SystemID)
859{
860    callbacks++;
861    if (quiet)
862	return;
863    fprintf(SAXdebug, "SAX.externalSubset(%s,", name);
864    if (ExternalID == NULL)
865	fprintf(SAXdebug, " ,");
866    else
867	fprintf(SAXdebug, " %s,", ExternalID);
868    if (SystemID == NULL)
869	fprintf(SAXdebug, " )\n");
870    else
871	fprintf(SAXdebug, " %s)\n", SystemID);
872}
873
874/**
875 * resolveEntityDebug:
876 * @ctxt:  An XML parser context
877 * @publicId: The public ID of the entity
878 * @systemId: The system ID of the entity
879 *
880 * Special entity resolver, better left to the parser, it has
881 * more context than the application layer.
882 * The default behaviour is to NOT resolve the entities, in that case
883 * the ENTITY_REF nodes are built in the structure (and the parameter
884 * values).
885 *
886 * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
887 */
888static xmlParserInputPtr
889resolveEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *publicId, const xmlChar *systemId)
890{
891    callbacks++;
892    if (quiet)
893	return(NULL);
894    /* xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; */
895
896
897    fprintf(SAXdebug, "SAX.resolveEntity(");
898    if (publicId != NULL)
899	fprintf(SAXdebug, "%s", (char *)publicId);
900    else
901	fprintf(SAXdebug, " ");
902    if (systemId != NULL)
903	fprintf(SAXdebug, ", %s)\n", (char *)systemId);
904    else
905	fprintf(SAXdebug, ", )\n");
906/*********
907    if (systemId != NULL) {
908        return(xmlNewInputFromFile(ctxt, (char *) systemId));
909    }
910 *********/
911    return(NULL);
912}
913
914/**
915 * getEntityDebug:
916 * @ctxt:  An XML parser context
917 * @name: The entity name
918 *
919 * Get an entity by name
920 *
921 * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
922 */
923static xmlEntityPtr
924getEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
925{
926    callbacks++;
927    if (quiet)
928	return(NULL);
929    fprintf(SAXdebug, "SAX.getEntity(%s)\n", name);
930    return(NULL);
931}
932
933/**
934 * getParameterEntityDebug:
935 * @ctxt:  An XML parser context
936 * @name: The entity name
937 *
938 * Get a parameter entity by name
939 *
940 * Returns the xmlParserInputPtr
941 */
942static xmlEntityPtr
943getParameterEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
944{
945    callbacks++;
946    if (quiet)
947	return(NULL);
948    fprintf(SAXdebug, "SAX.getParameterEntity(%s)\n", name);
949    return(NULL);
950}
951
952
953/**
954 * entityDeclDebug:
955 * @ctxt:  An XML parser context
956 * @name:  the entity name
957 * @type:  the entity type
958 * @publicId: The public ID of the entity
959 * @systemId: The system ID of the entity
960 * @content: the entity value (without processing).
961 *
962 * An entity definition has been parsed
963 */
964static void
965entityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type,
966          const xmlChar *publicId, const xmlChar *systemId, xmlChar *content)
967{
968const xmlChar *nullstr = BAD_CAST "(null)";
969    /* not all libraries handle printing null pointers nicely */
970    if (publicId == NULL)
971        publicId = nullstr;
972    if (systemId == NULL)
973        systemId = nullstr;
974    if (content == NULL)
975        content = (xmlChar *)nullstr;
976    callbacks++;
977    if (quiet)
978	return;
979    fprintf(SAXdebug, "SAX.entityDecl(%s, %d, %s, %s, %s)\n",
980            name, type, publicId, systemId, content);
981}
982
983/**
984 * attributeDeclDebug:
985 * @ctxt:  An XML parser context
986 * @name:  the attribute name
987 * @type:  the attribute type
988 *
989 * An attribute definition has been parsed
990 */
991static void
992attributeDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar * elem,
993                   const xmlChar * name, int type, int def,
994                   const xmlChar * defaultValue, xmlEnumerationPtr tree)
995{
996    callbacks++;
997    if (quiet)
998        return;
999    if (defaultValue == NULL)
1000        fprintf(SAXdebug, "SAX.attributeDecl(%s, %s, %d, %d, NULL, ...)\n",
1001                elem, name, type, def);
1002    else
1003        fprintf(SAXdebug, "SAX.attributeDecl(%s, %s, %d, %d, %s, ...)\n",
1004                elem, name, type, def, defaultValue);
1005    xmlFreeEnumeration(tree);
1006}
1007
1008/**
1009 * elementDeclDebug:
1010 * @ctxt:  An XML parser context
1011 * @name:  the element name
1012 * @type:  the element type
1013 * @content: the element value (without processing).
1014 *
1015 * An element definition has been parsed
1016 */
1017static void
1018elementDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type,
1019	    xmlElementContentPtr content ATTRIBUTE_UNUSED)
1020{
1021    callbacks++;
1022    if (quiet)
1023	return;
1024    fprintf(SAXdebug, "SAX.elementDecl(%s, %d, ...)\n",
1025            name, type);
1026}
1027
1028/**
1029 * notationDeclDebug:
1030 * @ctxt:  An XML parser context
1031 * @name: The name of the notation
1032 * @publicId: The public ID of the entity
1033 * @systemId: The system ID of the entity
1034 *
1035 * What to do when a notation declaration has been parsed.
1036 */
1037static void
1038notationDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
1039	     const xmlChar *publicId, const xmlChar *systemId)
1040{
1041    callbacks++;
1042    if (quiet)
1043	return;
1044    fprintf(SAXdebug, "SAX.notationDecl(%s, %s, %s)\n",
1045            (char *) name, (char *) publicId, (char *) systemId);
1046}
1047
1048/**
1049 * unparsedEntityDeclDebug:
1050 * @ctxt:  An XML parser context
1051 * @name: The name of the entity
1052 * @publicId: The public ID of the entity
1053 * @systemId: The system ID of the entity
1054 * @notationName: the name of the notation
1055 *
1056 * What to do when an unparsed entity declaration is parsed
1057 */
1058static void
1059unparsedEntityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
1060		   const xmlChar *publicId, const xmlChar *systemId,
1061		   const xmlChar *notationName)
1062{
1063const xmlChar *nullstr = BAD_CAST "(null)";
1064
1065    if (publicId == NULL)
1066        publicId = nullstr;
1067    if (systemId == NULL)
1068        systemId = nullstr;
1069    if (notationName == NULL)
1070        notationName = nullstr;
1071    callbacks++;
1072    if (quiet)
1073	return;
1074    fprintf(SAXdebug, "SAX.unparsedEntityDecl(%s, %s, %s, %s)\n",
1075            (char *) name, (char *) publicId, (char *) systemId,
1076	    (char *) notationName);
1077}
1078
1079/**
1080 * setDocumentLocatorDebug:
1081 * @ctxt:  An XML parser context
1082 * @loc: A SAX Locator
1083 *
1084 * Receive the document locator at startup, actually xmlDefaultSAXLocator
1085 * Everything is available on the context, so this is useless in our case.
1086 */
1087static void
1088setDocumentLocatorDebug(void *ctx ATTRIBUTE_UNUSED, xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED)
1089{
1090    callbacks++;
1091    if (quiet)
1092	return;
1093    fprintf(SAXdebug, "SAX.setDocumentLocator()\n");
1094}
1095
1096/**
1097 * startDocumentDebug:
1098 * @ctxt:  An XML parser context
1099 *
1100 * called when the document start being processed.
1101 */
1102static void
1103startDocumentDebug(void *ctx ATTRIBUTE_UNUSED)
1104{
1105    callbacks++;
1106    if (quiet)
1107	return;
1108    fprintf(SAXdebug, "SAX.startDocument()\n");
1109}
1110
1111/**
1112 * endDocumentDebug:
1113 * @ctxt:  An XML parser context
1114 *
1115 * called when the document end has been detected.
1116 */
1117static void
1118endDocumentDebug(void *ctx ATTRIBUTE_UNUSED)
1119{
1120    callbacks++;
1121    if (quiet)
1122	return;
1123    fprintf(SAXdebug, "SAX.endDocument()\n");
1124}
1125
1126/**
1127 * startElementDebug:
1128 * @ctxt:  An XML parser context
1129 * @name:  The element name
1130 *
1131 * called when an opening tag has been processed.
1132 */
1133static void
1134startElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar **atts)
1135{
1136    int i;
1137
1138    callbacks++;
1139    if (quiet)
1140	return;
1141    fprintf(SAXdebug, "SAX.startElement(%s", (char *) name);
1142    if (atts != NULL) {
1143        for (i = 0;(atts[i] != NULL);i++) {
1144	    fprintf(SAXdebug, ", %s='", atts[i++]);
1145	    if (atts[i] != NULL)
1146	        fprintf(SAXdebug, "%s'", atts[i]);
1147	}
1148    }
1149    fprintf(SAXdebug, ")\n");
1150}
1151
1152/**
1153 * endElementDebug:
1154 * @ctxt:  An XML parser context
1155 * @name:  The element name
1156 *
1157 * called when the end of an element has been detected.
1158 */
1159static void
1160endElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
1161{
1162    callbacks++;
1163    if (quiet)
1164	return;
1165    fprintf(SAXdebug, "SAX.endElement(%s)\n", (char *) name);
1166}
1167
1168/**
1169 * charactersDebug:
1170 * @ctxt:  An XML parser context
1171 * @ch:  a xmlChar string
1172 * @len: the number of xmlChar
1173 *
1174 * receiving some chars from the parser.
1175 * Question: how much at a time ???
1176 */
1177static void
1178charactersDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
1179{
1180    char output[40];
1181    int i;
1182
1183    callbacks++;
1184    if (quiet)
1185	return;
1186    for (i = 0;(i<len) && (i < 30);i++)
1187	output[i] = ch[i];
1188    output[i] = 0;
1189
1190    fprintf(SAXdebug, "SAX.characters(%s, %d)\n", output, len);
1191}
1192
1193/**
1194 * referenceDebug:
1195 * @ctxt:  An XML parser context
1196 * @name:  The entity name
1197 *
1198 * called when an entity reference is detected.
1199 */
1200static void
1201referenceDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
1202{
1203    callbacks++;
1204    if (quiet)
1205	return;
1206    fprintf(SAXdebug, "SAX.reference(%s)\n", name);
1207}
1208
1209/**
1210 * ignorableWhitespaceDebug:
1211 * @ctxt:  An XML parser context
1212 * @ch:  a xmlChar string
1213 * @start: the first char in the string
1214 * @len: the number of xmlChar
1215 *
1216 * receiving some ignorable whitespaces from the parser.
1217 * Question: how much at a time ???
1218 */
1219static void
1220ignorableWhitespaceDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
1221{
1222    char output[40];
1223    int i;
1224
1225    callbacks++;
1226    if (quiet)
1227	return;
1228    for (i = 0;(i<len) && (i < 30);i++)
1229	output[i] = ch[i];
1230    output[i] = 0;
1231    fprintf(SAXdebug, "SAX.ignorableWhitespace(%s, %d)\n", output, len);
1232}
1233
1234/**
1235 * processingInstructionDebug:
1236 * @ctxt:  An XML parser context
1237 * @target:  the target name
1238 * @data: the PI data's
1239 * @len: the number of xmlChar
1240 *
1241 * A processing instruction has been parsed.
1242 */
1243static void
1244processingInstructionDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *target,
1245                      const xmlChar *data)
1246{
1247    callbacks++;
1248    if (quiet)
1249	return;
1250    if (data != NULL)
1251	fprintf(SAXdebug, "SAX.processingInstruction(%s, %s)\n",
1252		(char *) target, (char *) data);
1253    else
1254	fprintf(SAXdebug, "SAX.processingInstruction(%s, NULL)\n",
1255		(char *) target);
1256}
1257
1258/**
1259 * cdataBlockDebug:
1260 * @ctx: the user data (XML parser context)
1261 * @value:  The pcdata content
1262 * @len:  the block length
1263 *
1264 * called when a pcdata block has been parsed
1265 */
1266static void
1267cdataBlockDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *value, int len)
1268{
1269    callbacks++;
1270    if (quiet)
1271	return;
1272    fprintf(SAXdebug, "SAX.pcdata(%.20s, %d)\n",
1273	    (char *) value, len);
1274}
1275
1276/**
1277 * commentDebug:
1278 * @ctxt:  An XML parser context
1279 * @value:  the comment content
1280 *
1281 * A comment has been parsed.
1282 */
1283static void
1284commentDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *value)
1285{
1286    callbacks++;
1287    if (quiet)
1288	return;
1289    fprintf(SAXdebug, "SAX.comment(%s)\n", value);
1290}
1291
1292/**
1293 * warningDebug:
1294 * @ctxt:  An XML parser context
1295 * @msg:  the message to display/transmit
1296 * @...:  extra parameters for the message display
1297 *
1298 * Display and format a warning messages, gives file, line, position and
1299 * extra parameters.
1300 */
1301static void XMLCDECL
1302warningDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...)
1303{
1304    va_list args;
1305
1306    callbacks++;
1307    if (quiet)
1308	return;
1309    va_start(args, msg);
1310    fprintf(SAXdebug, "SAX.warning: ");
1311    vfprintf(SAXdebug, msg, args);
1312    va_end(args);
1313}
1314
1315/**
1316 * errorDebug:
1317 * @ctxt:  An XML parser context
1318 * @msg:  the message to display/transmit
1319 * @...:  extra parameters for the message display
1320 *
1321 * Display and format a error messages, gives file, line, position and
1322 * extra parameters.
1323 */
1324static void XMLCDECL
1325errorDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...)
1326{
1327    va_list args;
1328
1329    callbacks++;
1330    if (quiet)
1331	return;
1332    va_start(args, msg);
1333    fprintf(SAXdebug, "SAX.error: ");
1334    vfprintf(SAXdebug, msg, args);
1335    va_end(args);
1336}
1337
1338/**
1339 * fatalErrorDebug:
1340 * @ctxt:  An XML parser context
1341 * @msg:  the message to display/transmit
1342 * @...:  extra parameters for the message display
1343 *
1344 * Display and format a fatalError messages, gives file, line, position and
1345 * extra parameters.
1346 */
1347static void XMLCDECL
1348fatalErrorDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...)
1349{
1350    va_list args;
1351
1352    callbacks++;
1353    if (quiet)
1354	return;
1355    va_start(args, msg);
1356    fprintf(SAXdebug, "SAX.fatalError: ");
1357    vfprintf(SAXdebug, msg, args);
1358    va_end(args);
1359}
1360
1361static xmlSAXHandler debugSAXHandlerStruct = {
1362    internalSubsetDebug,
1363    isStandaloneDebug,
1364    hasInternalSubsetDebug,
1365    hasExternalSubsetDebug,
1366    resolveEntityDebug,
1367    getEntityDebug,
1368    entityDeclDebug,
1369    notationDeclDebug,
1370    attributeDeclDebug,
1371    elementDeclDebug,
1372    unparsedEntityDeclDebug,
1373    setDocumentLocatorDebug,
1374    startDocumentDebug,
1375    endDocumentDebug,
1376    startElementDebug,
1377    endElementDebug,
1378    referenceDebug,
1379    charactersDebug,
1380    ignorableWhitespaceDebug,
1381    processingInstructionDebug,
1382    commentDebug,
1383    warningDebug,
1384    errorDebug,
1385    fatalErrorDebug,
1386    getParameterEntityDebug,
1387    cdataBlockDebug,
1388    externalSubsetDebug,
1389    1,
1390    NULL,
1391    NULL,
1392    NULL,
1393    NULL
1394};
1395
1396static xmlSAXHandlerPtr debugSAXHandler = &debugSAXHandlerStruct;
1397
1398/*
1399 * SAX2 specific callbacks
1400 */
1401/**
1402 * startElementNsDebug:
1403 * @ctxt:  An XML parser context
1404 * @name:  The element name
1405 *
1406 * called when an opening tag has been processed.
1407 */
1408static void
1409startElementNsDebug(void *ctx ATTRIBUTE_UNUSED,
1410                    const xmlChar *localname,
1411                    const xmlChar *prefix,
1412                    const xmlChar *URI,
1413		    int nb_namespaces,
1414		    const xmlChar **namespaces,
1415		    int nb_attributes,
1416		    int nb_defaulted,
1417		    const xmlChar **attributes)
1418{
1419    int i;
1420
1421    callbacks++;
1422    if (quiet)
1423	return;
1424    fprintf(SAXdebug, "SAX.startElementNs(%s", (char *) localname);
1425    if (prefix == NULL)
1426	fprintf(SAXdebug, ", NULL");
1427    else
1428	fprintf(SAXdebug, ", %s", (char *) prefix);
1429    if (URI == NULL)
1430	fprintf(SAXdebug, ", NULL");
1431    else
1432	fprintf(SAXdebug, ", '%s'", (char *) URI);
1433    fprintf(SAXdebug, ", %d", nb_namespaces);
1434
1435    if (namespaces != NULL) {
1436        for (i = 0;i < nb_namespaces * 2;i++) {
1437	    fprintf(SAXdebug, ", xmlns");
1438	    if (namespaces[i] != NULL)
1439	        fprintf(SAXdebug, ":%s", namespaces[i]);
1440	    i++;
1441	    fprintf(SAXdebug, "='%s'", namespaces[i]);
1442	}
1443    }
1444    fprintf(SAXdebug, ", %d, %d", nb_attributes, nb_defaulted);
1445    if (attributes != NULL) {
1446        for (i = 0;i < nb_attributes * 5;i += 5) {
1447	    if (attributes[i + 1] != NULL)
1448		fprintf(SAXdebug, ", %s:%s='", attributes[i + 1], attributes[i]);
1449	    else
1450		fprintf(SAXdebug, ", %s='", attributes[i]);
1451	    fprintf(SAXdebug, "%.4s...', %d", attributes[i + 3],
1452		    (int)(attributes[i + 4] - attributes[i + 3]));
1453	}
1454    }
1455    fprintf(SAXdebug, ")\n");
1456}
1457
1458/**
1459 * endElementDebug:
1460 * @ctxt:  An XML parser context
1461 * @name:  The element name
1462 *
1463 * called when the end of an element has been detected.
1464 */
1465static void
1466endElementNsDebug(void *ctx ATTRIBUTE_UNUSED,
1467                  const xmlChar *localname,
1468                  const xmlChar *prefix,
1469                  const xmlChar *URI)
1470{
1471    callbacks++;
1472    if (quiet)
1473	return;
1474    fprintf(SAXdebug, "SAX.endElementNs(%s", (char *) localname);
1475    if (prefix == NULL)
1476	fprintf(SAXdebug, ", NULL");
1477    else
1478	fprintf(SAXdebug, ", %s", (char *) prefix);
1479    if (URI == NULL)
1480	fprintf(SAXdebug, ", NULL)\n");
1481    else
1482	fprintf(SAXdebug, ", '%s')\n", (char *) URI);
1483}
1484
1485static xmlSAXHandler debugSAX2HandlerStruct = {
1486    internalSubsetDebug,
1487    isStandaloneDebug,
1488    hasInternalSubsetDebug,
1489    hasExternalSubsetDebug,
1490    resolveEntityDebug,
1491    getEntityDebug,
1492    entityDeclDebug,
1493    notationDeclDebug,
1494    attributeDeclDebug,
1495    elementDeclDebug,
1496    unparsedEntityDeclDebug,
1497    setDocumentLocatorDebug,
1498    startDocumentDebug,
1499    endDocumentDebug,
1500    NULL,
1501    NULL,
1502    referenceDebug,
1503    charactersDebug,
1504    ignorableWhitespaceDebug,
1505    processingInstructionDebug,
1506    commentDebug,
1507    warningDebug,
1508    errorDebug,
1509    fatalErrorDebug,
1510    getParameterEntityDebug,
1511    cdataBlockDebug,
1512    externalSubsetDebug,
1513    XML_SAX2_MAGIC,
1514    NULL,
1515    startElementNsDebug,
1516    endElementNsDebug,
1517    NULL
1518};
1519
1520static xmlSAXHandlerPtr debugSAX2Handler = &debugSAX2HandlerStruct;
1521
1522#ifdef LIBXML_HTML_ENABLED
1523/**
1524 * htmlstartElementDebug:
1525 * @ctxt:  An XML parser context
1526 * @name:  The element name
1527 *
1528 * called when an opening tag has been processed.
1529 */
1530static void
1531htmlstartElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar **atts)
1532{
1533    int i;
1534
1535    fprintf(SAXdebug, "SAX.startElement(%s", (char *) name);
1536    if (atts != NULL) {
1537        for (i = 0;(atts[i] != NULL);i++) {
1538	    fprintf(SAXdebug, ", %s", atts[i++]);
1539	    if (atts[i] != NULL) {
1540		unsigned char output[40];
1541		const unsigned char *att = atts[i];
1542		int outlen, attlen;
1543	        fprintf(SAXdebug, "='");
1544		while ((attlen = strlen((char*)att)) > 0) {
1545		    outlen = sizeof output - 1;
1546		    htmlEncodeEntities(output, &outlen, att, &attlen, '\'');
1547		    output[outlen] = 0;
1548		    fprintf(SAXdebug, "%s", (char *) output);
1549		    att += attlen;
1550		}
1551		fprintf(SAXdebug, "'");
1552	    }
1553	}
1554    }
1555    fprintf(SAXdebug, ")\n");
1556}
1557
1558/**
1559 * htmlcharactersDebug:
1560 * @ctxt:  An XML parser context
1561 * @ch:  a xmlChar string
1562 * @len: the number of xmlChar
1563 *
1564 * receiving some chars from the parser.
1565 * Question: how much at a time ???
1566 */
1567static void
1568htmlcharactersDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
1569{
1570    unsigned char output[40];
1571    int inlen = len, outlen = 30;
1572
1573    htmlEncodeEntities(output, &outlen, ch, &inlen, 0);
1574    output[outlen] = 0;
1575
1576    fprintf(SAXdebug, "SAX.characters(%s, %d)\n", output, len);
1577}
1578
1579/**
1580 * htmlcdataDebug:
1581 * @ctxt:  An XML parser context
1582 * @ch:  a xmlChar string
1583 * @len: the number of xmlChar
1584 *
1585 * receiving some cdata chars from the parser.
1586 * Question: how much at a time ???
1587 */
1588static void
1589htmlcdataDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
1590{
1591    unsigned char output[40];
1592    int inlen = len, outlen = 30;
1593
1594    htmlEncodeEntities(output, &outlen, ch, &inlen, 0);
1595    output[outlen] = 0;
1596
1597    fprintf(SAXdebug, "SAX.cdata(%s, %d)\n", output, len);
1598}
1599
1600static xmlSAXHandler debugHTMLSAXHandlerStruct = {
1601    internalSubsetDebug,
1602    isStandaloneDebug,
1603    hasInternalSubsetDebug,
1604    hasExternalSubsetDebug,
1605    resolveEntityDebug,
1606    getEntityDebug,
1607    entityDeclDebug,
1608    notationDeclDebug,
1609    attributeDeclDebug,
1610    elementDeclDebug,
1611    unparsedEntityDeclDebug,
1612    setDocumentLocatorDebug,
1613    startDocumentDebug,
1614    endDocumentDebug,
1615    htmlstartElementDebug,
1616    endElementDebug,
1617    referenceDebug,
1618    htmlcharactersDebug,
1619    ignorableWhitespaceDebug,
1620    processingInstructionDebug,
1621    commentDebug,
1622    warningDebug,
1623    errorDebug,
1624    fatalErrorDebug,
1625    getParameterEntityDebug,
1626    htmlcdataDebug,
1627    externalSubsetDebug,
1628    1,
1629    NULL,
1630    NULL,
1631    NULL,
1632    NULL
1633};
1634
1635static xmlSAXHandlerPtr debugHTMLSAXHandler = &debugHTMLSAXHandlerStruct;
1636#endif /* LIBXML_HTML_ENABLED */
1637
1638#ifdef LIBXML_SAX1_ENABLED
1639/**
1640 * saxParseTest:
1641 * @filename: the file to parse
1642 * @result: the file with expected result
1643 * @err: the file with error messages
1644 *
1645 * Parse a file using the SAX API and check for errors.
1646 *
1647 * Returns 0 in case of success, an error code otherwise
1648 */
1649static int
1650saxParseTest(const char *filename, const char *result,
1651             const char *err ATTRIBUTE_UNUSED,
1652             int options) {
1653    int ret;
1654    char *temp;
1655
1656    nb_tests++;
1657    temp = resultFilename(filename, "", ".res");
1658    if (temp == NULL) {
1659        fprintf(stderr, "out of memory\n");
1660        fatalError();
1661    }
1662    SAXdebug = fopen(temp, "wb");
1663    if (SAXdebug == NULL) {
1664        fprintf(stderr, "Failed to write to %s\n", temp);
1665	free(temp);
1666	return(-1);
1667    }
1668
1669    /* for SAX we really want the callbacks though the context handlers */
1670    xmlSetStructuredErrorFunc(NULL, NULL);
1671    xmlSetGenericErrorFunc(NULL, testErrorHandler);
1672
1673#ifdef LIBXML_HTML_ENABLED
1674    if (options & XML_PARSE_HTML) {
1675	htmlSAXParseFile(filename, NULL, emptySAXHandler, NULL);
1676	ret = 0;
1677    } else
1678#endif
1679    ret = xmlSAXUserParseFile(emptySAXHandler, NULL, filename);
1680    if (ret == XML_WAR_UNDECLARED_ENTITY) {
1681        fprintf(SAXdebug, "xmlSAXUserParseFile returned error %d\n", ret);
1682        ret = 0;
1683    }
1684    if (ret != 0) {
1685        fprintf(stderr, "Failed to parse %s\n", filename);
1686	return(1);
1687    }
1688#ifdef LIBXML_HTML_ENABLED
1689    if (options & XML_PARSE_HTML) {
1690	htmlSAXParseFile(filename, NULL, debugHTMLSAXHandler, NULL);
1691	ret = 0;
1692    } else
1693#endif
1694    if (options & XML_PARSE_SAX1) {
1695	ret = xmlSAXUserParseFile(debugSAXHandler, NULL, filename);
1696    } else {
1697	ret = xmlSAXUserParseFile(debugSAX2Handler, NULL, filename);
1698    }
1699    if (ret == XML_WAR_UNDECLARED_ENTITY) {
1700        fprintf(SAXdebug, "xmlSAXUserParseFile returned error %d\n", ret);
1701        ret = 0;
1702    }
1703    fclose(SAXdebug);
1704    if (compareFiles(temp, result)) {
1705        fprintf(stderr, "Got a difference for %s\n", filename);
1706        ret = 1;
1707    } else
1708    unlink(temp);
1709    free(temp);
1710
1711    /* switch back to structured error handling */
1712    xmlSetGenericErrorFunc(NULL, NULL);
1713    xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
1714
1715    return(ret);
1716}
1717#endif
1718
1719/************************************************************************
1720 *									*
1721 *		Parse to tree based tests				*
1722 *									*
1723 ************************************************************************/
1724/**
1725 * oldParseTest:
1726 * @filename: the file to parse
1727 * @result: the file with expected result
1728 * @err: the file with error messages: unused
1729 *
1730 * Parse a file using the old xmlParseFile API, then serialize back
1731 * reparse the result and serialize again, then check for deviation
1732 * in serialization.
1733 *
1734 * Returns 0 in case of success, an error code otherwise
1735 */
1736static int
1737oldParseTest(const char *filename, const char *result,
1738             const char *err ATTRIBUTE_UNUSED,
1739	     int options ATTRIBUTE_UNUSED) {
1740    xmlDocPtr doc;
1741    char *temp;
1742    int res = 0;
1743
1744    nb_tests++;
1745    /*
1746     * base of the test, parse with the old API
1747     */
1748#ifdef LIBXML_SAX1_ENABLED
1749    doc = xmlParseFile(filename);
1750#else
1751    doc = xmlReadFile(filename, NULL, 0);
1752#endif
1753    if (doc == NULL)
1754        return(1);
1755    temp = resultFilename(filename, "", ".res");
1756    if (temp == NULL) {
1757        fprintf(stderr, "out of memory\n");
1758        fatalError();
1759    }
1760    xmlSaveFile(temp, doc);
1761    if (compareFiles(temp, result)) {
1762        res = 1;
1763    }
1764    xmlFreeDoc(doc);
1765
1766    /*
1767     * Parse the saved result to make sure the round trip is okay
1768     */
1769#ifdef LIBXML_SAX1_ENABLED
1770    doc = xmlParseFile(temp);
1771#else
1772    doc = xmlReadFile(temp, NULL, 0);
1773#endif
1774    if (doc == NULL)
1775        return(1);
1776    xmlSaveFile(temp, doc);
1777    if (compareFiles(temp, result)) {
1778        res = 1;
1779    }
1780    xmlFreeDoc(doc);
1781
1782    unlink(temp);
1783    free(temp);
1784    return(res);
1785}
1786
1787#ifdef LIBXML_PUSH_ENABLED
1788/**
1789 * pushParseTest:
1790 * @filename: the file to parse
1791 * @result: the file with expected result
1792 * @err: the file with error messages: unused
1793 *
1794 * Parse a file using the Push API, then serialize back
1795 * to check for content.
1796 *
1797 * Returns 0 in case of success, an error code otherwise
1798 */
1799static int
1800pushParseTest(const char *filename, const char *result,
1801             const char *err ATTRIBUTE_UNUSED,
1802	     int options) {
1803    xmlParserCtxtPtr ctxt;
1804    xmlDocPtr doc;
1805    const char *base;
1806    int size, res;
1807    int cur = 0;
1808
1809    nb_tests++;
1810    /*
1811     * load the document in memory and work from there.
1812     */
1813    if (loadMem(filename, &base, &size) != 0) {
1814        fprintf(stderr, "Failed to load %s\n", filename);
1815	return(-1);
1816    }
1817
1818#ifdef LIBXML_HTML_ENABLED
1819    if (options & XML_PARSE_HTML)
1820	ctxt = htmlCreatePushParserCtxt(NULL, NULL, base + cur, 4, filename,
1821	                                XML_CHAR_ENCODING_NONE);
1822    else
1823#endif
1824    ctxt = xmlCreatePushParserCtxt(NULL, NULL, base + cur, 4, filename);
1825    xmlCtxtUseOptions(ctxt, options);
1826    cur += 4;
1827    while (cur < size) {
1828        if (cur + 1024 >= size) {
1829#ifdef LIBXML_HTML_ENABLED
1830	    if (options & XML_PARSE_HTML)
1831		htmlParseChunk(ctxt, base + cur, size - cur, 1);
1832	    else
1833#endif
1834	    xmlParseChunk(ctxt, base + cur, size - cur, 1);
1835	    break;
1836	} else {
1837#ifdef LIBXML_HTML_ENABLED
1838	    if (options & XML_PARSE_HTML)
1839		htmlParseChunk(ctxt, base + cur, 1024, 0);
1840	    else
1841#endif
1842	    xmlParseChunk(ctxt, base + cur, 1024, 0);
1843	    cur += 1024;
1844	}
1845    }
1846    doc = ctxt->myDoc;
1847#ifdef LIBXML_HTML_ENABLED
1848    if (options & XML_PARSE_HTML)
1849        res = 1;
1850    else
1851#endif
1852    res = ctxt->wellFormed;
1853    xmlFreeParserCtxt(ctxt);
1854    free((char *)base);
1855    if (!res) {
1856	xmlFreeDoc(doc);
1857	fprintf(stderr, "Failed to parse %s\n", filename);
1858	return(-1);
1859    }
1860#ifdef LIBXML_HTML_ENABLED
1861    if (options & XML_PARSE_HTML)
1862	htmlDocDumpMemory(doc, (xmlChar **) &base, &size);
1863    else
1864#endif
1865    xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
1866    xmlFreeDoc(doc);
1867    res = compareFileMem(result, base, size);
1868    if ((base == NULL) || (res != 0)) {
1869	if (base != NULL)
1870	    xmlFree((char *)base);
1871        fprintf(stderr, "Result for %s failed\n", filename);
1872	return(-1);
1873    }
1874    xmlFree((char *)base);
1875    if (err != NULL) {
1876	res = compareFileMem(err, testErrors, testErrorsSize);
1877	if (res != 0) {
1878	    fprintf(stderr, "Error for %s failed\n", filename);
1879	    return(-1);
1880	}
1881    }
1882    return(0);
1883}
1884#endif
1885
1886/**
1887 * memParseTest:
1888 * @filename: the file to parse
1889 * @result: the file with expected result
1890 * @err: the file with error messages: unused
1891 *
1892 * Parse a file using the old xmlReadMemory API, then serialize back
1893 * reparse the result and serialize again, then check for deviation
1894 * in serialization.
1895 *
1896 * Returns 0 in case of success, an error code otherwise
1897 */
1898static int
1899memParseTest(const char *filename, const char *result,
1900             const char *err ATTRIBUTE_UNUSED,
1901	     int options ATTRIBUTE_UNUSED) {
1902    xmlDocPtr doc;
1903    const char *base;
1904    int size, res;
1905
1906    nb_tests++;
1907    /*
1908     * load and parse the memory
1909     */
1910    if (loadMem(filename, &base, &size) != 0) {
1911        fprintf(stderr, "Failed to load %s\n", filename);
1912	return(-1);
1913    }
1914
1915    doc = xmlReadMemory(base, size, filename, NULL, 0);
1916    unloadMem(base);
1917    if (doc == NULL) {
1918        return(1);
1919    }
1920    xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
1921    xmlFreeDoc(doc);
1922    res = compareFileMem(result, base, size);
1923    if ((base == NULL) || (res != 0)) {
1924	if (base != NULL)
1925	    xmlFree((char *)base);
1926        fprintf(stderr, "Result for %s failed\n", filename);
1927	return(-1);
1928    }
1929    xmlFree((char *)base);
1930    return(0);
1931}
1932
1933/**
1934 * noentParseTest:
1935 * @filename: the file to parse
1936 * @result: the file with expected result
1937 * @err: the file with error messages: unused
1938 *
1939 * Parse a file with entity resolution, then serialize back
1940 * reparse the result and serialize again, then check for deviation
1941 * in serialization.
1942 *
1943 * Returns 0 in case of success, an error code otherwise
1944 */
1945static int
1946noentParseTest(const char *filename, const char *result,
1947               const char *err  ATTRIBUTE_UNUSED,
1948	       int options) {
1949    xmlDocPtr doc;
1950    char *temp;
1951    int res = 0;
1952
1953    nb_tests++;
1954    /*
1955     * base of the test, parse with the old API
1956     */
1957    doc = xmlReadFile(filename, NULL, options);
1958    if (doc == NULL)
1959        return(1);
1960    temp = resultFilename(filename, "", ".res");
1961    if (temp == NULL) {
1962        fprintf(stderr, "Out of memory\n");
1963        fatalError();
1964    }
1965    xmlSaveFile(temp, doc);
1966    if (compareFiles(temp, result)) {
1967        res = 1;
1968    }
1969    xmlFreeDoc(doc);
1970
1971    /*
1972     * Parse the saved result to make sure the round trip is okay
1973     */
1974    doc = xmlReadFile(filename, NULL, options);
1975    if (doc == NULL)
1976        return(1);
1977    xmlSaveFile(temp, doc);
1978    if (compareFiles(temp, result)) {
1979        res = 1;
1980    }
1981    xmlFreeDoc(doc);
1982
1983    unlink(temp);
1984    free(temp);
1985    return(res);
1986}
1987
1988/**
1989 * errParseTest:
1990 * @filename: the file to parse
1991 * @result: the file with expected result
1992 * @err: the file with error messages
1993 *
1994 * Parse a file using the xmlReadFile API and check for errors.
1995 *
1996 * Returns 0 in case of success, an error code otherwise
1997 */
1998static int
1999errParseTest(const char *filename, const char *result, const char *err,
2000             int options) {
2001    xmlDocPtr doc;
2002    const char *base = NULL;
2003    int size, res = 0;
2004
2005    nb_tests++;
2006#ifdef LIBXML_HTML_ENABLED
2007    if (options & XML_PARSE_HTML) {
2008        doc = htmlReadFile(filename, NULL, options);
2009    } else
2010#endif
2011#ifdef LIBXML_XINCLUDE_ENABLED
2012    if (options & XML_PARSE_XINCLUDE) {
2013	doc = xmlReadFile(filename, NULL, options);
2014	xmlXIncludeProcessFlags(doc, options);
2015    } else
2016#endif
2017    {
2018	xmlGetWarningsDefaultValue = 1;
2019	doc = xmlReadFile(filename, NULL, options);
2020    }
2021    xmlGetWarningsDefaultValue = 0;
2022    if (result) {
2023	if (doc == NULL) {
2024	    base = "";
2025	    size = 0;
2026	} else {
2027#ifdef LIBXML_HTML_ENABLED
2028	    if (options & XML_PARSE_HTML) {
2029		htmlDocDumpMemory(doc, (xmlChar **) &base, &size);
2030	    } else
2031#endif
2032	    xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
2033	}
2034	res = compareFileMem(result, base, size);
2035    }
2036    if (doc != NULL) {
2037	if (base != NULL)
2038	    xmlFree((char *)base);
2039	xmlFreeDoc(doc);
2040    }
2041    if (res != 0) {
2042        fprintf(stderr, "Result for %s failed\n", filename);
2043	return(-1);
2044    }
2045    if (err != NULL) {
2046	res = compareFileMem(err, testErrors, testErrorsSize);
2047	if (res != 0) {
2048	    fprintf(stderr, "Error for %s failed\n", filename);
2049	    return(-1);
2050	}
2051    } else if (options & XML_PARSE_DTDVALID) {
2052        if (testErrorsSize != 0)
2053	    fprintf(stderr, "Validation for %s failed\n", filename);
2054    }
2055
2056    return(0);
2057}
2058
2059#ifdef LIBXML_READER_ENABLED
2060/************************************************************************
2061 *									*
2062 *		Reader based tests					*
2063 *									*
2064 ************************************************************************/
2065
2066static void processNode(FILE *out, xmlTextReaderPtr reader) {
2067    const xmlChar *name, *value;
2068    int type, empty;
2069
2070    type = xmlTextReaderNodeType(reader);
2071    empty = xmlTextReaderIsEmptyElement(reader);
2072
2073    name = xmlTextReaderConstName(reader);
2074    if (name == NULL)
2075	name = BAD_CAST "--";
2076
2077    value = xmlTextReaderConstValue(reader);
2078
2079
2080    fprintf(out, "%d %d %s %d %d",
2081	    xmlTextReaderDepth(reader),
2082	    type,
2083	    name,
2084	    empty,
2085	    xmlTextReaderHasValue(reader));
2086    if (value == NULL)
2087	fprintf(out, "\n");
2088    else {
2089	fprintf(out, " %s\n", value);
2090    }
2091}
2092static int
2093streamProcessTest(const char *filename, const char *result, const char *err,
2094                  xmlTextReaderPtr reader, const char *rng) {
2095    int ret;
2096    char *temp = NULL;
2097    FILE *t = NULL;
2098
2099    if (reader == NULL)
2100        return(-1);
2101
2102    nb_tests++;
2103    if (result != NULL) {
2104	temp = resultFilename(filename, "", ".res");
2105	if (temp == NULL) {
2106	    fprintf(stderr, "Out of memory\n");
2107	    fatalError();
2108	}
2109	t = fopen(temp, "wb");
2110	if (t == NULL) {
2111	    fprintf(stderr, "Can't open temp file %s\n", temp);
2112	    free(temp);
2113	    return(-1);
2114	}
2115    }
2116#ifdef LIBXML_SCHEMAS_ENABLED
2117    if (rng != NULL) {
2118	ret = xmlTextReaderRelaxNGValidate(reader, rng);
2119	if (ret < 0) {
2120	    testErrorHandler(NULL, "Relax-NG schema %s failed to compile\n",
2121	                     rng);
2122	    fclose(t);
2123	    unlink(temp);
2124	    free(temp);
2125	    return(0);
2126	}
2127    }
2128#endif
2129    xmlGetWarningsDefaultValue = 1;
2130    ret = xmlTextReaderRead(reader);
2131    while (ret == 1) {
2132	if ((t != NULL) && (rng == NULL))
2133	    processNode(t, reader);
2134        ret = xmlTextReaderRead(reader);
2135    }
2136    if (ret != 0) {
2137        testErrorHandler(NULL, "%s : failed to parse\n", filename);
2138    }
2139    if (rng != NULL) {
2140        if (xmlTextReaderIsValid(reader) != 1) {
2141	    testErrorHandler(NULL, "%s fails to validate\n", filename);
2142	} else {
2143	    testErrorHandler(NULL, "%s validates\n", filename);
2144	}
2145    }
2146    xmlGetWarningsDefaultValue = 0;
2147    if (t != NULL) {
2148        fclose(t);
2149	ret = compareFiles(temp, result);
2150	unlink(temp);
2151	free(temp);
2152	if (ret) {
2153	    fprintf(stderr, "Result for %s failed\n", filename);
2154	    return(-1);
2155	}
2156    }
2157    if (err != NULL) {
2158	ret = compareFileMem(err, testErrors, testErrorsSize);
2159	if (ret != 0) {
2160	    fprintf(stderr, "Error for %s failed\n", filename);
2161	    printf("%s", testErrors);
2162	    return(-1);
2163	}
2164    }
2165
2166    return(0);
2167}
2168
2169/**
2170 * streamParseTest:
2171 * @filename: the file to parse
2172 * @result: the file with expected result
2173 * @err: the file with error messages
2174 *
2175 * Parse a file using the reader API and check for errors.
2176 *
2177 * Returns 0 in case of success, an error code otherwise
2178 */
2179static int
2180streamParseTest(const char *filename, const char *result, const char *err,
2181                int options) {
2182    xmlTextReaderPtr reader;
2183    int ret;
2184
2185    reader = xmlReaderForFile(filename, NULL, options);
2186    ret = streamProcessTest(filename, result, err, reader, NULL);
2187    xmlFreeTextReader(reader);
2188    return(ret);
2189}
2190
2191/**
2192 * walkerParseTest:
2193 * @filename: the file to parse
2194 * @result: the file with expected result
2195 * @err: the file with error messages
2196 *
2197 * Parse a file using the walker, i.e. a reader built from a atree.
2198 *
2199 * Returns 0 in case of success, an error code otherwise
2200 */
2201static int
2202walkerParseTest(const char *filename, const char *result, const char *err,
2203                int options) {
2204    xmlDocPtr doc;
2205    xmlTextReaderPtr reader;
2206    int ret;
2207
2208    doc = xmlReadFile(filename, NULL, options);
2209    if (doc == NULL) {
2210        fprintf(stderr, "Failed to parse %s\n", filename);
2211	return(-1);
2212    }
2213    reader = xmlReaderWalker(doc);
2214    ret = streamProcessTest(filename, result, err, reader, NULL);
2215    xmlFreeTextReader(reader);
2216    xmlFreeDoc(doc);
2217    return(ret);
2218}
2219
2220/**
2221 * streamMemParseTest:
2222 * @filename: the file to parse
2223 * @result: the file with expected result
2224 * @err: the file with error messages
2225 *
2226 * Parse a file using the reader API from memory and check for errors.
2227 *
2228 * Returns 0 in case of success, an error code otherwise
2229 */
2230static int
2231streamMemParseTest(const char *filename, const char *result, const char *err,
2232                   int options) {
2233    xmlTextReaderPtr reader;
2234    int ret;
2235    const char *base;
2236    int size;
2237
2238    /*
2239     * load and parse the memory
2240     */
2241    if (loadMem(filename, &base, &size) != 0) {
2242        fprintf(stderr, "Failed to load %s\n", filename);
2243	return(-1);
2244    }
2245    reader = xmlReaderForMemory(base, size, filename, NULL, options);
2246    ret = streamProcessTest(filename, result, err, reader, NULL);
2247    free((char *)base);
2248    xmlFreeTextReader(reader);
2249    return(ret);
2250}
2251#endif
2252
2253#ifdef LIBXML_XPATH_ENABLED
2254#ifdef LIBXML_DEBUG_ENABLED
2255/************************************************************************
2256 *									*
2257 *		XPath and XPointer based tests				*
2258 *									*
2259 ************************************************************************/
2260
2261static FILE *xpathOutput;
2262static xmlDocPtr xpathDocument;
2263
2264static void
2265testXPath(const char *str, int xptr, int expr) {
2266    xmlXPathObjectPtr res;
2267    xmlXPathContextPtr ctxt;
2268
2269    nb_tests++;
2270#if defined(LIBXML_XPTR_ENABLED)
2271    if (xptr) {
2272	ctxt = xmlXPtrNewContext(xpathDocument, NULL, NULL);
2273	res = xmlXPtrEval(BAD_CAST str, ctxt);
2274    } else {
2275#endif
2276	ctxt = xmlXPathNewContext(xpathDocument);
2277	ctxt->node = xmlDocGetRootElement(xpathDocument);
2278	if (expr)
2279	    res = xmlXPathEvalExpression(BAD_CAST str, ctxt);
2280	else {
2281	    /* res = xmlXPathEval(BAD_CAST str, ctxt); */
2282	    xmlXPathCompExprPtr comp;
2283
2284	    comp = xmlXPathCompile(BAD_CAST str);
2285	    if (comp != NULL) {
2286		res = xmlXPathCompiledEval(comp, ctxt);
2287		xmlXPathFreeCompExpr(comp);
2288	    } else
2289		res = NULL;
2290	}
2291#if defined(LIBXML_XPTR_ENABLED)
2292    }
2293#endif
2294    xmlXPathDebugDumpObject(xpathOutput, res, 0);
2295    xmlXPathFreeObject(res);
2296    xmlXPathFreeContext(ctxt);
2297}
2298
2299/**
2300 * xpathExprTest:
2301 * @filename: the file to parse
2302 * @result: the file with expected result
2303 * @err: the file with error messages
2304 *
2305 * Parse a file containing XPath standalone expressions and evaluate them
2306 *
2307 * Returns 0 in case of success, an error code otherwise
2308 */
2309static int
2310xpathCommonTest(const char *filename, const char *result,
2311                int xptr, int expr) {
2312    FILE *input;
2313    char expression[5000];
2314    int len, ret = 0;
2315    char *temp;
2316
2317    temp = resultFilename(filename, "", ".res");
2318    if (temp == NULL) {
2319        fprintf(stderr, "Out of memory\n");
2320        fatalError();
2321    }
2322    xpathOutput = fopen(temp, "wb");
2323    if (xpathOutput == NULL) {
2324	fprintf(stderr, "failed to open output file %s\n", temp);
2325        free(temp);
2326	return(-1);
2327    }
2328
2329    input = fopen(filename, "rb");
2330    if (input == NULL) {
2331        xmlGenericError(xmlGenericErrorContext,
2332		"Cannot open %s for reading\n", filename);
2333        free(temp);
2334	return(-1);
2335    }
2336    while (fgets(expression, 4500, input) != NULL) {
2337	len = strlen(expression);
2338	len--;
2339	while ((len >= 0) &&
2340	       ((expression[len] == '\n') || (expression[len] == '\t') ||
2341		(expression[len] == '\r') || (expression[len] == ' '))) len--;
2342	expression[len + 1] = 0;
2343	if (len >= 0) {
2344	    fprintf(xpathOutput,
2345	            "\n========================\nExpression: %s\n",
2346		    expression) ;
2347	    testXPath(expression, xptr, expr);
2348	}
2349    }
2350
2351    fclose(input);
2352    fclose(xpathOutput);
2353    if (result != NULL) {
2354	ret = compareFiles(temp, result);
2355	if (ret) {
2356	    fprintf(stderr, "Result for %s failed\n", filename);
2357	}
2358    }
2359
2360    unlink(temp);
2361    free(temp);
2362    return(ret);
2363}
2364
2365/**
2366 * xpathExprTest:
2367 * @filename: the file to parse
2368 * @result: the file with expected result
2369 * @err: the file with error messages
2370 *
2371 * Parse a file containing XPath standalone expressions and evaluate them
2372 *
2373 * Returns 0 in case of success, an error code otherwise
2374 */
2375static int
2376xpathExprTest(const char *filename, const char *result,
2377              const char *err ATTRIBUTE_UNUSED,
2378              int options ATTRIBUTE_UNUSED) {
2379    return(xpathCommonTest(filename, result, 0, 1));
2380}
2381
2382/**
2383 * xpathDocTest:
2384 * @filename: the file to parse
2385 * @result: the file with expected result
2386 * @err: the file with error messages
2387 *
2388 * Parse a file containing XPath expressions and evaluate them against
2389 * a set of corresponding documents.
2390 *
2391 * Returns 0 in case of success, an error code otherwise
2392 */
2393static int
2394xpathDocTest(const char *filename,
2395             const char *resul ATTRIBUTE_UNUSED,
2396             const char *err ATTRIBUTE_UNUSED,
2397             int options) {
2398
2399    char pattern[500];
2400    char result[500];
2401    glob_t globbuf;
2402    size_t i;
2403    int ret = 0, res;
2404
2405    xpathDocument = xmlReadFile(filename, NULL,
2406                                options | XML_PARSE_DTDATTR | XML_PARSE_NOENT);
2407    if (xpathDocument == NULL) {
2408        fprintf(stderr, "Failed to load %s\n", filename);
2409	return(-1);
2410    }
2411
2412    snprintf(pattern, 499, "./test/XPath/tests/%s*", baseFilename(filename));
2413    pattern[499] = 0;
2414    globbuf.gl_offs = 0;
2415    glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
2416    for (i = 0;i < globbuf.gl_pathc;i++) {
2417        snprintf(result, 499, "result/XPath/tests/%s",
2418	         baseFilename(globbuf.gl_pathv[i]));
2419	res = xpathCommonTest(globbuf.gl_pathv[i], &result[0], 0, 0);
2420	if (res != 0)
2421	    ret = res;
2422    }
2423    globfree(&globbuf);
2424
2425    xmlFreeDoc(xpathDocument);
2426    return(ret);
2427}
2428
2429#ifdef LIBXML_XPTR_ENABLED
2430/**
2431 * xptrDocTest:
2432 * @filename: the file to parse
2433 * @result: the file with expected result
2434 * @err: the file with error messages
2435 *
2436 * Parse a file containing XPath expressions and evaluate them against
2437 * a set of corresponding documents.
2438 *
2439 * Returns 0 in case of success, an error code otherwise
2440 */
2441static int
2442xptrDocTest(const char *filename,
2443            const char *resul ATTRIBUTE_UNUSED,
2444            const char *err ATTRIBUTE_UNUSED,
2445            int options) {
2446
2447    char pattern[500];
2448    char result[500];
2449    glob_t globbuf;
2450    size_t i;
2451    int ret = 0, res;
2452
2453    xpathDocument = xmlReadFile(filename, NULL,
2454                                options | XML_PARSE_DTDATTR | XML_PARSE_NOENT);
2455    if (xpathDocument == NULL) {
2456        fprintf(stderr, "Failed to load %s\n", filename);
2457	return(-1);
2458    }
2459
2460    snprintf(pattern, 499, "./test/XPath/xptr/%s*", baseFilename(filename));
2461    pattern[499] = 0;
2462    globbuf.gl_offs = 0;
2463    glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
2464    for (i = 0;i < globbuf.gl_pathc;i++) {
2465        snprintf(result, 499, "result/XPath/xptr/%s",
2466	         baseFilename(globbuf.gl_pathv[i]));
2467	res = xpathCommonTest(globbuf.gl_pathv[i], &result[0], 1, 0);
2468	if (res != 0)
2469	    ret = res;
2470    }
2471    globfree(&globbuf);
2472
2473    xmlFreeDoc(xpathDocument);
2474    return(ret);
2475}
2476#endif /* LIBXML_XPTR_ENABLED */
2477
2478/**
2479 * xmlidDocTest:
2480 * @filename: the file to parse
2481 * @result: the file with expected result
2482 * @err: the file with error messages
2483 *
2484 * Parse a file containing xml:id and check for errors and verify
2485 * that XPath queries will work on them as expected.
2486 *
2487 * Returns 0 in case of success, an error code otherwise
2488 */
2489static int
2490xmlidDocTest(const char *filename,
2491             const char *result,
2492             const char *err,
2493             int options) {
2494
2495    int res = 0;
2496    int ret = 0;
2497    char *temp;
2498
2499    xpathDocument = xmlReadFile(filename, NULL,
2500                                options | XML_PARSE_DTDATTR | XML_PARSE_NOENT);
2501    if (xpathDocument == NULL) {
2502        fprintf(stderr, "Failed to load %s\n", filename);
2503	return(-1);
2504    }
2505
2506    temp = resultFilename(filename, "", ".res");
2507    if (temp == NULL) {
2508        fprintf(stderr, "Out of memory\n");
2509        fatalError();
2510    }
2511    xpathOutput = fopen(temp, "wb");
2512    if (xpathOutput == NULL) {
2513	fprintf(stderr, "failed to open output file %s\n", temp);
2514        xmlFreeDoc(xpathDocument);
2515        free(temp);
2516	return(-1);
2517    }
2518
2519    testXPath("id('bar')", 0, 0);
2520
2521    fclose(xpathOutput);
2522    if (result != NULL) {
2523	ret = compareFiles(temp, result);
2524	if (ret) {
2525	    fprintf(stderr, "Result for %s failed\n", filename);
2526	    res = 1;
2527	}
2528    }
2529
2530    unlink(temp);
2531    free(temp);
2532    xmlFreeDoc(xpathDocument);
2533
2534    if (err != NULL) {
2535	ret = compareFileMem(err, testErrors, testErrorsSize);
2536	if (ret != 0) {
2537	    fprintf(stderr, "Error for %s failed\n", filename);
2538	    res = 1;
2539	}
2540    }
2541    return(res);
2542}
2543
2544#endif /* LIBXML_DEBUG_ENABLED */
2545#endif /* XPATH */
2546/************************************************************************
2547 *									*
2548 *			URI based tests					*
2549 *									*
2550 ************************************************************************/
2551
2552static void
2553handleURI(const char *str, const char *base, FILE *o) {
2554    int ret;
2555    xmlURIPtr uri;
2556    xmlChar *res = NULL;
2557
2558    uri = xmlCreateURI();
2559
2560    if (base == NULL) {
2561	ret = xmlParseURIReference(uri, str);
2562	if (ret != 0)
2563	    fprintf(o, "%s : error %d\n", str, ret);
2564	else {
2565	    xmlNormalizeURIPath(uri->path);
2566	    xmlPrintURI(o, uri);
2567	    fprintf(o, "\n");
2568	}
2569    } else {
2570	res = xmlBuildURI((xmlChar *)str, (xmlChar *) base);
2571	if (res != NULL) {
2572	    fprintf(o, "%s\n", (char *) res);
2573	}
2574	else
2575	    fprintf(o, "::ERROR::\n");
2576    }
2577    if (res != NULL)
2578	xmlFree(res);
2579    xmlFreeURI(uri);
2580}
2581
2582/**
2583 * uriCommonTest:
2584 * @filename: the file to parse
2585 * @result: the file with expected result
2586 * @err: the file with error messages
2587 *
2588 * Parse a file containing URI and check for errors
2589 *
2590 * Returns 0 in case of success, an error code otherwise
2591 */
2592static int
2593uriCommonTest(const char *filename,
2594             const char *result,
2595             const char *err,
2596             const char *base) {
2597    char *temp;
2598    FILE *o, *f;
2599    char str[1024];
2600    int res = 0, i, ret;
2601
2602    temp = resultFilename(filename, "", ".res");
2603    if (temp == NULL) {
2604        fprintf(stderr, "Out of memory\n");
2605        fatalError();
2606    }
2607    o = fopen(temp, "wb");
2608    if (o == NULL) {
2609	fprintf(stderr, "failed to open output file %s\n", temp);
2610        free(temp);
2611	return(-1);
2612    }
2613    f = fopen(filename, "rb");
2614    if (f == NULL) {
2615	fprintf(stderr, "failed to open input file %s\n", filename);
2616	fclose(o);
2617	unlink(temp);
2618        free(temp);
2619	return(-1);
2620    }
2621
2622    while (1) {
2623	/*
2624	 * read one line in string buffer.
2625	 */
2626	if (fgets (&str[0], sizeof (str) - 1, f) == NULL)
2627	   break;
2628
2629	/*
2630	 * remove the ending spaces
2631	 */
2632	i = strlen(str);
2633	while ((i > 0) &&
2634	       ((str[i - 1] == '\n') || (str[i - 1] == '\r') ||
2635		(str[i - 1] == ' ') || (str[i - 1] == '\t'))) {
2636	    i--;
2637	    str[i] = 0;
2638	}
2639	nb_tests++;
2640	handleURI(str, base, o);
2641    }
2642
2643    fclose(f);
2644    fclose(o);
2645
2646    if (result != NULL) {
2647	ret = compareFiles(temp, result);
2648	if (ret) {
2649	    fprintf(stderr, "Result for %s failed\n", filename);
2650	    res = 1;
2651	}
2652    }
2653    if (err != NULL) {
2654	ret = compareFileMem(err, testErrors, testErrorsSize);
2655	if (ret != 0) {
2656	    fprintf(stderr, "Error for %s failed\n", filename);
2657	    res = 1;
2658	}
2659    }
2660
2661    unlink(temp);
2662    free(temp);
2663    return(res);
2664}
2665
2666/**
2667 * uriParseTest:
2668 * @filename: the file to parse
2669 * @result: the file with expected result
2670 * @err: the file with error messages
2671 *
2672 * Parse a file containing URI and check for errors
2673 *
2674 * Returns 0 in case of success, an error code otherwise
2675 */
2676static int
2677uriParseTest(const char *filename,
2678             const char *result,
2679             const char *err,
2680             int options ATTRIBUTE_UNUSED) {
2681    return(uriCommonTest(filename, result, err, NULL));
2682}
2683
2684/**
2685 * uriBaseTest:
2686 * @filename: the file to parse
2687 * @result: the file with expected result
2688 * @err: the file with error messages
2689 *
2690 * Parse a file containing URI, compose them against a fixed base and
2691 * check for errors
2692 *
2693 * Returns 0 in case of success, an error code otherwise
2694 */
2695static int
2696uriBaseTest(const char *filename,
2697             const char *result,
2698             const char *err,
2699             int options ATTRIBUTE_UNUSED) {
2700    return(uriCommonTest(filename, result, err,
2701                         "http://foo.com/path/to/index.html?orig#help"));
2702}
2703
2704static int urip_success = 1;
2705static int urip_current = 0;
2706static const char *urip_testURLs[] = {
2707    "urip://example.com/a b.html",
2708    "urip://example.com/a%20b.html",
2709    "file:///path/to/a b.html",
2710    "file:///path/to/a%20b.html",
2711    "/path/to/a b.html",
2712    "/path/to/a%20b.html",
2713    "urip://example.com/r�sum�.html",
2714    "urip://example.com/test?a=1&b=2%263&c=4#foo",
2715    NULL
2716};
2717static const char *urip_rcvsURLs[] = {
2718    /* it is an URI the strings must be escaped */
2719    "urip://example.com/a%20b.html",
2720    /* check that % escaping is not broken */
2721    "urip://example.com/a%20b.html",
2722    /* it's an URI path the strings must be escaped */
2723    "file:///path/to/a%20b.html",
2724    /* check that % escaping is not broken */
2725    "file:///path/to/a%20b.html",
2726    /* this is not an URI, this is a path, so this should not be escaped */
2727    "/path/to/a b.html",
2728    /* check that paths with % are not broken */
2729    "/path/to/a%20b.html",
2730    /* out of context the encoding can't be guessed byte by byte conversion */
2731    "urip://example.com/r%E9sum%E9.html",
2732    /* verify we don't destroy URIs especially the query part */
2733    "urip://example.com/test?a=1&b=2%263&c=4#foo",
2734    NULL
2735};
2736static const char *urip_res = "<list/>";
2737static const char *urip_cur = NULL;
2738static int urip_rlen;
2739
2740/**
2741 * uripMatch:
2742 * @URI: an URI to test
2743 *
2744 * Check for an urip: query
2745 *
2746 * Returns 1 if yes and 0 if another Input module should be used
2747 */
2748static int
2749uripMatch(const char * URI) {
2750    if ((URI == NULL) || (!strcmp(URI, "file:///etc/xml/catalog")))
2751        return(0);
2752    /* Verify we received the escaped URL */
2753    if (strcmp(urip_rcvsURLs[urip_current], URI))
2754	urip_success = 0;
2755    return(1);
2756}
2757
2758/**
2759 * uripOpen:
2760 * @URI: an URI to test
2761 *
2762 * Return a pointer to the urip: query handler, in this example simply
2763 * the urip_current pointer...
2764 *
2765 * Returns an Input context or NULL in case or error
2766 */
2767static void *
2768uripOpen(const char * URI) {
2769    if ((URI == NULL) || (!strcmp(URI, "file:///etc/xml/catalog")))
2770        return(NULL);
2771    /* Verify we received the escaped URL */
2772    if (strcmp(urip_rcvsURLs[urip_current], URI))
2773	urip_success = 0;
2774    urip_cur = urip_res;
2775    urip_rlen = strlen(urip_res);
2776    return((void *) urip_cur);
2777}
2778
2779/**
2780 * uripClose:
2781 * @context: the read context
2782 *
2783 * Close the urip: query handler
2784 *
2785 * Returns 0 or -1 in case of error
2786 */
2787static int
2788uripClose(void * context) {
2789    if (context == NULL) return(-1);
2790    urip_cur = NULL;
2791    urip_rlen = 0;
2792    return(0);
2793}
2794
2795/**
2796 * uripRead:
2797 * @context: the read context
2798 * @buffer: where to store data
2799 * @len: number of bytes to read
2800 *
2801 * Implement an urip: query read.
2802 *
2803 * Returns the number of bytes read or -1 in case of error
2804 */
2805static int
2806uripRead(void * context, char * buffer, int len) {
2807   const char *ptr = (const char *) context;
2808
2809   if ((context == NULL) || (buffer == NULL) || (len < 0))
2810       return(-1);
2811
2812   if (len > urip_rlen) len = urip_rlen;
2813   memcpy(buffer, ptr, len);
2814   urip_rlen -= len;
2815   return(len);
2816}
2817
2818static int
2819urip_checkURL(const char *URL) {
2820    xmlDocPtr doc;
2821
2822    doc = xmlReadFile(URL, NULL, 0);
2823    if (doc == NULL)
2824        return(-1);
2825    xmlFreeDoc(doc);
2826    return(1);
2827}
2828
2829/**
2830 * uriPathTest:
2831 * @filename: ignored
2832 * @result: ignored
2833 * @err: ignored
2834 *
2835 * Run a set of tests to check how Path and URI are handled before
2836 * being passed to the I/O layer
2837 *
2838 * Returns 0 in case of success, an error code otherwise
2839 */
2840static int
2841uriPathTest(const char *filename ATTRIBUTE_UNUSED,
2842             const char *result ATTRIBUTE_UNUSED,
2843             const char *err ATTRIBUTE_UNUSED,
2844             int options ATTRIBUTE_UNUSED) {
2845    int parsed;
2846    int failures = 0;
2847
2848    /*
2849     * register the new I/O handlers
2850     */
2851    if (xmlRegisterInputCallbacks(uripMatch, uripOpen, uripRead, uripClose) < 0)
2852    {
2853        fprintf(stderr, "failed to register HTTP handler\n");
2854	return(-1);
2855    }
2856
2857    for (urip_current = 0;urip_testURLs[urip_current] != NULL;urip_current++) {
2858        urip_success = 1;
2859        parsed = urip_checkURL(urip_testURLs[urip_current]);
2860	if (urip_success != 1) {
2861	    fprintf(stderr, "failed the URL passing test for %s",
2862	            urip_testURLs[urip_current]);
2863	    failures++;
2864	} else if (parsed != 1) {
2865	    fprintf(stderr, "failed the parsing test for %s",
2866	            urip_testURLs[urip_current]);
2867	    failures++;
2868	}
2869	nb_tests++;
2870    }
2871
2872    xmlPopInputCallbacks();
2873    return(failures);
2874}
2875
2876#ifdef LIBXML_SCHEMAS_ENABLED
2877/************************************************************************
2878 *									*
2879 *			Schemas tests					*
2880 *									*
2881 ************************************************************************/
2882static int
2883schemasOneTest(const char *sch,
2884               const char *filename,
2885               const char *result,
2886	       const char *err,
2887	       int options,
2888	       xmlSchemaPtr schemas) {
2889    xmlDocPtr doc;
2890    xmlSchemaValidCtxtPtr ctxt;
2891    int ret = 0;
2892    int validResult = 0;
2893    char *temp;
2894    FILE *schemasOutput;
2895
2896    doc = xmlReadFile(filename, NULL, options);
2897    if (doc == NULL) {
2898        fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch);
2899	return(-1);
2900    }
2901
2902    temp = resultFilename(result, "", ".res");
2903    if (temp == NULL) {
2904        fprintf(stderr, "Out of memory\n");
2905        fatalError();
2906    }
2907    schemasOutput = fopen(temp, "wb");
2908    if (schemasOutput == NULL) {
2909	fprintf(stderr, "failed to open output file %s\n", temp);
2910	xmlFreeDoc(doc);
2911        free(temp);
2912	return(-1);
2913    }
2914
2915    ctxt = xmlSchemaNewValidCtxt(schemas);
2916    xmlSchemaSetValidErrors(ctxt,
2917         (xmlSchemaValidityErrorFunc) testErrorHandler,
2918         (xmlSchemaValidityWarningFunc) testErrorHandler,
2919	 ctxt);
2920    validResult = xmlSchemaValidateDoc(ctxt, doc);
2921    if (validResult == 0) {
2922	fprintf(schemasOutput, "%s validates\n", filename);
2923    } else if (validResult > 0) {
2924	fprintf(schemasOutput, "%s fails to validate\n", filename);
2925    } else {
2926	fprintf(schemasOutput, "%s validation generated an internal error\n",
2927	       filename);
2928    }
2929    fclose(schemasOutput);
2930    if (result) {
2931	if (compareFiles(temp, result)) {
2932	    fprintf(stderr, "Result for %s on %s failed\n", filename, sch);
2933	    ret = 1;
2934	}
2935    }
2936    unlink(temp);
2937    free(temp);
2938
2939    if ((validResult != 0) && (err != NULL)) {
2940	if (compareFileMem(err, testErrors, testErrorsSize)) {
2941	    fprintf(stderr, "Error for %s on %s failed\n", filename, sch);
2942	    ret = 1;
2943	}
2944    }
2945
2946    xmlSchemaFreeValidCtxt(ctxt);
2947    xmlFreeDoc(doc);
2948    return(ret);
2949}
2950/**
2951 * schemasTest:
2952 * @filename: the schemas file
2953 * @result: the file with expected result
2954 * @err: the file with error messages
2955 *
2956 * Parse a file containing URI, compose them against a fixed base and
2957 * check for errors
2958 *
2959 * Returns 0 in case of success, an error code otherwise
2960 */
2961static int
2962schemasTest(const char *filename,
2963            const char *resul ATTRIBUTE_UNUSED,
2964            const char *errr ATTRIBUTE_UNUSED,
2965            int options) {
2966    const char *base = baseFilename(filename);
2967    const char *base2;
2968    const char *instance;
2969    xmlSchemaParserCtxtPtr ctxt;
2970    xmlSchemaPtr schemas;
2971    int res = 0, len, ret;
2972    char pattern[500];
2973    char prefix[500];
2974    char result[500];
2975    char err[500];
2976    glob_t globbuf;
2977    size_t i;
2978    char count = 0;
2979
2980    /* first compile the schemas if possible */
2981    ctxt = xmlSchemaNewParserCtxt(filename);
2982    xmlSchemaSetParserErrors(ctxt,
2983         (xmlSchemaValidityErrorFunc) testErrorHandler,
2984         (xmlSchemaValidityWarningFunc) testErrorHandler,
2985	 ctxt);
2986    schemas = xmlSchemaParse(ctxt);
2987    xmlSchemaFreeParserCtxt(ctxt);
2988
2989    /*
2990     * most of the mess is about the output filenames generated by the Makefile
2991     */
2992    len = strlen(base);
2993    if ((len > 499) || (len < 5)) {
2994        xmlSchemaFree(schemas);
2995	return(-1);
2996    }
2997    len -= 4; /* remove trailing .xsd */
2998    if (base[len - 2] == '_') {
2999        len -= 2; /* remove subtest number */
3000    }
3001    if (base[len - 2] == '_') {
3002        len -= 2; /* remove subtest number */
3003    }
3004    memcpy(prefix, base, len);
3005    prefix[len] = 0;
3006
3007    snprintf(pattern, 499, "./test/schemas/%s_?.xml", prefix);
3008    pattern[499] = 0;
3009
3010    if (base[len] == '_') {
3011        len += 2;
3012	memcpy(prefix, base, len);
3013	prefix[len] = 0;
3014    }
3015
3016    globbuf.gl_offs = 0;
3017    glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
3018    for (i = 0;i < globbuf.gl_pathc;i++) {
3019        testErrorsSize = 0;
3020	testErrors[0] = 0;
3021        instance = globbuf.gl_pathv[i];
3022	base2 = baseFilename(instance);
3023	len = strlen(base2);
3024	if ((len > 6) && (base2[len - 6] == '_')) {
3025	    count = base2[len - 5];
3026	    snprintf(result, 499, "result/schemas/%s_%c",
3027		     prefix, count);
3028	    result[499] = 0;
3029	    snprintf(err, 499, "result/schemas/%s_%c.err",
3030		     prefix, count);
3031	    err[499] = 0;
3032	} else {
3033	    fprintf(stderr, "don't know how to process %s\n", instance);
3034	    continue;
3035	}
3036	if (schemas == NULL) {
3037	} else {
3038	    nb_tests++;
3039	    ret = schemasOneTest(filename, instance, result, err,
3040	                         options, schemas);
3041	    if (ret != 0)
3042		res = ret;
3043	}
3044    }
3045    globfree(&globbuf);
3046    xmlSchemaFree(schemas);
3047
3048    return(res);
3049}
3050
3051/************************************************************************
3052 *									*
3053 *			Schemas tests					*
3054 *									*
3055 ************************************************************************/
3056static int
3057rngOneTest(const char *sch,
3058               const char *filename,
3059               const char *result,
3060	       const char *err,
3061	       int options,
3062	       xmlRelaxNGPtr schemas) {
3063    xmlDocPtr doc;
3064    xmlRelaxNGValidCtxtPtr ctxt;
3065    int ret = 0;
3066    char *temp;
3067    FILE *schemasOutput;
3068
3069    doc = xmlReadFile(filename, NULL, options);
3070    if (doc == NULL) {
3071        fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch);
3072	return(-1);
3073    }
3074
3075    temp = resultFilename(result, "", ".res");
3076    if (temp == NULL) {
3077        fprintf(stderr, "Out of memory\n");
3078        fatalError();
3079    }
3080    schemasOutput = fopen(temp, "wb");
3081    if (schemasOutput == NULL) {
3082	fprintf(stderr, "failed to open output file %s\n", temp);
3083	xmlFreeDoc(doc);
3084        free(temp);
3085	return(-1);
3086    }
3087
3088    ctxt = xmlRelaxNGNewValidCtxt(schemas);
3089    xmlRelaxNGSetValidErrors(ctxt,
3090         (xmlRelaxNGValidityErrorFunc) testErrorHandler,
3091         (xmlRelaxNGValidityWarningFunc) testErrorHandler,
3092	 ctxt);
3093    ret = xmlRelaxNGValidateDoc(ctxt, doc);
3094    if (ret == 0) {
3095	testErrorHandler(NULL, "%s validates\n", filename);
3096    } else if (ret > 0) {
3097	testErrorHandler(NULL, "%s fails to validate\n", filename);
3098    } else {
3099	testErrorHandler(NULL, "%s validation generated an internal error\n",
3100	       filename);
3101    }
3102    fclose(schemasOutput);
3103    if (result) {
3104	if (compareFiles(temp, result)) {
3105	    fprintf(stderr, "Result for %s on %s failed\n", filename, sch);
3106	    ret = 1;
3107	}
3108    }
3109    unlink(temp);
3110    free(temp);
3111
3112    if (err != NULL) {
3113	if (compareFileMem(err, testErrors, testErrorsSize)) {
3114	    fprintf(stderr, "Error for %s on %s failed\n", filename, sch);
3115	    ret = 1;
3116	    printf("%s", testErrors);
3117	}
3118    }
3119
3120
3121    xmlRelaxNGFreeValidCtxt(ctxt);
3122    xmlFreeDoc(doc);
3123    return(ret);
3124}
3125/**
3126 * rngTest:
3127 * @filename: the schemas file
3128 * @result: the file with expected result
3129 * @err: the file with error messages
3130 *
3131 * Parse an RNG schemas and then apply it to the related .xml
3132 *
3133 * Returns 0 in case of success, an error code otherwise
3134 */
3135static int
3136rngTest(const char *filename,
3137            const char *resul ATTRIBUTE_UNUSED,
3138            const char *errr ATTRIBUTE_UNUSED,
3139            int options) {
3140    const char *base = baseFilename(filename);
3141    const char *base2;
3142    const char *instance;
3143    xmlRelaxNGParserCtxtPtr ctxt;
3144    xmlRelaxNGPtr schemas;
3145    int res = 0, len, ret;
3146    char pattern[500];
3147    char prefix[500];
3148    char result[500];
3149    char err[500];
3150    glob_t globbuf;
3151    size_t i;
3152    char count = 0;
3153
3154    /* first compile the schemas if possible */
3155    ctxt = xmlRelaxNGNewParserCtxt(filename);
3156    xmlRelaxNGSetParserErrors(ctxt,
3157         (xmlRelaxNGValidityErrorFunc) testErrorHandler,
3158         (xmlRelaxNGValidityWarningFunc) testErrorHandler,
3159	 ctxt);
3160    schemas = xmlRelaxNGParse(ctxt);
3161    xmlRelaxNGFreeParserCtxt(ctxt);
3162
3163    /*
3164     * most of the mess is about the output filenames generated by the Makefile
3165     */
3166    len = strlen(base);
3167    if ((len > 499) || (len < 5)) {
3168        xmlRelaxNGFree(schemas);
3169	return(-1);
3170    }
3171    len -= 4; /* remove trailing .rng */
3172    memcpy(prefix, base, len);
3173    prefix[len] = 0;
3174
3175    snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix);
3176    pattern[499] = 0;
3177
3178    globbuf.gl_offs = 0;
3179    glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
3180    for (i = 0;i < globbuf.gl_pathc;i++) {
3181        testErrorsSize = 0;
3182	testErrors[0] = 0;
3183        instance = globbuf.gl_pathv[i];
3184	base2 = baseFilename(instance);
3185	len = strlen(base2);
3186	if ((len > 6) && (base2[len - 6] == '_')) {
3187	    count = base2[len - 5];
3188	    snprintf(result, 499, "result/relaxng/%s_%c",
3189		     prefix, count);
3190	    result[499] = 0;
3191	    snprintf(err, 499, "result/relaxng/%s_%c.err",
3192		     prefix, count);
3193	    err[499] = 0;
3194	} else {
3195	    fprintf(stderr, "don't know how to process %s\n", instance);
3196	    continue;
3197	}
3198	if (schemas == NULL) {
3199	} else {
3200	    nb_tests++;
3201	    ret = rngOneTest(filename, instance, result, err,
3202	                         options, schemas);
3203	    if (res != 0)
3204		ret = res;
3205	}
3206    }
3207    globfree(&globbuf);
3208    xmlRelaxNGFree(schemas);
3209
3210    return(res);
3211}
3212
3213#ifdef LIBXML_READER_ENABLED
3214/**
3215 * rngStreamTest:
3216 * @filename: the schemas file
3217 * @result: the file with expected result
3218 * @err: the file with error messages
3219 *
3220 * Parse a set of files with streaming, applying an RNG schemas
3221 *
3222 * Returns 0 in case of success, an error code otherwise
3223 */
3224static int
3225rngStreamTest(const char *filename,
3226            const char *resul ATTRIBUTE_UNUSED,
3227            const char *errr ATTRIBUTE_UNUSED,
3228            int options) {
3229    const char *base = baseFilename(filename);
3230    const char *base2;
3231    const char *instance;
3232    int res = 0, len, ret;
3233    char pattern[500];
3234    char prefix[500];
3235    char result[500];
3236    char err[500];
3237    glob_t globbuf;
3238    size_t i;
3239    char count = 0;
3240    xmlTextReaderPtr reader;
3241    int disable_err = 0;
3242
3243    /*
3244     * most of the mess is about the output filenames generated by the Makefile
3245     */
3246    len = strlen(base);
3247    if ((len > 499) || (len < 5)) {
3248	fprintf(stderr, "len(base) == %d !\n", len);
3249	return(-1);
3250    }
3251    len -= 4; /* remove trailing .rng */
3252    memcpy(prefix, base, len);
3253    prefix[len] = 0;
3254
3255    /*
3256     * strictly unifying the error messages is nearly impossible this
3257     * hack is also done in the Makefile
3258     */
3259    if ((!strcmp(prefix, "tutor10_1")) || (!strcmp(prefix, "tutor10_2")) ||
3260        (!strcmp(prefix, "tutor3_2")) || (!strcmp(prefix, "307377")))
3261	disable_err = 1;
3262
3263    snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix);
3264    pattern[499] = 0;
3265
3266    globbuf.gl_offs = 0;
3267    glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
3268    for (i = 0;i < globbuf.gl_pathc;i++) {
3269        testErrorsSize = 0;
3270	testErrors[0] = 0;
3271        instance = globbuf.gl_pathv[i];
3272	base2 = baseFilename(instance);
3273	len = strlen(base2);
3274	if ((len > 6) && (base2[len - 6] == '_')) {
3275	    count = base2[len - 5];
3276	    snprintf(result, 499, "result/relaxng/%s_%c",
3277		     prefix, count);
3278	    result[499] = 0;
3279	    snprintf(err, 499, "result/relaxng/%s_%c.err",
3280		     prefix, count);
3281	    err[499] = 0;
3282	} else {
3283	    fprintf(stderr, "don't know how to process %s\n", instance);
3284	    continue;
3285	}
3286	reader = xmlReaderForFile(instance, NULL, options);
3287	if (reader == NULL) {
3288	    fprintf(stderr, "Failed to build reder for %s\n", instance);
3289	}
3290	if (disable_err == 1)
3291	    ret = streamProcessTest(instance, result, NULL, reader, filename);
3292	else
3293	    ret = streamProcessTest(instance, result, err, reader, filename);
3294	xmlFreeTextReader(reader);
3295	if (ret != 0) {
3296	    fprintf(stderr, "instance %s failed\n", instance);
3297	    res = ret;
3298	}
3299    }
3300    globfree(&globbuf);
3301
3302    return(res);
3303}
3304#endif /* READER */
3305
3306#endif
3307
3308#ifdef LIBXML_PATTERN_ENABLED
3309#ifdef LIBXML_READER_ENABLED
3310/************************************************************************
3311 *									*
3312 *			Patterns tests					*
3313 *									*
3314 ************************************************************************/
3315static void patternNode(FILE *out, xmlTextReaderPtr reader,
3316                        const char *pattern, xmlPatternPtr patternc,
3317			xmlStreamCtxtPtr patstream) {
3318    xmlChar *path = NULL;
3319    int match = -1;
3320    int type, empty;
3321
3322    type = xmlTextReaderNodeType(reader);
3323    empty = xmlTextReaderIsEmptyElement(reader);
3324
3325    if (type == XML_READER_TYPE_ELEMENT) {
3326	/* do the check only on element start */
3327	match = xmlPatternMatch(patternc, xmlTextReaderCurrentNode(reader));
3328
3329	if (match) {
3330	    path = xmlGetNodePath(xmlTextReaderCurrentNode(reader));
3331	    fprintf(out, "Node %s matches pattern %s\n", path, pattern);
3332	}
3333    }
3334    if (patstream != NULL) {
3335	int ret;
3336
3337	if (type == XML_READER_TYPE_ELEMENT) {
3338	    ret = xmlStreamPush(patstream,
3339				xmlTextReaderConstLocalName(reader),
3340				xmlTextReaderConstNamespaceUri(reader));
3341	    if (ret < 0) {
3342		fprintf(out, "xmlStreamPush() failure\n");
3343		xmlFreeStreamCtxt(patstream);
3344		patstream = NULL;
3345	    } else if (ret != match) {
3346		if (path == NULL) {
3347		    path = xmlGetNodePath(
3348				   xmlTextReaderCurrentNode(reader));
3349		}
3350		fprintf(out,
3351			"xmlPatternMatch and xmlStreamPush disagree\n");
3352		fprintf(out,
3353			"  pattern %s node %s\n",
3354			pattern, path);
3355	    }
3356
3357
3358	}
3359	if ((type == XML_READER_TYPE_END_ELEMENT) ||
3360	    ((type == XML_READER_TYPE_ELEMENT) && (empty))) {
3361	    ret = xmlStreamPop(patstream);
3362	    if (ret < 0) {
3363		fprintf(out, "xmlStreamPop() failure\n");
3364		xmlFreeStreamCtxt(patstream);
3365		patstream = NULL;
3366	    }
3367	}
3368    }
3369    if (path != NULL)
3370	xmlFree(path);
3371}
3372
3373/**
3374 * patternTest:
3375 * @filename: the schemas file
3376 * @result: the file with expected result
3377 * @err: the file with error messages
3378 *
3379 * Parse a set of files with streaming, applying an RNG schemas
3380 *
3381 * Returns 0 in case of success, an error code otherwise
3382 */
3383static int
3384patternTest(const char *filename,
3385            const char *resul ATTRIBUTE_UNUSED,
3386            const char *err ATTRIBUTE_UNUSED,
3387            int options) {
3388    xmlPatternPtr patternc = NULL;
3389    xmlStreamCtxtPtr patstream = NULL;
3390    FILE *o, *f;
3391    char str[1024];
3392    char xml[500];
3393    char result[500];
3394    int len, i;
3395    int ret = 0, res;
3396    char *temp;
3397    xmlTextReaderPtr reader;
3398    xmlDocPtr doc;
3399
3400    len = strlen(filename);
3401    len -= 4;
3402    memcpy(xml, filename, len);
3403    xml[len] = 0;
3404    snprintf(result, 499, "result/pattern/%s", baseFilename(xml));
3405    result[499] = 0;
3406    memcpy(xml + len, ".xml", 5);
3407
3408    if (!checkTestFile(xml)) {
3409	fprintf(stderr, "Missing xml file %s\n", xml);
3410	return(-1);
3411    }
3412    if (!checkTestFile(result)) {
3413	fprintf(stderr, "Missing result file %s\n", result);
3414	return(-1);
3415    }
3416    f = fopen(filename, "rb");
3417    if (f == NULL) {
3418        fprintf(stderr, "Failed to open %s\n", filename);
3419	return(-1);
3420    }
3421    temp = resultFilename(filename, "", ".res");
3422    if (temp == NULL) {
3423        fprintf(stderr, "Out of memory\n");
3424        fatalError();
3425    }
3426    o = fopen(temp, "wb");
3427    if (o == NULL) {
3428	fprintf(stderr, "failed to open output file %s\n", temp);
3429	fclose(f);
3430        free(temp);
3431	return(-1);
3432    }
3433    while (1) {
3434	/*
3435	 * read one line in string buffer.
3436	 */
3437	if (fgets (&str[0], sizeof (str) - 1, f) == NULL)
3438	   break;
3439
3440	/*
3441	 * remove the ending spaces
3442	 */
3443	i = strlen(str);
3444	while ((i > 0) &&
3445	       ((str[i - 1] == '\n') || (str[i - 1] == '\r') ||
3446		(str[i - 1] == ' ') || (str[i - 1] == '\t'))) {
3447	    i--;
3448	    str[i] = 0;
3449	}
3450	doc = xmlReadFile(xml, NULL, options);
3451	if (doc == NULL) {
3452	    fprintf(stderr, "Failed to parse %s\n", xml);
3453	    ret = 1;
3454	} else {
3455	    xmlNodePtr root;
3456	    const xmlChar *namespaces[22];
3457	    int j;
3458	    xmlNsPtr ns;
3459
3460	    root = xmlDocGetRootElement(doc);
3461	    for (ns = root->nsDef, j = 0;ns != NULL && j < 20;ns=ns->next) {
3462		namespaces[j++] = ns->href;
3463		namespaces[j++] = ns->prefix;
3464	    }
3465	    namespaces[j++] = NULL;
3466	    namespaces[j++] = NULL;
3467
3468	    patternc = xmlPatterncompile((const xmlChar *) str, doc->dict,
3469					 0, &namespaces[0]);
3470	    if (patternc == NULL) {
3471		testErrorHandler(NULL,
3472			"Pattern %s failed to compile\n", str);
3473		xmlFreeDoc(doc);
3474		ret = 1;
3475		continue;
3476	    }
3477	    patstream = xmlPatternGetStreamCtxt(patternc);
3478	    if (patstream != NULL) {
3479		ret = xmlStreamPush(patstream, NULL, NULL);
3480		if (ret < 0) {
3481		    fprintf(stderr, "xmlStreamPush() failure\n");
3482		    xmlFreeStreamCtxt(patstream);
3483		    patstream = NULL;
3484		}
3485	    }
3486	    nb_tests++;
3487
3488	    reader = xmlReaderWalker(doc);
3489	    res = xmlTextReaderRead(reader);
3490	    while (res == 1) {
3491		patternNode(o, reader, str, patternc, patstream);
3492		res = xmlTextReaderRead(reader);
3493	    }
3494	    if (res != 0) {
3495		fprintf(o, "%s : failed to parse\n", filename);
3496	    }
3497	    xmlFreeTextReader(reader);
3498	    xmlFreeDoc(doc);
3499	    xmlFreeStreamCtxt(patstream);
3500	    patstream = NULL;
3501	    xmlFreePattern(patternc);
3502
3503	}
3504    }
3505
3506    fclose(f);
3507    fclose(o);
3508
3509    ret = compareFiles(temp, result);
3510    if (ret) {
3511	fprintf(stderr, "Result for %s failed\n", filename);
3512	ret = 1;
3513    }
3514    unlink(temp);
3515    free(temp);
3516    return(ret);
3517}
3518#endif /* READER */
3519#endif /* PATTERN */
3520#ifdef LIBXML_C14N_ENABLED
3521/************************************************************************
3522 *									*
3523 *			Canonicalization tests				*
3524 *									*
3525 ************************************************************************/
3526static xmlXPathObjectPtr
3527load_xpath_expr (xmlDocPtr parent_doc, const char* filename) {
3528    xmlXPathObjectPtr xpath;
3529    xmlDocPtr doc;
3530    xmlChar *expr;
3531    xmlXPathContextPtr ctx;
3532    xmlNodePtr node;
3533    xmlNsPtr ns;
3534
3535    /*
3536     * load XPath expr as a file
3537     */
3538    xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
3539    xmlSubstituteEntitiesDefault(1);
3540
3541    doc = xmlReadFile(filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT);
3542    if (doc == NULL) {
3543	fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename);
3544	return(NULL);
3545    }
3546
3547    /*
3548     * Check the document is of the right kind
3549     */
3550    if(xmlDocGetRootElement(doc) == NULL) {
3551        fprintf(stderr,"Error: empty document for file \"%s\"\n", filename);
3552	xmlFreeDoc(doc);
3553	return(NULL);
3554    }
3555
3556    node = doc->children;
3557    while(node != NULL && !xmlStrEqual(node->name, (const xmlChar *)"XPath")) {
3558	node = node->next;
3559    }
3560
3561    if(node == NULL) {
3562        fprintf(stderr,"Error: XPath element expected in the file  \"%s\"\n", filename);
3563	xmlFreeDoc(doc);
3564	return(NULL);
3565    }
3566
3567    expr = xmlNodeGetContent(node);
3568    if(expr == NULL) {
3569        fprintf(stderr,"Error: XPath content element is NULL \"%s\"\n", filename);
3570	xmlFreeDoc(doc);
3571	return(NULL);
3572    }
3573
3574    ctx = xmlXPathNewContext(parent_doc);
3575    if(ctx == NULL) {
3576        fprintf(stderr,"Error: unable to create new context\n");
3577        xmlFree(expr);
3578        xmlFreeDoc(doc);
3579        return(NULL);
3580    }
3581
3582    /*
3583     * Register namespaces
3584     */
3585    ns = node->nsDef;
3586    while(ns != NULL) {
3587	if(xmlXPathRegisterNs(ctx, ns->prefix, ns->href) != 0) {
3588	    fprintf(stderr,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", ns->prefix, ns->href);
3589    xmlFree(expr);
3590	    xmlXPathFreeContext(ctx);
3591	    xmlFreeDoc(doc);
3592	    return(NULL);
3593	}
3594	ns = ns->next;
3595    }
3596
3597    /*
3598     * Evaluate xpath
3599     */
3600    xpath = xmlXPathEvalExpression(expr, ctx);
3601    if(xpath == NULL) {
3602        fprintf(stderr,"Error: unable to evaluate xpath expression\n");
3603xmlFree(expr);
3604        xmlXPathFreeContext(ctx);
3605        xmlFreeDoc(doc);
3606        return(NULL);
3607    }
3608
3609    /* print_xpath_nodes(xpath->nodesetval); */
3610
3611    xmlFree(expr);
3612    xmlXPathFreeContext(ctx);
3613    xmlFreeDoc(doc);
3614    return(xpath);
3615}
3616
3617/*
3618 * Macro used to grow the current buffer.
3619 */
3620#define xxx_growBufferReentrant() {						\
3621    buffer_size *= 2;							\
3622    buffer = (xmlChar **)						\
3623	xmlRealloc(buffer, buffer_size * sizeof(xmlChar*));	\
3624    if (buffer == NULL) {						\
3625	perror("realloc failed");					\
3626	return(NULL);							\
3627    }									\
3628}
3629
3630static xmlChar **
3631parse_list(xmlChar *str) {
3632    xmlChar **buffer;
3633    xmlChar **out = NULL;
3634    int buffer_size = 0;
3635    int len;
3636
3637    if(str == NULL) {
3638	return(NULL);
3639    }
3640
3641    len = xmlStrlen(str);
3642    if((str[0] == '\'') && (str[len - 1] == '\'')) {
3643	str[len - 1] = '\0';
3644	str++;
3645	len -= 2;
3646    }
3647    /*
3648     * allocate an translation buffer.
3649     */
3650    buffer_size = 1000;
3651    buffer = (xmlChar **) xmlMalloc(buffer_size * sizeof(xmlChar*));
3652    if (buffer == NULL) {
3653	perror("malloc failed");
3654	return(NULL);
3655    }
3656    out = buffer;
3657
3658    while(*str != '\0') {
3659	if (out - buffer > buffer_size - 10) {
3660	    int indx = out - buffer;
3661
3662	    xxx_growBufferReentrant();
3663	    out = &buffer[indx];
3664	}
3665	(*out++) = str;
3666	while(*str != ',' && *str != '\0') ++str;
3667	if(*str == ',') *(str++) = '\0';
3668    }
3669    (*out) = NULL;
3670    return buffer;
3671}
3672
3673static int
3674c14nRunTest(const char* xml_filename, int with_comments, int exclusive,
3675	    const char* xpath_filename, const char *ns_filename,
3676	    const char* result_file) {
3677    xmlDocPtr doc;
3678    xmlXPathObjectPtr xpath = NULL;
3679    xmlChar *result = NULL;
3680    int ret;
3681    xmlChar **inclusive_namespaces = NULL;
3682    const char *nslist = NULL;
3683    int nssize;
3684
3685
3686    /*
3687     * build an XML tree from a the file; we need to add default
3688     * attributes and resolve all character and entities references
3689     */
3690    xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
3691    xmlSubstituteEntitiesDefault(1);
3692
3693    doc = xmlReadFile(xml_filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT);
3694    if (doc == NULL) {
3695	fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_filename);
3696	return(-1);
3697    }
3698
3699    /*
3700     * Check the document is of the right kind
3701     */
3702    if(xmlDocGetRootElement(doc) == NULL) {
3703        fprintf(stderr,"Error: empty document for file \"%s\"\n", xml_filename);
3704	xmlFreeDoc(doc);
3705	return(-1);
3706    }
3707
3708    /*
3709     * load xpath file if specified
3710     */
3711    if(xpath_filename) {
3712	xpath = load_xpath_expr(doc, xpath_filename);
3713	if(xpath == NULL) {
3714	    fprintf(stderr,"Error: unable to evaluate xpath expression\n");
3715	    xmlFreeDoc(doc);
3716	    return(-1);
3717	}
3718    }
3719
3720    if (ns_filename != NULL) {
3721        if (loadMem(ns_filename, &nslist, &nssize)) {
3722	    fprintf(stderr,"Error: unable to evaluate xpath expression\n");
3723	    if(xpath != NULL) xmlXPathFreeObject(xpath);
3724	    xmlFreeDoc(doc);
3725	    return(-1);
3726	}
3727        inclusive_namespaces = parse_list((xmlChar *) nslist);
3728    }
3729
3730    /*
3731     * Canonical form
3732     */
3733    /* fprintf(stderr,"File \"%s\" loaded: start canonization\n", xml_filename); */
3734    ret = xmlC14NDocDumpMemory(doc,
3735	    (xpath) ? xpath->nodesetval : NULL,
3736	    exclusive, inclusive_namespaces,
3737	    with_comments, &result);
3738    if (ret >= 0) {
3739	if(result != NULL) {
3740	    if (compareFileMem(result_file, (const char *) result, ret)) {
3741		fprintf(stderr, "Result mismatch for %s\n", xml_filename);
3742	        ret = -1;
3743	    }
3744	}
3745    } else {
3746	fprintf(stderr,"Error: failed to canonicalize XML file \"%s\" (ret=%d)\n", xml_filename, ret);
3747	ret = -1;
3748    }
3749
3750    /*
3751     * Cleanup
3752     */
3753    if (result != NULL) xmlFree(result);
3754    if(xpath != NULL) xmlXPathFreeObject(xpath);
3755    if (inclusive_namespaces != NULL) xmlFree(inclusive_namespaces);
3756    if (nslist != NULL) free((char *) nslist);
3757    xmlFreeDoc(doc);
3758
3759    return(ret);
3760}
3761
3762static int
3763c14nCommonTest(const char *filename, int with_comments, int exclusive,
3764               const char *subdir) {
3765    char buf[500];
3766    char prefix[500];
3767    const char *base;
3768    int len;
3769    char *result = NULL;
3770    char *xpath = NULL;
3771    char *ns = NULL;
3772    int ret = 0;
3773
3774    base = baseFilename(filename);
3775    len = strlen(base);
3776    len -= 4;
3777    memcpy(prefix, base, len);
3778    prefix[len] = 0;
3779
3780    snprintf(buf, 499, "result/c14n/%s/%s", subdir,prefix);
3781    if (!checkTestFile(buf)) {
3782        fprintf(stderr, "Missing result file %s", buf);
3783	return(-1);
3784    }
3785    result = strdup(buf);
3786    snprintf(buf, 499, "test/c14n/%s/%s.xpath", subdir,prefix);
3787    if (checkTestFile(buf)) {
3788	xpath = strdup(buf);
3789    }
3790    snprintf(buf, 499, "test/c14n/%s/%s.ns", subdir,prefix);
3791    if (checkTestFile(buf)) {
3792	ns = strdup(buf);
3793    }
3794
3795    nb_tests++;
3796    if (c14nRunTest(filename, with_comments, exclusive,
3797                    xpath, ns, result) < 0)
3798        ret = 1;
3799
3800    if (result != NULL) free(result);
3801    if (xpath != NULL) free(xpath);
3802    if (ns != NULL) free(ns);
3803    return(ret);
3804}
3805
3806static int
3807c14nWithCommentTest(const char *filename,
3808                    const char *resul ATTRIBUTE_UNUSED,
3809		    const char *err ATTRIBUTE_UNUSED,
3810		    int options ATTRIBUTE_UNUSED) {
3811    return(c14nCommonTest(filename, 1, 0, "with-comments"));
3812}
3813static int
3814c14nWithoutCommentTest(const char *filename,
3815                    const char *resul ATTRIBUTE_UNUSED,
3816		    const char *err ATTRIBUTE_UNUSED,
3817		    int options ATTRIBUTE_UNUSED) {
3818    return(c14nCommonTest(filename, 0, 0, "without-comments"));
3819}
3820static int
3821c14nExcWithoutCommentTest(const char *filename,
3822                    const char *resul ATTRIBUTE_UNUSED,
3823		    const char *err ATTRIBUTE_UNUSED,
3824		    int options ATTRIBUTE_UNUSED) {
3825    return(c14nCommonTest(filename, 0, 1, "exc-without-comments"));
3826}
3827#endif
3828#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED) && defined (LIBXML_SAX1_ENABLED)
3829/************************************************************************
3830 *									*
3831 *			Catalog and threads test			*
3832 *									*
3833 ************************************************************************/
3834
3835/*
3836 * mostly a cut and paste from testThreads.c
3837 */
3838#define	MAX_ARGC	20
3839
3840static const char *catalog = "test/threads/complex.xml";
3841static const char *testfiles[] = {
3842    "test/threads/abc.xml",
3843    "test/threads/acb.xml",
3844    "test/threads/bac.xml",
3845    "test/threads/bca.xml",
3846    "test/threads/cab.xml",
3847    "test/threads/cba.xml",
3848    "test/threads/invalid.xml",
3849};
3850
3851static const char *Okay = "OK";
3852static const char *Failed = "Failed";
3853
3854#ifndef xmlDoValidityCheckingDefaultValue
3855#error xmlDoValidityCheckingDefaultValue is not a macro
3856#endif
3857#ifndef xmlGenericErrorContext
3858#error xmlGenericErrorContext is not a macro
3859#endif
3860
3861static void *
3862thread_specific_data(void *private_data)
3863{
3864    xmlDocPtr myDoc;
3865    const char *filename = (const char *) private_data;
3866    int okay = 1;
3867
3868    if (!strcmp(filename, "test/threads/invalid.xml")) {
3869        xmlDoValidityCheckingDefaultValue = 0;
3870        xmlGenericErrorContext = stdout;
3871    } else {
3872        xmlDoValidityCheckingDefaultValue = 1;
3873        xmlGenericErrorContext = stderr;
3874    }
3875    myDoc = xmlParseFile(filename);
3876    if (myDoc) {
3877        xmlFreeDoc(myDoc);
3878    } else {
3879        printf("parse failed\n");
3880        okay = 0;
3881    }
3882    if (!strcmp(filename, "test/threads/invalid.xml")) {
3883        if (xmlDoValidityCheckingDefaultValue != 0) {
3884            printf("ValidityCheckingDefaultValue override failed\n");
3885            okay = 0;
3886        }
3887        if (xmlGenericErrorContext != stdout) {
3888            printf("xmlGenericErrorContext override failed\n");
3889            okay = 0;
3890        }
3891    } else {
3892        if (xmlDoValidityCheckingDefaultValue != 1) {
3893            printf("ValidityCheckingDefaultValue override failed\n");
3894            okay = 0;
3895        }
3896        if (xmlGenericErrorContext != stderr) {
3897            printf("xmlGenericErrorContext override failed\n");
3898            okay = 0;
3899        }
3900    }
3901    if (okay == 0)
3902        return ((void *) Failed);
3903    return ((void *) Okay);
3904}
3905
3906#if defined(linux) || defined(solaris) || defined(__APPLE_CC__)
3907
3908#include <pthread.h>
3909
3910static pthread_t tid[MAX_ARGC];
3911
3912static int
3913testThread(void)
3914{
3915    unsigned int i, repeat;
3916    unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]);
3917    void *results[MAX_ARGC];
3918    int ret;
3919    int res = 0;
3920
3921    xmlInitParser();
3922
3923    for (repeat = 0; repeat < 500; repeat++) {
3924        xmlLoadCatalog(catalog);
3925        nb_tests++;
3926
3927        for (i = 0; i < num_threads; i++) {
3928            results[i] = NULL;
3929            tid[i] = (pthread_t) - 1;
3930        }
3931
3932        for (i = 0; i < num_threads; i++) {
3933            ret = pthread_create(&tid[i], 0, thread_specific_data,
3934                                 (void *) testfiles[i]);
3935            if (ret != 0) {
3936                fprintf(stderr, "pthread_create failed\n");
3937                return (1);
3938            }
3939        }
3940        for (i = 0; i < num_threads; i++) {
3941            ret = pthread_join(tid[i], &results[i]);
3942            if (ret != 0) {
3943                fprintf(stderr, "pthread_join failed\n");
3944                return (1);
3945            }
3946        }
3947
3948        xmlCatalogCleanup();
3949        for (i = 0; i < num_threads; i++)
3950            if (results[i] != (void *) Okay) {
3951                fprintf(stderr, "Thread %d handling %s failed\n",
3952                        i, testfiles[i]);
3953                res = 1;
3954            }
3955    }
3956    return (res);
3957}
3958
3959#elif defined WIN32
3960#include <windows.h>
3961#include <string.h>
3962
3963#define TEST_REPEAT_COUNT 500
3964
3965static HANDLE tid[MAX_ARGC];
3966
3967static DWORD WINAPI
3968win32_thread_specific_data(void *private_data)
3969{
3970    return((DWORD) thread_specific_data(private_data));
3971}
3972
3973static int
3974testThread(void)
3975{
3976    unsigned int i, repeat;
3977    unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]);
3978    DWORD results[MAX_ARGC];
3979    BOOL ret;
3980    int res = 0;
3981
3982    xmlInitParser();
3983    for (repeat = 0; repeat < TEST_REPEAT_COUNT; repeat++) {
3984        xmlLoadCatalog(catalog);
3985        nb_tests++;
3986
3987        for (i = 0; i < num_threads; i++) {
3988            results[i] = 0;
3989            tid[i] = (HANDLE) - 1;
3990        }
3991
3992        for (i = 0; i < num_threads; i++) {
3993            DWORD useless;
3994
3995            tid[i] = CreateThread(NULL, 0,
3996                                  win32_thread_specific_data,
3997				  (void *) testfiles[i], 0,
3998                                  &useless);
3999            if (tid[i] == NULL) {
4000                fprintf(stderr, "CreateThread failed\n");
4001                return(1);
4002            }
4003        }
4004
4005        if (WaitForMultipleObjects(num_threads, tid, TRUE, INFINITE) ==
4006            WAIT_FAILED) {
4007            fprintf(stderr, "WaitForMultipleObjects failed\n");
4008	    return(1);
4009	}
4010
4011        for (i = 0; i < num_threads; i++) {
4012            ret = GetExitCodeThread(tid[i], &results[i]);
4013            if (ret == 0) {
4014                fprintf(stderr, "GetExitCodeThread failed\n");
4015                return(1);
4016            }
4017            CloseHandle(tid[i]);
4018        }
4019
4020        xmlCatalogCleanup();
4021        for (i = 0; i < num_threads; i++) {
4022            if (results[i] != (DWORD) Okay) {
4023                fprintf(stderr, "Thread %d handling %s failed\n",
4024		        i, testfiles[i]);
4025	        res = 1;
4026	    }
4027        }
4028    }
4029
4030    return (res);
4031}
4032
4033#elif defined __BEOS__
4034#include <OS.h>
4035
4036static thread_id tid[MAX_ARGC];
4037
4038static int
4039testThread(void)
4040{
4041    unsigned int i, repeat;
4042    unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]);
4043    void *results[MAX_ARGC];
4044    status_t ret;
4045    int res = 0;
4046
4047    xmlInitParser();
4048    for (repeat = 0; repeat < 500; repeat++) {
4049        xmlLoadCatalog(catalog);
4050        for (i = 0; i < num_threads; i++) {
4051            results[i] = NULL;
4052            tid[i] = (thread_id) - 1;
4053        }
4054        for (i = 0; i < num_threads; i++) {
4055            tid[i] =
4056                spawn_thread(thread_specific_data, "xmlTestThread",
4057                             B_NORMAL_PRIORITY, (void *) testfiles[i]);
4058            if (tid[i] < B_OK) {
4059                fprintf(stderr, "beos_thread_create failed\n");
4060                return (1);
4061            }
4062            printf("beos_thread_create %d -> %d\n", i, tid[i]);
4063        }
4064        for (i = 0; i < num_threads; i++) {
4065            ret = wait_for_thread(tid[i], &results[i]);
4066            printf("beos_thread_wait %d -> %d\n", i, ret);
4067            if (ret != B_OK) {
4068                fprintf(stderr, "beos_thread_wait failed\n");
4069                return (1);
4070            }
4071        }
4072
4073        xmlCatalogCleanup();
4074        ret = B_OK;
4075        for (i = 0; i < num_threads; i++)
4076            if (results[i] != (void *) Okay) {
4077                printf("Thread %d handling %s failed\n", i, testfiles[i]);
4078                ret = B_ERROR;
4079            }
4080    }
4081    if (ret != B_OK)
4082        return(1);
4083    return (0);
4084}
4085#else
4086static int
4087testThread(void)
4088{
4089    fprintf(stderr,
4090            "Specific platform thread support not detected\n");
4091    return (-1);
4092}
4093#endif
4094static int
4095threadsTest(const char *filename ATTRIBUTE_UNUSED,
4096	    const char *resul ATTRIBUTE_UNUSED,
4097	    const char *err ATTRIBUTE_UNUSED,
4098	    int options ATTRIBUTE_UNUSED) {
4099    return(testThread());
4100}
4101#endif
4102/************************************************************************
4103 *									*
4104 *			Tests Descriptions				*
4105 *									*
4106 ************************************************************************/
4107
4108static
4109testDesc testDescriptions[] = {
4110    { "XML regression tests" ,
4111      oldParseTest, "./test/*", "result/", "", NULL,
4112      0 },
4113    { "XML regression tests on memory" ,
4114      memParseTest, "./test/*", "result/", "", NULL,
4115      0 },
4116    { "XML entity subst regression tests" ,
4117      noentParseTest, "./test/*", "result/noent/", "", NULL,
4118      XML_PARSE_NOENT },
4119    { "XML Namespaces regression tests",
4120      errParseTest, "./test/namespaces/*", "result/namespaces/", "", ".err",
4121      0 },
4122    { "Error cases regression tests",
4123      errParseTest, "./test/errors/*.xml", "result/errors/", "", ".err",
4124      0 },
4125#ifdef LIBXML_READER_ENABLED
4126    { "Error cases stream regression tests",
4127      streamParseTest, "./test/errors/*.xml", "result/errors/", NULL, ".str",
4128      0 },
4129    { "Reader regression tests",
4130      streamParseTest, "./test/*", "result/", ".rdr", NULL,
4131      0 },
4132    { "Reader entities substitution regression tests",
4133      streamParseTest, "./test/*", "result/", ".rde", NULL,
4134      XML_PARSE_NOENT },
4135    { "Reader on memory regression tests",
4136      streamMemParseTest, "./test/*", "result/", ".rdr", NULL,
4137      0 },
4138    { "Walker regression tests",
4139      walkerParseTest, "./test/*", "result/", ".rdr", NULL,
4140      0 },
4141#endif
4142#ifdef LIBXML_SAX1_ENABLED
4143    { "SAX1 callbacks regression tests" ,
4144      saxParseTest, "./test/*", "result/", ".sax", NULL,
4145      XML_PARSE_SAX1 },
4146    { "SAX2 callbacks regression tests" ,
4147      saxParseTest, "./test/*", "result/", ".sax2", NULL,
4148      0 },
4149#endif
4150#ifdef LIBXML_PUSH_ENABLED
4151    { "XML push regression tests" ,
4152      pushParseTest, "./test/*", "result/", "", NULL,
4153      0 },
4154#endif
4155#ifdef LIBXML_HTML_ENABLED
4156    { "HTML regression tests" ,
4157      errParseTest, "./test/HTML/*", "result/HTML/", "", ".err",
4158      XML_PARSE_HTML },
4159#ifdef LIBXML_PUSH_ENABLED
4160    { "Push HTML regression tests" ,
4161      pushParseTest, "./test/HTML/*", "result/HTML/", "", ".err",
4162      XML_PARSE_HTML },
4163#endif
4164#ifdef LIBXML_SAX1_ENABLED
4165    { "HTML SAX regression tests" ,
4166      saxParseTest, "./test/HTML/*", "result/HTML/", ".sax", NULL,
4167      XML_PARSE_HTML },
4168#endif
4169#endif
4170#ifdef LIBXML_VALID_ENABLED
4171    { "Valid documents regression tests" ,
4172      errParseTest, "./test/VCM/*", NULL, NULL, NULL,
4173      XML_PARSE_DTDVALID },
4174    { "Validity checking regression tests" ,
4175      errParseTest, "./test/VC/*", "result/VC/", NULL, "",
4176      XML_PARSE_DTDVALID },
4177    { "General documents valid regression tests" ,
4178      errParseTest, "./test/valid/*", "result/valid/", "", ".err",
4179      XML_PARSE_DTDVALID },
4180#endif
4181#ifdef LIBXML_XINCLUDE_ENABLED
4182    { "XInclude regression tests" ,
4183      errParseTest, "./test/XInclude/docs/*", "result/XInclude/", "", NULL,
4184      /* Ignore errors at this point ".err", */
4185      XML_PARSE_XINCLUDE },
4186#ifdef LIBXML_READER_ENABLED
4187    { "XInclude xmlReader regression tests",
4188      streamParseTest, "./test/XInclude/docs/*", "result/XInclude/", ".rdr",
4189      /* Ignore errors at this point ".err", */
4190      NULL, XML_PARSE_XINCLUDE },
4191#endif
4192    { "XInclude regression tests stripping include nodes" ,
4193      errParseTest, "./test/XInclude/docs/*", "result/XInclude/", "", NULL,
4194      /* Ignore errors at this point ".err", */
4195      XML_PARSE_XINCLUDE | XML_PARSE_NOXINCNODE },
4196#ifdef LIBXML_READER_ENABLED
4197    { "XInclude xmlReader regression tests stripping include nodes",
4198      streamParseTest, "./test/XInclude/docs/*", "result/XInclude/", ".rdr",
4199      /* Ignore errors at this point ".err", */
4200      NULL, XML_PARSE_XINCLUDE | XML_PARSE_NOXINCNODE },
4201#endif
4202#endif
4203#ifdef LIBXML_XPATH_ENABLED
4204#ifdef LIBXML_DEBUG_ENABLED
4205    { "XPath expressions regression tests" ,
4206      xpathExprTest, "./test/XPath/expr/*", "result/XPath/expr/", "", NULL,
4207      0 },
4208    { "XPath document queries regression tests" ,
4209      xpathDocTest, "./test/XPath/docs/*", NULL, NULL, NULL,
4210      0 },
4211#ifdef LIBXML_XPTR_ENABLED
4212    { "XPointer document queries regression tests" ,
4213      xptrDocTest, "./test/XPath/docs/*", NULL, NULL, NULL,
4214      0 },
4215#endif
4216    { "xml:id regression tests" ,
4217      xmlidDocTest, "./test/xmlid/*", "result/xmlid/", "", ".err",
4218      0 },
4219#endif
4220#endif
4221    { "URI parsing tests" ,
4222      uriParseTest, "./test/URI/*.uri", "result/URI/", "", NULL,
4223      0 },
4224    { "URI base composition tests" ,
4225      uriBaseTest, "./test/URI/*.data", "result/URI/", "", NULL,
4226      0 },
4227    { "Path URI conversion tests" ,
4228      uriPathTest, NULL, NULL, NULL, NULL,
4229      0 },
4230#ifdef LIBXML_SCHEMAS_ENABLED
4231    { "Schemas regression tests" ,
4232      schemasTest, "./test/schemas/*_*.xsd", NULL, NULL, NULL,
4233      0 },
4234    { "Relax-NG regression tests" ,
4235      rngTest, "./test/relaxng/*.rng", NULL, NULL, NULL,
4236      XML_PARSE_DTDATTR | XML_PARSE_NOENT },
4237#ifdef LIBXML_READER_ENABLED
4238    { "Relax-NG streaming regression tests" ,
4239      rngStreamTest, "./test/relaxng/*.rng", NULL, NULL, NULL,
4240      XML_PARSE_DTDATTR | XML_PARSE_NOENT },
4241#endif
4242#endif
4243#ifdef LIBXML_PATTERN_ENABLED
4244#ifdef LIBXML_READER_ENABLED
4245    { "Pattern regression tests" ,
4246      patternTest, "./test/pattern/*.pat", "result/pattern/", NULL, NULL,
4247      0 },
4248#endif
4249#endif
4250#ifdef LIBXML_C14N_ENABLED
4251    { "C14N with comments regression tests" ,
4252      c14nWithCommentTest, "./test/c14n/with-comments/*.xml", NULL, NULL, NULL,
4253      0 },
4254    { "C14N without comments regression tests" ,
4255      c14nWithoutCommentTest, "./test/c14n/without-comments/*.xml", NULL, NULL, NULL,
4256      0 },
4257    { "C14N exclusive without comments regression tests" ,
4258      c14nExcWithoutCommentTest, "./test/c14n/exc-without-comments/*.xml", NULL, NULL, NULL,
4259      0 },
4260#endif
4261#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED) && defined(LIBXML_SAX1_ENABLED)
4262    { "Catalog and Threads regression tests" ,
4263      threadsTest, NULL, NULL, NULL, NULL,
4264      0 },
4265#endif
4266    {NULL, NULL, NULL, NULL, NULL, NULL, 0}
4267};
4268
4269/************************************************************************
4270 *									*
4271 *		The main code driving the tests				*
4272 *									*
4273 ************************************************************************/
4274
4275static int
4276launchTests(testDescPtr tst) {
4277    int res = 0, err = 0;
4278    size_t i;
4279    char *result;
4280    char *error;
4281    int mem;
4282
4283    if (tst == NULL) return(-1);
4284    if (tst->in != NULL) {
4285	glob_t globbuf;
4286
4287	globbuf.gl_offs = 0;
4288	glob(tst->in, GLOB_DOOFFS, NULL, &globbuf);
4289	for (i = 0;i < globbuf.gl_pathc;i++) {
4290	    if (!checkTestFile(globbuf.gl_pathv[i]))
4291	        continue;
4292	    if (tst->suffix != NULL) {
4293		result = resultFilename(globbuf.gl_pathv[i], tst->out,
4294					tst->suffix);
4295		if (result == NULL) {
4296		    fprintf(stderr, "Out of memory !\n");
4297		    fatalError();
4298		}
4299	    } else {
4300	        result = NULL;
4301	    }
4302	    if (tst->err != NULL) {
4303		error = resultFilename(globbuf.gl_pathv[i], tst->out,
4304		                        tst->err);
4305		if (error == NULL) {
4306		    fprintf(stderr, "Out of memory !\n");
4307		    fatalError();
4308		}
4309	    } else {
4310	        error = NULL;
4311	    }
4312	    if ((result) &&(!checkTestFile(result))) {
4313	        fprintf(stderr, "Missing result file %s\n", result);
4314	    } else if ((error) &&(!checkTestFile(error))) {
4315	        fprintf(stderr, "Missing error file %s\n", error);
4316	    } else {
4317		mem = xmlMemUsed();
4318		extraMemoryFromResolver = 0;
4319		testErrorsSize = 0;
4320		testErrors[0] = 0;
4321		res = tst->func(globbuf.gl_pathv[i], result, error,
4322		                tst->options | XML_PARSE_COMPACT);
4323		xmlResetLastError();
4324		if (res != 0) {
4325		    fprintf(stderr, "File %s generated an error\n",
4326		            globbuf.gl_pathv[i]);
4327		    nb_errors++;
4328		    err++;
4329		}
4330		else if (xmlMemUsed() != mem) {
4331		    if ((xmlMemUsed() != mem) &&
4332		        (extraMemoryFromResolver == 0)) {
4333			fprintf(stderr, "File %s leaked %d bytes\n",
4334				globbuf.gl_pathv[i], xmlMemUsed() - mem);
4335			nb_leaks++;
4336			err++;
4337		    }
4338		}
4339		testErrorsSize = 0;
4340	    }
4341	    if (result)
4342		free(result);
4343	    if (error)
4344		free(error);
4345	}
4346	globfree(&globbuf);
4347    } else {
4348        testErrorsSize = 0;
4349	testErrors[0] = 0;
4350	extraMemoryFromResolver = 0;
4351        res = tst->func(NULL, NULL, NULL, tst->options);
4352	if (res != 0) {
4353	    nb_errors++;
4354	    err++;
4355	}
4356    }
4357    return(err);
4358}
4359
4360static int verbose = 0;
4361static int tests_quiet = 0;
4362
4363static int
4364runtest(int i) {
4365    int ret = 0, res;
4366    int old_errors, old_tests, old_leaks;
4367
4368    old_errors = nb_errors;
4369    old_tests = nb_tests;
4370    old_leaks = nb_leaks;
4371    if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL))
4372	printf("## %s\n", testDescriptions[i].desc);
4373    res = launchTests(&testDescriptions[i]);
4374    if (res != 0)
4375	ret++;
4376    if (verbose) {
4377	if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
4378	    printf("Ran %d tests, no errors\n", nb_tests - old_tests);
4379	else
4380	    printf("Ran %d tests, %d errors, %d leaks\n",
4381		   nb_tests - old_tests,
4382		   nb_errors - old_errors,
4383		   nb_leaks - old_leaks);
4384    }
4385    return(ret);
4386}
4387
4388int
4389main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
4390    int i, a, ret = 0;
4391    int subset = 0;
4392
4393    initializeLibxml2();
4394
4395    for (a = 1; a < argc;a++) {
4396        if (!strcmp(argv[a], "-v"))
4397	    verbose = 1;
4398        else if (!strcmp(argv[a], "-quiet"))
4399	    tests_quiet = 1;
4400	else {
4401	    for (i = 0; testDescriptions[i].func != NULL; i++) {
4402	        if (strstr(testDescriptions[i].desc, argv[a])) {
4403		    ret += runtest(i);
4404		    subset++;
4405		}
4406	    }
4407	}
4408    }
4409    if (subset == 0) {
4410	for (i = 0; testDescriptions[i].func != NULL; i++) {
4411	    ret += runtest(i);
4412	}
4413    }
4414    if ((nb_errors == 0) && (nb_leaks == 0)) {
4415        ret = 0;
4416	printf("Total %d tests, no errors\n",
4417	       nb_tests);
4418    } else {
4419        ret = 1;
4420	printf("Total %d tests, %d errors, %d leaks\n",
4421	       nb_tests, nb_errors, nb_leaks);
4422    }
4423    xmlCleanupParser();
4424    xmlMemoryDump();
4425
4426    return(ret);
4427}
4428
4429#else /* ! LIBXML_OUTPUT_ENABLED */
4430int
4431main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
4432    fprintf(stderr, "runtest requires output to be enabled in libxml2\n");
4433    return(1);
4434}
4435#endif
4436