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    }
1708    if (temp != NULL) {
1709        unlink(temp);
1710        free(temp);
1711    }
1712
1713    /* switch back to structured error handling */
1714    xmlSetGenericErrorFunc(NULL, NULL);
1715    xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
1716
1717    return(ret);
1718}
1719#endif
1720
1721/************************************************************************
1722 *									*
1723 *		Parse to tree based tests				*
1724 *									*
1725 ************************************************************************/
1726/**
1727 * oldParseTest:
1728 * @filename: the file to parse
1729 * @result: the file with expected result
1730 * @err: the file with error messages: unused
1731 *
1732 * Parse a file using the old xmlParseFile API, then serialize back
1733 * reparse the result and serialize again, then check for deviation
1734 * in serialization.
1735 *
1736 * Returns 0 in case of success, an error code otherwise
1737 */
1738static int
1739oldParseTest(const char *filename, const char *result,
1740             const char *err ATTRIBUTE_UNUSED,
1741	     int options ATTRIBUTE_UNUSED) {
1742    xmlDocPtr doc;
1743    char *temp;
1744    int res = 0;
1745
1746    nb_tests++;
1747    /*
1748     * base of the test, parse with the old API
1749     */
1750#ifdef LIBXML_SAX1_ENABLED
1751    doc = xmlParseFile(filename);
1752#else
1753    doc = xmlReadFile(filename, NULL, 0);
1754#endif
1755    if (doc == NULL)
1756        return(1);
1757    temp = resultFilename(filename, "", ".res");
1758    if (temp == NULL) {
1759        fprintf(stderr, "out of memory\n");
1760        fatalError();
1761    }
1762    xmlSaveFile(temp, doc);
1763    if (compareFiles(temp, result)) {
1764        res = 1;
1765    }
1766    xmlFreeDoc(doc);
1767
1768    /*
1769     * Parse the saved result to make sure the round trip is okay
1770     */
1771#ifdef LIBXML_SAX1_ENABLED
1772    doc = xmlParseFile(temp);
1773#else
1774    doc = xmlReadFile(temp, NULL, 0);
1775#endif
1776    if (doc == NULL)
1777        return(1);
1778    xmlSaveFile(temp, doc);
1779    if (compareFiles(temp, result)) {
1780        res = 1;
1781    }
1782    xmlFreeDoc(doc);
1783
1784    if (temp != NULL) {
1785        unlink(temp);
1786        free(temp);
1787    }
1788    return(res);
1789}
1790
1791#ifdef LIBXML_PUSH_ENABLED
1792/**
1793 * pushParseTest:
1794 * @filename: the file to parse
1795 * @result: the file with expected result
1796 * @err: the file with error messages: unused
1797 *
1798 * Parse a file using the Push API, then serialize back
1799 * to check for content.
1800 *
1801 * Returns 0 in case of success, an error code otherwise
1802 */
1803static int
1804pushParseTest(const char *filename, const char *result,
1805             const char *err ATTRIBUTE_UNUSED,
1806	     int options) {
1807    xmlParserCtxtPtr ctxt;
1808    xmlDocPtr doc;
1809    const char *base;
1810    int size, res;
1811    int cur = 0;
1812
1813    nb_tests++;
1814    /*
1815     * load the document in memory and work from there.
1816     */
1817    if (loadMem(filename, &base, &size) != 0) {
1818        fprintf(stderr, "Failed to load %s\n", filename);
1819	return(-1);
1820    }
1821
1822#ifdef LIBXML_HTML_ENABLED
1823    if (options & XML_PARSE_HTML)
1824	ctxt = htmlCreatePushParserCtxt(NULL, NULL, base + cur, 4, filename,
1825	                                XML_CHAR_ENCODING_NONE);
1826    else
1827#endif
1828    ctxt = xmlCreatePushParserCtxt(NULL, NULL, base + cur, 4, filename);
1829    xmlCtxtUseOptions(ctxt, options);
1830    cur += 4;
1831    while (cur < size) {
1832        if (cur + 1024 >= size) {
1833#ifdef LIBXML_HTML_ENABLED
1834	    if (options & XML_PARSE_HTML)
1835		htmlParseChunk(ctxt, base + cur, size - cur, 1);
1836	    else
1837#endif
1838	    xmlParseChunk(ctxt, base + cur, size - cur, 1);
1839	    break;
1840	} else {
1841#ifdef LIBXML_HTML_ENABLED
1842	    if (options & XML_PARSE_HTML)
1843		htmlParseChunk(ctxt, base + cur, 1024, 0);
1844	    else
1845#endif
1846	    xmlParseChunk(ctxt, base + cur, 1024, 0);
1847	    cur += 1024;
1848	}
1849    }
1850    doc = ctxt->myDoc;
1851#ifdef LIBXML_HTML_ENABLED
1852    if (options & XML_PARSE_HTML)
1853        res = 1;
1854    else
1855#endif
1856    res = ctxt->wellFormed;
1857    xmlFreeParserCtxt(ctxt);
1858    free((char *)base);
1859    if (!res) {
1860	xmlFreeDoc(doc);
1861	fprintf(stderr, "Failed to parse %s\n", filename);
1862	return(-1);
1863    }
1864#ifdef LIBXML_HTML_ENABLED
1865    if (options & XML_PARSE_HTML)
1866	htmlDocDumpMemory(doc, (xmlChar **) &base, &size);
1867    else
1868#endif
1869    xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
1870    xmlFreeDoc(doc);
1871    res = compareFileMem(result, base, size);
1872    if ((base == NULL) || (res != 0)) {
1873	if (base != NULL)
1874	    xmlFree((char *)base);
1875        fprintf(stderr, "Result for %s failed\n", filename);
1876	return(-1);
1877    }
1878    xmlFree((char *)base);
1879    if (err != NULL) {
1880	res = compareFileMem(err, testErrors, testErrorsSize);
1881	if (res != 0) {
1882	    fprintf(stderr, "Error for %s failed\n", filename);
1883	    return(-1);
1884	}
1885    }
1886    return(0);
1887}
1888#endif
1889
1890/**
1891 * memParseTest:
1892 * @filename: the file to parse
1893 * @result: the file with expected result
1894 * @err: the file with error messages: unused
1895 *
1896 * Parse a file using the old xmlReadMemory API, then serialize back
1897 * reparse the result and serialize again, then check for deviation
1898 * in serialization.
1899 *
1900 * Returns 0 in case of success, an error code otherwise
1901 */
1902static int
1903memParseTest(const char *filename, const char *result,
1904             const char *err ATTRIBUTE_UNUSED,
1905	     int options ATTRIBUTE_UNUSED) {
1906    xmlDocPtr doc;
1907    const char *base;
1908    int size, res;
1909
1910    nb_tests++;
1911    /*
1912     * load and parse the memory
1913     */
1914    if (loadMem(filename, &base, &size) != 0) {
1915        fprintf(stderr, "Failed to load %s\n", filename);
1916	return(-1);
1917    }
1918
1919    doc = xmlReadMemory(base, size, filename, NULL, 0);
1920    unloadMem(base);
1921    if (doc == NULL) {
1922        return(1);
1923    }
1924    xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
1925    xmlFreeDoc(doc);
1926    res = compareFileMem(result, base, size);
1927    if ((base == NULL) || (res != 0)) {
1928	if (base != NULL)
1929	    xmlFree((char *)base);
1930        fprintf(stderr, "Result for %s failed\n", filename);
1931	return(-1);
1932    }
1933    xmlFree((char *)base);
1934    return(0);
1935}
1936
1937/**
1938 * noentParseTest:
1939 * @filename: the file to parse
1940 * @result: the file with expected result
1941 * @err: the file with error messages: unused
1942 *
1943 * Parse a file with entity resolution, then serialize back
1944 * reparse the result and serialize again, then check for deviation
1945 * in serialization.
1946 *
1947 * Returns 0 in case of success, an error code otherwise
1948 */
1949static int
1950noentParseTest(const char *filename, const char *result,
1951               const char *err  ATTRIBUTE_UNUSED,
1952	       int options) {
1953    xmlDocPtr doc;
1954    char *temp;
1955    int res = 0;
1956
1957    nb_tests++;
1958    /*
1959     * base of the test, parse with the old API
1960     */
1961    doc = xmlReadFile(filename, NULL, options);
1962    if (doc == NULL)
1963        return(1);
1964    temp = resultFilename(filename, "", ".res");
1965    if (temp == NULL) {
1966        fprintf(stderr, "Out of memory\n");
1967        fatalError();
1968    }
1969    xmlSaveFile(temp, doc);
1970    if (compareFiles(temp, result)) {
1971        res = 1;
1972    }
1973    xmlFreeDoc(doc);
1974
1975    /*
1976     * Parse the saved result to make sure the round trip is okay
1977     */
1978    doc = xmlReadFile(filename, NULL, options);
1979    if (doc == NULL)
1980        return(1);
1981    xmlSaveFile(temp, doc);
1982    if (compareFiles(temp, result)) {
1983        res = 1;
1984    }
1985    xmlFreeDoc(doc);
1986
1987    if (temp != NULL) {
1988        unlink(temp);
1989        free(temp);
1990    }
1991    return(res);
1992}
1993
1994/**
1995 * errParseTest:
1996 * @filename: the file to parse
1997 * @result: the file with expected result
1998 * @err: the file with error messages
1999 *
2000 * Parse a file using the xmlReadFile API and check for errors.
2001 *
2002 * Returns 0 in case of success, an error code otherwise
2003 */
2004static int
2005errParseTest(const char *filename, const char *result, const char *err,
2006             int options) {
2007    xmlDocPtr doc;
2008    const char *base = NULL;
2009    int size, res = 0;
2010
2011    nb_tests++;
2012#ifdef LIBXML_HTML_ENABLED
2013    if (options & XML_PARSE_HTML) {
2014        doc = htmlReadFile(filename, NULL, options);
2015    } else
2016#endif
2017#ifdef LIBXML_XINCLUDE_ENABLED
2018    if (options & XML_PARSE_XINCLUDE) {
2019	doc = xmlReadFile(filename, NULL, options);
2020	xmlXIncludeProcessFlags(doc, options);
2021    } else
2022#endif
2023    {
2024	xmlGetWarningsDefaultValue = 1;
2025	doc = xmlReadFile(filename, NULL, options);
2026    }
2027    xmlGetWarningsDefaultValue = 0;
2028    if (result) {
2029	if (doc == NULL) {
2030	    base = "";
2031	    size = 0;
2032	} else {
2033#ifdef LIBXML_HTML_ENABLED
2034	    if (options & XML_PARSE_HTML) {
2035		htmlDocDumpMemory(doc, (xmlChar **) &base, &size);
2036	    } else
2037#endif
2038	    xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
2039	}
2040	res = compareFileMem(result, base, size);
2041    }
2042    if (doc != NULL) {
2043	if (base != NULL)
2044	    xmlFree((char *)base);
2045	xmlFreeDoc(doc);
2046    }
2047    if (res != 0) {
2048        fprintf(stderr, "Result for %s failed\n", filename);
2049	return(-1);
2050    }
2051    if (err != NULL) {
2052	res = compareFileMem(err, testErrors, testErrorsSize);
2053	if (res != 0) {
2054	    fprintf(stderr, "Error for %s failed\n", filename);
2055	    return(-1);
2056	}
2057    } else if (options & XML_PARSE_DTDVALID) {
2058        if (testErrorsSize != 0)
2059	    fprintf(stderr, "Validation for %s failed\n", filename);
2060    }
2061
2062    return(0);
2063}
2064
2065#ifdef LIBXML_READER_ENABLED
2066/************************************************************************
2067 *									*
2068 *		Reader based tests					*
2069 *									*
2070 ************************************************************************/
2071
2072static void processNode(FILE *out, xmlTextReaderPtr reader) {
2073    const xmlChar *name, *value;
2074    int type, empty;
2075
2076    type = xmlTextReaderNodeType(reader);
2077    empty = xmlTextReaderIsEmptyElement(reader);
2078
2079    name = xmlTextReaderConstName(reader);
2080    if (name == NULL)
2081	name = BAD_CAST "--";
2082
2083    value = xmlTextReaderConstValue(reader);
2084
2085
2086    fprintf(out, "%d %d %s %d %d",
2087	    xmlTextReaderDepth(reader),
2088	    type,
2089	    name,
2090	    empty,
2091	    xmlTextReaderHasValue(reader));
2092    if (value == NULL)
2093	fprintf(out, "\n");
2094    else {
2095	fprintf(out, " %s\n", value);
2096    }
2097}
2098static int
2099streamProcessTest(const char *filename, const char *result, const char *err,
2100                  xmlTextReaderPtr reader, const char *rng) {
2101    int ret;
2102    char *temp = NULL;
2103    FILE *t = NULL;
2104
2105    if (reader == NULL)
2106        return(-1);
2107
2108    nb_tests++;
2109    if (result != NULL) {
2110	temp = resultFilename(filename, "", ".res");
2111	if (temp == NULL) {
2112	    fprintf(stderr, "Out of memory\n");
2113	    fatalError();
2114	}
2115	t = fopen(temp, "wb");
2116	if (t == NULL) {
2117	    fprintf(stderr, "Can't open temp file %s\n", temp);
2118	    free(temp);
2119	    return(-1);
2120	}
2121    }
2122#ifdef LIBXML_SCHEMAS_ENABLED
2123    if (rng != NULL) {
2124	ret = xmlTextReaderRelaxNGValidate(reader, rng);
2125	if (ret < 0) {
2126	    testErrorHandler(NULL, "Relax-NG schema %s failed to compile\n",
2127	                     rng);
2128	    fclose(t);
2129            if (temp != NULL) {
2130                unlink(temp);
2131                free(temp);
2132            }
2133	    return(0);
2134	}
2135    }
2136#endif
2137    xmlGetWarningsDefaultValue = 1;
2138    ret = xmlTextReaderRead(reader);
2139    while (ret == 1) {
2140	if ((t != NULL) && (rng == NULL))
2141	    processNode(t, reader);
2142        ret = xmlTextReaderRead(reader);
2143    }
2144    if (ret != 0) {
2145        testErrorHandler(NULL, "%s : failed to parse\n", filename);
2146    }
2147    if (rng != NULL) {
2148        if (xmlTextReaderIsValid(reader) != 1) {
2149	    testErrorHandler(NULL, "%s fails to validate\n", filename);
2150	} else {
2151	    testErrorHandler(NULL, "%s validates\n", filename);
2152	}
2153    }
2154    xmlGetWarningsDefaultValue = 0;
2155    if (t != NULL) {
2156        fclose(t);
2157	ret = compareFiles(temp, result);
2158        if (temp != NULL) {
2159            unlink(temp);
2160            free(temp);
2161        }
2162	if (ret) {
2163	    fprintf(stderr, "Result for %s failed\n", filename);
2164	    return(-1);
2165	}
2166    }
2167    if (err != NULL) {
2168	ret = compareFileMem(err, testErrors, testErrorsSize);
2169	if (ret != 0) {
2170	    fprintf(stderr, "Error for %s failed\n", filename);
2171	    printf("%s", testErrors);
2172	    return(-1);
2173	}
2174    }
2175
2176    return(0);
2177}
2178
2179/**
2180 * streamParseTest:
2181 * @filename: the file to parse
2182 * @result: the file with expected result
2183 * @err: the file with error messages
2184 *
2185 * Parse a file using the reader API and check for errors.
2186 *
2187 * Returns 0 in case of success, an error code otherwise
2188 */
2189static int
2190streamParseTest(const char *filename, const char *result, const char *err,
2191                int options) {
2192    xmlTextReaderPtr reader;
2193    int ret;
2194
2195    reader = xmlReaderForFile(filename, NULL, options);
2196    ret = streamProcessTest(filename, result, err, reader, NULL);
2197    xmlFreeTextReader(reader);
2198    return(ret);
2199}
2200
2201/**
2202 * walkerParseTest:
2203 * @filename: the file to parse
2204 * @result: the file with expected result
2205 * @err: the file with error messages
2206 *
2207 * Parse a file using the walker, i.e. a reader built from a atree.
2208 *
2209 * Returns 0 in case of success, an error code otherwise
2210 */
2211static int
2212walkerParseTest(const char *filename, const char *result, const char *err,
2213                int options) {
2214    xmlDocPtr doc;
2215    xmlTextReaderPtr reader;
2216    int ret;
2217
2218    doc = xmlReadFile(filename, NULL, options);
2219    if (doc == NULL) {
2220        fprintf(stderr, "Failed to parse %s\n", filename);
2221	return(-1);
2222    }
2223    reader = xmlReaderWalker(doc);
2224    ret = streamProcessTest(filename, result, err, reader, NULL);
2225    xmlFreeTextReader(reader);
2226    xmlFreeDoc(doc);
2227    return(ret);
2228}
2229
2230/**
2231 * streamMemParseTest:
2232 * @filename: the file to parse
2233 * @result: the file with expected result
2234 * @err: the file with error messages
2235 *
2236 * Parse a file using the reader API from memory and check for errors.
2237 *
2238 * Returns 0 in case of success, an error code otherwise
2239 */
2240static int
2241streamMemParseTest(const char *filename, const char *result, const char *err,
2242                   int options) {
2243    xmlTextReaderPtr reader;
2244    int ret;
2245    const char *base;
2246    int size;
2247
2248    /*
2249     * load and parse the memory
2250     */
2251    if (loadMem(filename, &base, &size) != 0) {
2252        fprintf(stderr, "Failed to load %s\n", filename);
2253	return(-1);
2254    }
2255    reader = xmlReaderForMemory(base, size, filename, NULL, options);
2256    ret = streamProcessTest(filename, result, err, reader, NULL);
2257    free((char *)base);
2258    xmlFreeTextReader(reader);
2259    return(ret);
2260}
2261#endif
2262
2263#ifdef LIBXML_XPATH_ENABLED
2264#ifdef LIBXML_DEBUG_ENABLED
2265/************************************************************************
2266 *									*
2267 *		XPath and XPointer based tests				*
2268 *									*
2269 ************************************************************************/
2270
2271static FILE *xpathOutput;
2272static xmlDocPtr xpathDocument;
2273
2274static void
2275testXPath(const char *str, int xptr, int expr) {
2276    xmlXPathObjectPtr res;
2277    xmlXPathContextPtr ctxt;
2278
2279    nb_tests++;
2280#if defined(LIBXML_XPTR_ENABLED)
2281    if (xptr) {
2282	ctxt = xmlXPtrNewContext(xpathDocument, NULL, NULL);
2283	res = xmlXPtrEval(BAD_CAST str, ctxt);
2284    } else {
2285#endif
2286	ctxt = xmlXPathNewContext(xpathDocument);
2287	ctxt->node = xmlDocGetRootElement(xpathDocument);
2288	if (expr)
2289	    res = xmlXPathEvalExpression(BAD_CAST str, ctxt);
2290	else {
2291	    /* res = xmlXPathEval(BAD_CAST str, ctxt); */
2292	    xmlXPathCompExprPtr comp;
2293
2294	    comp = xmlXPathCompile(BAD_CAST str);
2295	    if (comp != NULL) {
2296		res = xmlXPathCompiledEval(comp, ctxt);
2297		xmlXPathFreeCompExpr(comp);
2298	    } else
2299		res = NULL;
2300	}
2301#if defined(LIBXML_XPTR_ENABLED)
2302    }
2303#endif
2304    xmlXPathDebugDumpObject(xpathOutput, res, 0);
2305    xmlXPathFreeObject(res);
2306    xmlXPathFreeContext(ctxt);
2307}
2308
2309/**
2310 * xpathExprTest:
2311 * @filename: the file to parse
2312 * @result: the file with expected result
2313 * @err: the file with error messages
2314 *
2315 * Parse a file containing XPath standalone expressions and evaluate them
2316 *
2317 * Returns 0 in case of success, an error code otherwise
2318 */
2319static int
2320xpathCommonTest(const char *filename, const char *result,
2321                int xptr, int expr) {
2322    FILE *input;
2323    char expression[5000];
2324    int len, ret = 0;
2325    char *temp;
2326
2327    temp = resultFilename(filename, "", ".res");
2328    if (temp == NULL) {
2329        fprintf(stderr, "Out of memory\n");
2330        fatalError();
2331    }
2332    xpathOutput = fopen(temp, "wb");
2333    if (xpathOutput == NULL) {
2334	fprintf(stderr, "failed to open output file %s\n", temp);
2335        free(temp);
2336	return(-1);
2337    }
2338
2339    input = fopen(filename, "rb");
2340    if (input == NULL) {
2341        xmlGenericError(xmlGenericErrorContext,
2342		"Cannot open %s for reading\n", filename);
2343        free(temp);
2344	return(-1);
2345    }
2346    while (fgets(expression, 4500, input) != NULL) {
2347	len = strlen(expression);
2348	len--;
2349	while ((len >= 0) &&
2350	       ((expression[len] == '\n') || (expression[len] == '\t') ||
2351		(expression[len] == '\r') || (expression[len] == ' '))) len--;
2352	expression[len + 1] = 0;
2353	if (len >= 0) {
2354	    fprintf(xpathOutput,
2355	            "\n========================\nExpression: %s\n",
2356		    expression) ;
2357	    testXPath(expression, xptr, expr);
2358	}
2359    }
2360
2361    fclose(input);
2362    fclose(xpathOutput);
2363    if (result != NULL) {
2364	ret = compareFiles(temp, result);
2365	if (ret) {
2366	    fprintf(stderr, "Result for %s failed\n", filename);
2367	}
2368    }
2369
2370    if (temp != NULL) {
2371        unlink(temp);
2372        free(temp);
2373    }
2374    return(ret);
2375}
2376
2377/**
2378 * xpathExprTest:
2379 * @filename: the file to parse
2380 * @result: the file with expected result
2381 * @err: the file with error messages
2382 *
2383 * Parse a file containing XPath standalone expressions and evaluate them
2384 *
2385 * Returns 0 in case of success, an error code otherwise
2386 */
2387static int
2388xpathExprTest(const char *filename, const char *result,
2389              const char *err ATTRIBUTE_UNUSED,
2390              int options ATTRIBUTE_UNUSED) {
2391    return(xpathCommonTest(filename, result, 0, 1));
2392}
2393
2394/**
2395 * xpathDocTest:
2396 * @filename: the file to parse
2397 * @result: the file with expected result
2398 * @err: the file with error messages
2399 *
2400 * Parse a file containing XPath expressions and evaluate them against
2401 * a set of corresponding documents.
2402 *
2403 * Returns 0 in case of success, an error code otherwise
2404 */
2405static int
2406xpathDocTest(const char *filename,
2407             const char *resul ATTRIBUTE_UNUSED,
2408             const char *err ATTRIBUTE_UNUSED,
2409             int options) {
2410
2411    char pattern[500];
2412    char result[500];
2413    glob_t globbuf;
2414    size_t i;
2415    int ret = 0, res;
2416
2417    xpathDocument = xmlReadFile(filename, NULL,
2418                                options | XML_PARSE_DTDATTR | XML_PARSE_NOENT);
2419    if (xpathDocument == NULL) {
2420        fprintf(stderr, "Failed to load %s\n", filename);
2421	return(-1);
2422    }
2423
2424    snprintf(pattern, 499, "./test/XPath/tests/%s*", baseFilename(filename));
2425    pattern[499] = 0;
2426    globbuf.gl_offs = 0;
2427    glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
2428    for (i = 0;i < globbuf.gl_pathc;i++) {
2429        snprintf(result, 499, "result/XPath/tests/%s",
2430	         baseFilename(globbuf.gl_pathv[i]));
2431	res = xpathCommonTest(globbuf.gl_pathv[i], &result[0], 0, 0);
2432	if (res != 0)
2433	    ret = res;
2434    }
2435    globfree(&globbuf);
2436
2437    xmlFreeDoc(xpathDocument);
2438    return(ret);
2439}
2440
2441#ifdef LIBXML_XPTR_ENABLED
2442/**
2443 * xptrDocTest:
2444 * @filename: the file to parse
2445 * @result: the file with expected result
2446 * @err: the file with error messages
2447 *
2448 * Parse a file containing XPath expressions and evaluate them against
2449 * a set of corresponding documents.
2450 *
2451 * Returns 0 in case of success, an error code otherwise
2452 */
2453static int
2454xptrDocTest(const char *filename,
2455            const char *resul ATTRIBUTE_UNUSED,
2456            const char *err ATTRIBUTE_UNUSED,
2457            int options) {
2458
2459    char pattern[500];
2460    char result[500];
2461    glob_t globbuf;
2462    size_t i;
2463    int ret = 0, res;
2464
2465    xpathDocument = xmlReadFile(filename, NULL,
2466                                options | XML_PARSE_DTDATTR | XML_PARSE_NOENT);
2467    if (xpathDocument == NULL) {
2468        fprintf(stderr, "Failed to load %s\n", filename);
2469	return(-1);
2470    }
2471
2472    snprintf(pattern, 499, "./test/XPath/xptr/%s*", baseFilename(filename));
2473    pattern[499] = 0;
2474    globbuf.gl_offs = 0;
2475    glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
2476    for (i = 0;i < globbuf.gl_pathc;i++) {
2477        snprintf(result, 499, "result/XPath/xptr/%s",
2478	         baseFilename(globbuf.gl_pathv[i]));
2479	res = xpathCommonTest(globbuf.gl_pathv[i], &result[0], 1, 0);
2480	if (res != 0)
2481	    ret = res;
2482    }
2483    globfree(&globbuf);
2484
2485    xmlFreeDoc(xpathDocument);
2486    return(ret);
2487}
2488#endif /* LIBXML_XPTR_ENABLED */
2489
2490/**
2491 * xmlidDocTest:
2492 * @filename: the file to parse
2493 * @result: the file with expected result
2494 * @err: the file with error messages
2495 *
2496 * Parse a file containing xml:id and check for errors and verify
2497 * that XPath queries will work on them as expected.
2498 *
2499 * Returns 0 in case of success, an error code otherwise
2500 */
2501static int
2502xmlidDocTest(const char *filename,
2503             const char *result,
2504             const char *err,
2505             int options) {
2506
2507    int res = 0;
2508    int ret = 0;
2509    char *temp;
2510
2511    xpathDocument = xmlReadFile(filename, NULL,
2512                                options | XML_PARSE_DTDATTR | XML_PARSE_NOENT);
2513    if (xpathDocument == NULL) {
2514        fprintf(stderr, "Failed to load %s\n", filename);
2515	return(-1);
2516    }
2517
2518    temp = resultFilename(filename, "", ".res");
2519    if (temp == NULL) {
2520        fprintf(stderr, "Out of memory\n");
2521        fatalError();
2522    }
2523    xpathOutput = fopen(temp, "wb");
2524    if (xpathOutput == NULL) {
2525	fprintf(stderr, "failed to open output file %s\n", temp);
2526        xmlFreeDoc(xpathDocument);
2527        free(temp);
2528	return(-1);
2529    }
2530
2531    testXPath("id('bar')", 0, 0);
2532
2533    fclose(xpathOutput);
2534    if (result != NULL) {
2535	ret = compareFiles(temp, result);
2536	if (ret) {
2537	    fprintf(stderr, "Result for %s failed\n", filename);
2538	    res = 1;
2539	}
2540    }
2541
2542    if (temp != NULL) {
2543        unlink(temp);
2544        free(temp);
2545    }
2546    xmlFreeDoc(xpathDocument);
2547
2548    if (err != NULL) {
2549	ret = compareFileMem(err, testErrors, testErrorsSize);
2550	if (ret != 0) {
2551	    fprintf(stderr, "Error for %s failed\n", filename);
2552	    res = 1;
2553	}
2554    }
2555    return(res);
2556}
2557
2558#endif /* LIBXML_DEBUG_ENABLED */
2559#endif /* XPATH */
2560/************************************************************************
2561 *									*
2562 *			URI based tests					*
2563 *									*
2564 ************************************************************************/
2565
2566static void
2567handleURI(const char *str, const char *base, FILE *o) {
2568    int ret;
2569    xmlURIPtr uri;
2570    xmlChar *res = NULL;
2571
2572    uri = xmlCreateURI();
2573
2574    if (base == NULL) {
2575	ret = xmlParseURIReference(uri, str);
2576	if (ret != 0)
2577	    fprintf(o, "%s : error %d\n", str, ret);
2578	else {
2579	    xmlNormalizeURIPath(uri->path);
2580	    xmlPrintURI(o, uri);
2581	    fprintf(o, "\n");
2582	}
2583    } else {
2584	res = xmlBuildURI((xmlChar *)str, (xmlChar *) base);
2585	if (res != NULL) {
2586	    fprintf(o, "%s\n", (char *) res);
2587	}
2588	else
2589	    fprintf(o, "::ERROR::\n");
2590    }
2591    if (res != NULL)
2592	xmlFree(res);
2593    xmlFreeURI(uri);
2594}
2595
2596/**
2597 * uriCommonTest:
2598 * @filename: the file to parse
2599 * @result: the file with expected result
2600 * @err: the file with error messages
2601 *
2602 * Parse a file containing URI and check for errors
2603 *
2604 * Returns 0 in case of success, an error code otherwise
2605 */
2606static int
2607uriCommonTest(const char *filename,
2608             const char *result,
2609             const char *err,
2610             const char *base) {
2611    char *temp;
2612    FILE *o, *f;
2613    char str[1024];
2614    int res = 0, i, ret;
2615
2616    temp = resultFilename(filename, "", ".res");
2617    if (temp == NULL) {
2618        fprintf(stderr, "Out of memory\n");
2619        fatalError();
2620    }
2621    o = fopen(temp, "wb");
2622    if (o == NULL) {
2623	fprintf(stderr, "failed to open output file %s\n", temp);
2624        free(temp);
2625	return(-1);
2626    }
2627    f = fopen(filename, "rb");
2628    if (f == NULL) {
2629	fprintf(stderr, "failed to open input file %s\n", filename);
2630	fclose(o);
2631        if (temp != NULL) {
2632            unlink(temp);
2633            free(temp);
2634        }
2635	return(-1);
2636    }
2637
2638    while (1) {
2639	/*
2640	 * read one line in string buffer.
2641	 */
2642	if (fgets (&str[0], sizeof (str) - 1, f) == NULL)
2643	   break;
2644
2645	/*
2646	 * remove the ending spaces
2647	 */
2648	i = strlen(str);
2649	while ((i > 0) &&
2650	       ((str[i - 1] == '\n') || (str[i - 1] == '\r') ||
2651		(str[i - 1] == ' ') || (str[i - 1] == '\t'))) {
2652	    i--;
2653	    str[i] = 0;
2654	}
2655	nb_tests++;
2656	handleURI(str, base, o);
2657    }
2658
2659    fclose(f);
2660    fclose(o);
2661
2662    if (result != NULL) {
2663	ret = compareFiles(temp, result);
2664	if (ret) {
2665	    fprintf(stderr, "Result for %s failed\n", filename);
2666	    res = 1;
2667	}
2668    }
2669    if (err != NULL) {
2670	ret = compareFileMem(err, testErrors, testErrorsSize);
2671	if (ret != 0) {
2672	    fprintf(stderr, "Error for %s failed\n", filename);
2673	    res = 1;
2674	}
2675    }
2676
2677    if (temp != NULL) {
2678        unlink(temp);
2679        free(temp);
2680    }
2681    return(res);
2682}
2683
2684/**
2685 * uriParseTest:
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 and check for errors
2691 *
2692 * Returns 0 in case of success, an error code otherwise
2693 */
2694static int
2695uriParseTest(const char *filename,
2696             const char *result,
2697             const char *err,
2698             int options ATTRIBUTE_UNUSED) {
2699    return(uriCommonTest(filename, result, err, NULL));
2700}
2701
2702/**
2703 * uriBaseTest:
2704 * @filename: the file to parse
2705 * @result: the file with expected result
2706 * @err: the file with error messages
2707 *
2708 * Parse a file containing URI, compose them against a fixed base and
2709 * check for errors
2710 *
2711 * Returns 0 in case of success, an error code otherwise
2712 */
2713static int
2714uriBaseTest(const char *filename,
2715             const char *result,
2716             const char *err,
2717             int options ATTRIBUTE_UNUSED) {
2718    return(uriCommonTest(filename, result, err,
2719                         "http://foo.com/path/to/index.html?orig#help"));
2720}
2721
2722static int urip_success = 1;
2723static int urip_current = 0;
2724static const char *urip_testURLs[] = {
2725    "urip://example.com/a b.html",
2726    "urip://example.com/a%20b.html",
2727    "file:///path/to/a b.html",
2728    "file:///path/to/a%20b.html",
2729    "/path/to/a b.html",
2730    "/path/to/a%20b.html",
2731    "urip://example.com/r�sum�.html",
2732    "urip://example.com/test?a=1&b=2%263&c=4#foo",
2733    NULL
2734};
2735static const char *urip_rcvsURLs[] = {
2736    /* it is an URI the strings must be escaped */
2737    "urip://example.com/a%20b.html",
2738    /* check that % escaping is not broken */
2739    "urip://example.com/a%20b.html",
2740    /* it's an URI path the strings must be escaped */
2741    "file:///path/to/a%20b.html",
2742    /* check that % escaping is not broken */
2743    "file:///path/to/a%20b.html",
2744    /* this is not an URI, this is a path, so this should not be escaped */
2745    "/path/to/a b.html",
2746    /* check that paths with % are not broken */
2747    "/path/to/a%20b.html",
2748    /* out of context the encoding can't be guessed byte by byte conversion */
2749    "urip://example.com/r%E9sum%E9.html",
2750    /* verify we don't destroy URIs especially the query part */
2751    "urip://example.com/test?a=1&b=2%263&c=4#foo",
2752    NULL
2753};
2754static const char *urip_res = "<list/>";
2755static const char *urip_cur = NULL;
2756static int urip_rlen;
2757
2758/**
2759 * uripMatch:
2760 * @URI: an URI to test
2761 *
2762 * Check for an urip: query
2763 *
2764 * Returns 1 if yes and 0 if another Input module should be used
2765 */
2766static int
2767uripMatch(const char * URI) {
2768    if ((URI == NULL) || (!strcmp(URI, "file:///etc/xml/catalog")))
2769        return(0);
2770    /* Verify we received the escaped URL */
2771    if (strcmp(urip_rcvsURLs[urip_current], URI))
2772	urip_success = 0;
2773    return(1);
2774}
2775
2776/**
2777 * uripOpen:
2778 * @URI: an URI to test
2779 *
2780 * Return a pointer to the urip: query handler, in this example simply
2781 * the urip_current pointer...
2782 *
2783 * Returns an Input context or NULL in case or error
2784 */
2785static void *
2786uripOpen(const char * URI) {
2787    if ((URI == NULL) || (!strcmp(URI, "file:///etc/xml/catalog")))
2788        return(NULL);
2789    /* Verify we received the escaped URL */
2790    if (strcmp(urip_rcvsURLs[urip_current], URI))
2791	urip_success = 0;
2792    urip_cur = urip_res;
2793    urip_rlen = strlen(urip_res);
2794    return((void *) urip_cur);
2795}
2796
2797/**
2798 * uripClose:
2799 * @context: the read context
2800 *
2801 * Close the urip: query handler
2802 *
2803 * Returns 0 or -1 in case of error
2804 */
2805static int
2806uripClose(void * context) {
2807    if (context == NULL) return(-1);
2808    urip_cur = NULL;
2809    urip_rlen = 0;
2810    return(0);
2811}
2812
2813/**
2814 * uripRead:
2815 * @context: the read context
2816 * @buffer: where to store data
2817 * @len: number of bytes to read
2818 *
2819 * Implement an urip: query read.
2820 *
2821 * Returns the number of bytes read or -1 in case of error
2822 */
2823static int
2824uripRead(void * context, char * buffer, int len) {
2825   const char *ptr = (const char *) context;
2826
2827   if ((context == NULL) || (buffer == NULL) || (len < 0))
2828       return(-1);
2829
2830   if (len > urip_rlen) len = urip_rlen;
2831   memcpy(buffer, ptr, len);
2832   urip_rlen -= len;
2833   return(len);
2834}
2835
2836static int
2837urip_checkURL(const char *URL) {
2838    xmlDocPtr doc;
2839
2840    doc = xmlReadFile(URL, NULL, 0);
2841    if (doc == NULL)
2842        return(-1);
2843    xmlFreeDoc(doc);
2844    return(1);
2845}
2846
2847/**
2848 * uriPathTest:
2849 * @filename: ignored
2850 * @result: ignored
2851 * @err: ignored
2852 *
2853 * Run a set of tests to check how Path and URI are handled before
2854 * being passed to the I/O layer
2855 *
2856 * Returns 0 in case of success, an error code otherwise
2857 */
2858static int
2859uriPathTest(const char *filename ATTRIBUTE_UNUSED,
2860             const char *result ATTRIBUTE_UNUSED,
2861             const char *err ATTRIBUTE_UNUSED,
2862             int options ATTRIBUTE_UNUSED) {
2863    int parsed;
2864    int failures = 0;
2865
2866    /*
2867     * register the new I/O handlers
2868     */
2869    if (xmlRegisterInputCallbacks(uripMatch, uripOpen, uripRead, uripClose) < 0)
2870    {
2871        fprintf(stderr, "failed to register HTTP handler\n");
2872	return(-1);
2873    }
2874
2875    for (urip_current = 0;urip_testURLs[urip_current] != NULL;urip_current++) {
2876        urip_success = 1;
2877        parsed = urip_checkURL(urip_testURLs[urip_current]);
2878	if (urip_success != 1) {
2879	    fprintf(stderr, "failed the URL passing test for %s",
2880	            urip_testURLs[urip_current]);
2881	    failures++;
2882	} else if (parsed != 1) {
2883	    fprintf(stderr, "failed the parsing test for %s",
2884	            urip_testURLs[urip_current]);
2885	    failures++;
2886	}
2887	nb_tests++;
2888    }
2889
2890    xmlPopInputCallbacks();
2891    return(failures);
2892}
2893
2894#ifdef LIBXML_SCHEMAS_ENABLED
2895/************************************************************************
2896 *									*
2897 *			Schemas tests					*
2898 *									*
2899 ************************************************************************/
2900static int
2901schemasOneTest(const char *sch,
2902               const char *filename,
2903               const char *result,
2904	       const char *err,
2905	       int options,
2906	       xmlSchemaPtr schemas) {
2907    xmlDocPtr doc;
2908    xmlSchemaValidCtxtPtr ctxt;
2909    int ret = 0;
2910    int validResult = 0;
2911    char *temp;
2912    FILE *schemasOutput;
2913
2914    doc = xmlReadFile(filename, NULL, options);
2915    if (doc == NULL) {
2916        fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch);
2917	return(-1);
2918    }
2919
2920    temp = resultFilename(result, "", ".res");
2921    if (temp == NULL) {
2922        fprintf(stderr, "Out of memory\n");
2923        fatalError();
2924    }
2925    schemasOutput = fopen(temp, "wb");
2926    if (schemasOutput == NULL) {
2927	fprintf(stderr, "failed to open output file %s\n", temp);
2928	xmlFreeDoc(doc);
2929        free(temp);
2930	return(-1);
2931    }
2932
2933    ctxt = xmlSchemaNewValidCtxt(schemas);
2934    xmlSchemaSetValidErrors(ctxt,
2935         (xmlSchemaValidityErrorFunc) testErrorHandler,
2936         (xmlSchemaValidityWarningFunc) testErrorHandler,
2937	 ctxt);
2938    validResult = xmlSchemaValidateDoc(ctxt, doc);
2939    if (validResult == 0) {
2940	fprintf(schemasOutput, "%s validates\n", filename);
2941    } else if (validResult > 0) {
2942	fprintf(schemasOutput, "%s fails to validate\n", filename);
2943    } else {
2944	fprintf(schemasOutput, "%s validation generated an internal error\n",
2945	       filename);
2946    }
2947    fclose(schemasOutput);
2948    if (result) {
2949	if (compareFiles(temp, result)) {
2950	    fprintf(stderr, "Result for %s on %s failed\n", filename, sch);
2951	    ret = 1;
2952	}
2953    }
2954    if (temp != NULL) {
2955        unlink(temp);
2956        free(temp);
2957    }
2958
2959    if ((validResult != 0) && (err != NULL)) {
2960	if (compareFileMem(err, testErrors, testErrorsSize)) {
2961	    fprintf(stderr, "Error for %s on %s failed\n", filename, sch);
2962	    ret = 1;
2963	}
2964    }
2965
2966    xmlSchemaFreeValidCtxt(ctxt);
2967    xmlFreeDoc(doc);
2968    return(ret);
2969}
2970/**
2971 * schemasTest:
2972 * @filename: the schemas file
2973 * @result: the file with expected result
2974 * @err: the file with error messages
2975 *
2976 * Parse a file containing URI, compose them against a fixed base and
2977 * check for errors
2978 *
2979 * Returns 0 in case of success, an error code otherwise
2980 */
2981static int
2982schemasTest(const char *filename,
2983            const char *resul ATTRIBUTE_UNUSED,
2984            const char *errr ATTRIBUTE_UNUSED,
2985            int options) {
2986    const char *base = baseFilename(filename);
2987    const char *base2;
2988    const char *instance;
2989    xmlSchemaParserCtxtPtr ctxt;
2990    xmlSchemaPtr schemas;
2991    int res = 0, len, ret;
2992    char pattern[500];
2993    char prefix[500];
2994    char result[500];
2995    char err[500];
2996    glob_t globbuf;
2997    size_t i;
2998    char count = 0;
2999
3000    /* first compile the schemas if possible */
3001    ctxt = xmlSchemaNewParserCtxt(filename);
3002    xmlSchemaSetParserErrors(ctxt,
3003         (xmlSchemaValidityErrorFunc) testErrorHandler,
3004         (xmlSchemaValidityWarningFunc) testErrorHandler,
3005	 ctxt);
3006    schemas = xmlSchemaParse(ctxt);
3007    xmlSchemaFreeParserCtxt(ctxt);
3008
3009    /*
3010     * most of the mess is about the output filenames generated by the Makefile
3011     */
3012    len = strlen(base);
3013    if ((len > 499) || (len < 5)) {
3014        xmlSchemaFree(schemas);
3015	return(-1);
3016    }
3017    len -= 4; /* remove trailing .xsd */
3018    if (base[len - 2] == '_') {
3019        len -= 2; /* remove subtest number */
3020    }
3021    if (base[len - 2] == '_') {
3022        len -= 2; /* remove subtest number */
3023    }
3024    memcpy(prefix, base, len);
3025    prefix[len] = 0;
3026
3027    snprintf(pattern, 499, "./test/schemas/%s_?.xml", prefix);
3028    pattern[499] = 0;
3029
3030    if (base[len] == '_') {
3031        len += 2;
3032	memcpy(prefix, base, len);
3033	prefix[len] = 0;
3034    }
3035
3036    globbuf.gl_offs = 0;
3037    glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
3038    for (i = 0;i < globbuf.gl_pathc;i++) {
3039        testErrorsSize = 0;
3040	testErrors[0] = 0;
3041        instance = globbuf.gl_pathv[i];
3042	base2 = baseFilename(instance);
3043	len = strlen(base2);
3044	if ((len > 6) && (base2[len - 6] == '_')) {
3045	    count = base2[len - 5];
3046	    snprintf(result, 499, "result/schemas/%s_%c",
3047		     prefix, count);
3048	    result[499] = 0;
3049	    snprintf(err, 499, "result/schemas/%s_%c.err",
3050		     prefix, count);
3051	    err[499] = 0;
3052	} else {
3053	    fprintf(stderr, "don't know how to process %s\n", instance);
3054	    continue;
3055	}
3056	if (schemas == NULL) {
3057	} else {
3058	    nb_tests++;
3059	    ret = schemasOneTest(filename, instance, result, err,
3060	                         options, schemas);
3061	    if (ret != 0)
3062		res = ret;
3063	}
3064    }
3065    globfree(&globbuf);
3066    xmlSchemaFree(schemas);
3067
3068    return(res);
3069}
3070
3071/************************************************************************
3072 *									*
3073 *			Schemas tests					*
3074 *									*
3075 ************************************************************************/
3076static int
3077rngOneTest(const char *sch,
3078               const char *filename,
3079               const char *result,
3080	       const char *err,
3081	       int options,
3082	       xmlRelaxNGPtr schemas) {
3083    xmlDocPtr doc;
3084    xmlRelaxNGValidCtxtPtr ctxt;
3085    int ret = 0;
3086    char *temp;
3087    FILE *schemasOutput;
3088
3089    doc = xmlReadFile(filename, NULL, options);
3090    if (doc == NULL) {
3091        fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch);
3092	return(-1);
3093    }
3094
3095    temp = resultFilename(result, "", ".res");
3096    if (temp == NULL) {
3097        fprintf(stderr, "Out of memory\n");
3098        fatalError();
3099    }
3100    schemasOutput = fopen(temp, "wb");
3101    if (schemasOutput == NULL) {
3102	fprintf(stderr, "failed to open output file %s\n", temp);
3103	xmlFreeDoc(doc);
3104        free(temp);
3105	return(-1);
3106    }
3107
3108    ctxt = xmlRelaxNGNewValidCtxt(schemas);
3109    xmlRelaxNGSetValidErrors(ctxt,
3110         (xmlRelaxNGValidityErrorFunc) testErrorHandler,
3111         (xmlRelaxNGValidityWarningFunc) testErrorHandler,
3112	 ctxt);
3113    ret = xmlRelaxNGValidateDoc(ctxt, doc);
3114    if (ret == 0) {
3115	testErrorHandler(NULL, "%s validates\n", filename);
3116    } else if (ret > 0) {
3117	testErrorHandler(NULL, "%s fails to validate\n", filename);
3118    } else {
3119	testErrorHandler(NULL, "%s validation generated an internal error\n",
3120	       filename);
3121    }
3122    fclose(schemasOutput);
3123    ret = 0;
3124    if (result) {
3125	if (compareFiles(temp, result)) {
3126	    fprintf(stderr, "Result for %s on %s failed\n", filename, sch);
3127	    ret = 1;
3128	}
3129    }
3130    if (temp != NULL) {
3131        unlink(temp);
3132        free(temp);
3133    }
3134
3135    if (err != NULL) {
3136	if (compareFileMem(err, testErrors, testErrorsSize)) {
3137	    fprintf(stderr, "Error for %s on %s failed\n", filename, sch);
3138	    ret = 1;
3139	    printf("%s", testErrors);
3140	}
3141    }
3142
3143
3144    xmlRelaxNGFreeValidCtxt(ctxt);
3145    xmlFreeDoc(doc);
3146    return(ret);
3147}
3148/**
3149 * rngTest:
3150 * @filename: the schemas file
3151 * @result: the file with expected result
3152 * @err: the file with error messages
3153 *
3154 * Parse an RNG schemas and then apply it to the related .xml
3155 *
3156 * Returns 0 in case of success, an error code otherwise
3157 */
3158static int
3159rngTest(const char *filename,
3160            const char *resul ATTRIBUTE_UNUSED,
3161            const char *errr ATTRIBUTE_UNUSED,
3162            int options) {
3163    const char *base = baseFilename(filename);
3164    const char *base2;
3165    const char *instance;
3166    xmlRelaxNGParserCtxtPtr ctxt;
3167    xmlRelaxNGPtr schemas;
3168    int res = 0, len, ret = 0;
3169    char pattern[500];
3170    char prefix[500];
3171    char result[500];
3172    char err[500];
3173    glob_t globbuf;
3174    size_t i;
3175    char count = 0;
3176
3177    /* first compile the schemas if possible */
3178    ctxt = xmlRelaxNGNewParserCtxt(filename);
3179    xmlRelaxNGSetParserErrors(ctxt,
3180         (xmlRelaxNGValidityErrorFunc) testErrorHandler,
3181         (xmlRelaxNGValidityWarningFunc) testErrorHandler,
3182	 ctxt);
3183    schemas = xmlRelaxNGParse(ctxt);
3184    xmlRelaxNGFreeParserCtxt(ctxt);
3185
3186    /*
3187     * most of the mess is about the output filenames generated by the Makefile
3188     */
3189    len = strlen(base);
3190    if ((len > 499) || (len < 5)) {
3191        xmlRelaxNGFree(schemas);
3192	return(-1);
3193    }
3194    len -= 4; /* remove trailing .rng */
3195    memcpy(prefix, base, len);
3196    prefix[len] = 0;
3197
3198    snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix);
3199    pattern[499] = 0;
3200
3201    globbuf.gl_offs = 0;
3202    glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
3203    for (i = 0;i < globbuf.gl_pathc;i++) {
3204        testErrorsSize = 0;
3205	testErrors[0] = 0;
3206        instance = globbuf.gl_pathv[i];
3207	base2 = baseFilename(instance);
3208	len = strlen(base2);
3209	if ((len > 6) && (base2[len - 6] == '_')) {
3210	    count = base2[len - 5];
3211	    snprintf(result, 499, "result/relaxng/%s_%c",
3212		     prefix, count);
3213	    result[499] = 0;
3214	    snprintf(err, 499, "result/relaxng/%s_%c.err",
3215		     prefix, count);
3216	    err[499] = 0;
3217	} else {
3218	    fprintf(stderr, "don't know how to process %s\n", instance);
3219	    continue;
3220	}
3221	if (schemas == NULL) {
3222	} else {
3223	    nb_tests++;
3224	    ret = rngOneTest(filename, instance, result, err,
3225	                         options, schemas);
3226	    if (res != 0)
3227		ret = res;
3228	}
3229    }
3230    globfree(&globbuf);
3231    xmlRelaxNGFree(schemas);
3232
3233    return(ret);
3234}
3235
3236#ifdef LIBXML_READER_ENABLED
3237/**
3238 * rngStreamTest:
3239 * @filename: the schemas file
3240 * @result: the file with expected result
3241 * @err: the file with error messages
3242 *
3243 * Parse a set of files with streaming, applying an RNG schemas
3244 *
3245 * Returns 0 in case of success, an error code otherwise
3246 */
3247static int
3248rngStreamTest(const char *filename,
3249            const char *resul ATTRIBUTE_UNUSED,
3250            const char *errr ATTRIBUTE_UNUSED,
3251            int options) {
3252    const char *base = baseFilename(filename);
3253    const char *base2;
3254    const char *instance;
3255    int res = 0, len, ret;
3256    char pattern[500];
3257    char prefix[500];
3258    char result[500];
3259    char err[500];
3260    glob_t globbuf;
3261    size_t i;
3262    char count = 0;
3263    xmlTextReaderPtr reader;
3264    int disable_err = 0;
3265
3266    /*
3267     * most of the mess is about the output filenames generated by the Makefile
3268     */
3269    len = strlen(base);
3270    if ((len > 499) || (len < 5)) {
3271	fprintf(stderr, "len(base) == %d !\n", len);
3272	return(-1);
3273    }
3274    len -= 4; /* remove trailing .rng */
3275    memcpy(prefix, base, len);
3276    prefix[len] = 0;
3277
3278    /*
3279     * strictly unifying the error messages is nearly impossible this
3280     * hack is also done in the Makefile
3281     */
3282    if ((!strcmp(prefix, "tutor10_1")) || (!strcmp(prefix, "tutor10_2")) ||
3283        (!strcmp(prefix, "tutor3_2")) || (!strcmp(prefix, "307377")) ||
3284        (!strcmp(prefix, "tutor8_2")))
3285	disable_err = 1;
3286
3287    snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix);
3288    pattern[499] = 0;
3289
3290    globbuf.gl_offs = 0;
3291    glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
3292    for (i = 0;i < globbuf.gl_pathc;i++) {
3293        testErrorsSize = 0;
3294	testErrors[0] = 0;
3295        instance = globbuf.gl_pathv[i];
3296	base2 = baseFilename(instance);
3297	len = strlen(base2);
3298	if ((len > 6) && (base2[len - 6] == '_')) {
3299	    count = base2[len - 5];
3300	    snprintf(result, 499, "result/relaxng/%s_%c",
3301		     prefix, count);
3302	    result[499] = 0;
3303	    snprintf(err, 499, "result/relaxng/%s_%c.err",
3304		     prefix, count);
3305	    err[499] = 0;
3306	} else {
3307	    fprintf(stderr, "don't know how to process %s\n", instance);
3308	    continue;
3309	}
3310	reader = xmlReaderForFile(instance, NULL, options);
3311	if (reader == NULL) {
3312	    fprintf(stderr, "Failed to build reder for %s\n", instance);
3313	}
3314	if (disable_err == 1)
3315	    ret = streamProcessTest(instance, result, NULL, reader, filename);
3316	else
3317	    ret = streamProcessTest(instance, result, err, reader, filename);
3318	xmlFreeTextReader(reader);
3319	if (ret != 0) {
3320	    fprintf(stderr, "instance %s failed\n", instance);
3321	    res = ret;
3322	}
3323    }
3324    globfree(&globbuf);
3325
3326    return(res);
3327}
3328#endif /* READER */
3329
3330#endif
3331
3332#ifdef LIBXML_PATTERN_ENABLED
3333#ifdef LIBXML_READER_ENABLED
3334/************************************************************************
3335 *									*
3336 *			Patterns tests					*
3337 *									*
3338 ************************************************************************/
3339static void patternNode(FILE *out, xmlTextReaderPtr reader,
3340                        const char *pattern, xmlPatternPtr patternc,
3341			xmlStreamCtxtPtr patstream) {
3342    xmlChar *path = NULL;
3343    int match = -1;
3344    int type, empty;
3345
3346    type = xmlTextReaderNodeType(reader);
3347    empty = xmlTextReaderIsEmptyElement(reader);
3348
3349    if (type == XML_READER_TYPE_ELEMENT) {
3350	/* do the check only on element start */
3351	match = xmlPatternMatch(patternc, xmlTextReaderCurrentNode(reader));
3352
3353	if (match) {
3354	    path = xmlGetNodePath(xmlTextReaderCurrentNode(reader));
3355	    fprintf(out, "Node %s matches pattern %s\n", path, pattern);
3356	}
3357    }
3358    if (patstream != NULL) {
3359	int ret;
3360
3361	if (type == XML_READER_TYPE_ELEMENT) {
3362	    ret = xmlStreamPush(patstream,
3363				xmlTextReaderConstLocalName(reader),
3364				xmlTextReaderConstNamespaceUri(reader));
3365	    if (ret < 0) {
3366		fprintf(out, "xmlStreamPush() failure\n");
3367		xmlFreeStreamCtxt(patstream);
3368		patstream = NULL;
3369	    } else if (ret != match) {
3370		if (path == NULL) {
3371		    path = xmlGetNodePath(
3372				   xmlTextReaderCurrentNode(reader));
3373		}
3374		fprintf(out,
3375			"xmlPatternMatch and xmlStreamPush disagree\n");
3376		fprintf(out,
3377			"  pattern %s node %s\n",
3378			pattern, path);
3379	    }
3380
3381
3382	}
3383	if ((type == XML_READER_TYPE_END_ELEMENT) ||
3384	    ((type == XML_READER_TYPE_ELEMENT) && (empty))) {
3385	    ret = xmlStreamPop(patstream);
3386	    if (ret < 0) {
3387		fprintf(out, "xmlStreamPop() failure\n");
3388		xmlFreeStreamCtxt(patstream);
3389		patstream = NULL;
3390	    }
3391	}
3392    }
3393    if (path != NULL)
3394	xmlFree(path);
3395}
3396
3397/**
3398 * patternTest:
3399 * @filename: the schemas file
3400 * @result: the file with expected result
3401 * @err: the file with error messages
3402 *
3403 * Parse a set of files with streaming, applying an RNG schemas
3404 *
3405 * Returns 0 in case of success, an error code otherwise
3406 */
3407static int
3408patternTest(const char *filename,
3409            const char *resul ATTRIBUTE_UNUSED,
3410            const char *err ATTRIBUTE_UNUSED,
3411            int options) {
3412    xmlPatternPtr patternc = NULL;
3413    xmlStreamCtxtPtr patstream = NULL;
3414    FILE *o, *f;
3415    char str[1024];
3416    char xml[500];
3417    char result[500];
3418    int len, i;
3419    int ret = 0, res;
3420    char *temp;
3421    xmlTextReaderPtr reader;
3422    xmlDocPtr doc;
3423
3424    len = strlen(filename);
3425    len -= 4;
3426    memcpy(xml, filename, len);
3427    xml[len] = 0;
3428    snprintf(result, 499, "result/pattern/%s", baseFilename(xml));
3429    result[499] = 0;
3430    memcpy(xml + len, ".xml", 5);
3431
3432    if (!checkTestFile(xml)) {
3433	fprintf(stderr, "Missing xml file %s\n", xml);
3434	return(-1);
3435    }
3436    if (!checkTestFile(result)) {
3437	fprintf(stderr, "Missing result file %s\n", result);
3438	return(-1);
3439    }
3440    f = fopen(filename, "rb");
3441    if (f == NULL) {
3442        fprintf(stderr, "Failed to open %s\n", filename);
3443	return(-1);
3444    }
3445    temp = resultFilename(filename, "", ".res");
3446    if (temp == NULL) {
3447        fprintf(stderr, "Out of memory\n");
3448        fatalError();
3449    }
3450    o = fopen(temp, "wb");
3451    if (o == NULL) {
3452	fprintf(stderr, "failed to open output file %s\n", temp);
3453	fclose(f);
3454        free(temp);
3455	return(-1);
3456    }
3457    while (1) {
3458	/*
3459	 * read one line in string buffer.
3460	 */
3461	if (fgets (&str[0], sizeof (str) - 1, f) == NULL)
3462	   break;
3463
3464	/*
3465	 * remove the ending spaces
3466	 */
3467	i = strlen(str);
3468	while ((i > 0) &&
3469	       ((str[i - 1] == '\n') || (str[i - 1] == '\r') ||
3470		(str[i - 1] == ' ') || (str[i - 1] == '\t'))) {
3471	    i--;
3472	    str[i] = 0;
3473	}
3474	doc = xmlReadFile(xml, NULL, options);
3475	if (doc == NULL) {
3476	    fprintf(stderr, "Failed to parse %s\n", xml);
3477	    ret = 1;
3478	} else {
3479	    xmlNodePtr root;
3480	    const xmlChar *namespaces[22];
3481	    int j;
3482	    xmlNsPtr ns;
3483
3484	    root = xmlDocGetRootElement(doc);
3485	    for (ns = root->nsDef, j = 0;ns != NULL && j < 20;ns=ns->next) {
3486		namespaces[j++] = ns->href;
3487		namespaces[j++] = ns->prefix;
3488	    }
3489	    namespaces[j++] = NULL;
3490	    namespaces[j] = NULL;
3491
3492	    patternc = xmlPatterncompile((const xmlChar *) str, doc->dict,
3493					 0, &namespaces[0]);
3494	    if (patternc == NULL) {
3495		testErrorHandler(NULL,
3496			"Pattern %s failed to compile\n", str);
3497		xmlFreeDoc(doc);
3498		ret = 1;
3499		continue;
3500	    }
3501	    patstream = xmlPatternGetStreamCtxt(patternc);
3502	    if (patstream != NULL) {
3503		ret = xmlStreamPush(patstream, NULL, NULL);
3504		if (ret < 0) {
3505		    fprintf(stderr, "xmlStreamPush() failure\n");
3506		    xmlFreeStreamCtxt(patstream);
3507		    patstream = NULL;
3508		}
3509	    }
3510	    nb_tests++;
3511
3512	    reader = xmlReaderWalker(doc);
3513	    res = xmlTextReaderRead(reader);
3514	    while (res == 1) {
3515		patternNode(o, reader, str, patternc, patstream);
3516		res = xmlTextReaderRead(reader);
3517	    }
3518	    if (res != 0) {
3519		fprintf(o, "%s : failed to parse\n", filename);
3520	    }
3521	    xmlFreeTextReader(reader);
3522	    xmlFreeDoc(doc);
3523	    xmlFreeStreamCtxt(patstream);
3524	    patstream = NULL;
3525	    xmlFreePattern(patternc);
3526
3527	}
3528    }
3529
3530    fclose(f);
3531    fclose(o);
3532
3533    ret = compareFiles(temp, result);
3534    if (ret) {
3535	fprintf(stderr, "Result for %s failed\n", filename);
3536	ret = 1;
3537    }
3538    if (temp != NULL) {
3539        unlink(temp);
3540        free(temp);
3541    }
3542    return(ret);
3543}
3544#endif /* READER */
3545#endif /* PATTERN */
3546#ifdef LIBXML_C14N_ENABLED
3547/************************************************************************
3548 *									*
3549 *			Canonicalization tests				*
3550 *									*
3551 ************************************************************************/
3552static xmlXPathObjectPtr
3553load_xpath_expr (xmlDocPtr parent_doc, const char* filename) {
3554    xmlXPathObjectPtr xpath;
3555    xmlDocPtr doc;
3556    xmlChar *expr;
3557    xmlXPathContextPtr ctx;
3558    xmlNodePtr node;
3559    xmlNsPtr ns;
3560
3561    /*
3562     * load XPath expr as a file
3563     */
3564    xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
3565    xmlSubstituteEntitiesDefault(1);
3566
3567    doc = xmlReadFile(filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT);
3568    if (doc == NULL) {
3569	fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename);
3570	return(NULL);
3571    }
3572
3573    /*
3574     * Check the document is of the right kind
3575     */
3576    if(xmlDocGetRootElement(doc) == NULL) {
3577        fprintf(stderr,"Error: empty document for file \"%s\"\n", filename);
3578	xmlFreeDoc(doc);
3579	return(NULL);
3580    }
3581
3582    node = doc->children;
3583    while(node != NULL && !xmlStrEqual(node->name, (const xmlChar *)"XPath")) {
3584	node = node->next;
3585    }
3586
3587    if(node == NULL) {
3588        fprintf(stderr,"Error: XPath element expected in the file  \"%s\"\n", filename);
3589	xmlFreeDoc(doc);
3590	return(NULL);
3591    }
3592
3593    expr = xmlNodeGetContent(node);
3594    if(expr == NULL) {
3595        fprintf(stderr,"Error: XPath content element is NULL \"%s\"\n", filename);
3596	xmlFreeDoc(doc);
3597	return(NULL);
3598    }
3599
3600    ctx = xmlXPathNewContext(parent_doc);
3601    if(ctx == NULL) {
3602        fprintf(stderr,"Error: unable to create new context\n");
3603        xmlFree(expr);
3604        xmlFreeDoc(doc);
3605        return(NULL);
3606    }
3607
3608    /*
3609     * Register namespaces
3610     */
3611    ns = node->nsDef;
3612    while(ns != NULL) {
3613	if(xmlXPathRegisterNs(ctx, ns->prefix, ns->href) != 0) {
3614	    fprintf(stderr,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", ns->prefix, ns->href);
3615    xmlFree(expr);
3616	    xmlXPathFreeContext(ctx);
3617	    xmlFreeDoc(doc);
3618	    return(NULL);
3619	}
3620	ns = ns->next;
3621    }
3622
3623    /*
3624     * Evaluate xpath
3625     */
3626    xpath = xmlXPathEvalExpression(expr, ctx);
3627    if(xpath == NULL) {
3628        fprintf(stderr,"Error: unable to evaluate xpath expression\n");
3629xmlFree(expr);
3630        xmlXPathFreeContext(ctx);
3631        xmlFreeDoc(doc);
3632        return(NULL);
3633    }
3634
3635    /* print_xpath_nodes(xpath->nodesetval); */
3636
3637    xmlFree(expr);
3638    xmlXPathFreeContext(ctx);
3639    xmlFreeDoc(doc);
3640    return(xpath);
3641}
3642
3643/*
3644 * Macro used to grow the current buffer.
3645 */
3646#define xxx_growBufferReentrant() {						\
3647    buffer_size *= 2;							\
3648    buffer = (xmlChar **)						\
3649	xmlRealloc(buffer, buffer_size * sizeof(xmlChar*));	\
3650    if (buffer == NULL) {						\
3651	perror("realloc failed");					\
3652	return(NULL);							\
3653    }									\
3654}
3655
3656static xmlChar **
3657parse_list(xmlChar *str) {
3658    xmlChar **buffer;
3659    xmlChar **out = NULL;
3660    int buffer_size = 0;
3661    int len;
3662
3663    if(str == NULL) {
3664	return(NULL);
3665    }
3666
3667    len = xmlStrlen(str);
3668    if((str[0] == '\'') && (str[len - 1] == '\'')) {
3669	str[len - 1] = '\0';
3670	str++;
3671    }
3672    /*
3673     * allocate an translation buffer.
3674     */
3675    buffer_size = 1000;
3676    buffer = (xmlChar **) xmlMalloc(buffer_size * sizeof(xmlChar*));
3677    if (buffer == NULL) {
3678	perror("malloc failed");
3679	return(NULL);
3680    }
3681    out = buffer;
3682
3683    while(*str != '\0') {
3684	if (out - buffer > buffer_size - 10) {
3685	    int indx = out - buffer;
3686
3687	    xxx_growBufferReentrant();
3688	    out = &buffer[indx];
3689	}
3690	(*out++) = str;
3691	while(*str != ',' && *str != '\0') ++str;
3692	if(*str == ',') *(str++) = '\0';
3693    }
3694    (*out) = NULL;
3695    return buffer;
3696}
3697
3698static int
3699c14nRunTest(const char* xml_filename, int with_comments, int mode,
3700	    const char* xpath_filename, const char *ns_filename,
3701	    const char* result_file) {
3702    xmlDocPtr doc;
3703    xmlXPathObjectPtr xpath = NULL;
3704    xmlChar *result = NULL;
3705    int ret;
3706    xmlChar **inclusive_namespaces = NULL;
3707    const char *nslist = NULL;
3708    int nssize;
3709
3710
3711    /*
3712     * build an XML tree from a the file; we need to add default
3713     * attributes and resolve all character and entities references
3714     */
3715    xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
3716    xmlSubstituteEntitiesDefault(1);
3717
3718    doc = xmlReadFile(xml_filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT);
3719    if (doc == NULL) {
3720	fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_filename);
3721	return(-1);
3722    }
3723
3724    /*
3725     * Check the document is of the right kind
3726     */
3727    if(xmlDocGetRootElement(doc) == NULL) {
3728        fprintf(stderr,"Error: empty document for file \"%s\"\n", xml_filename);
3729	xmlFreeDoc(doc);
3730	return(-1);
3731    }
3732
3733    /*
3734     * load xpath file if specified
3735     */
3736    if(xpath_filename) {
3737	xpath = load_xpath_expr(doc, xpath_filename);
3738	if(xpath == NULL) {
3739	    fprintf(stderr,"Error: unable to evaluate xpath expression\n");
3740	    xmlFreeDoc(doc);
3741	    return(-1);
3742	}
3743    }
3744
3745    if (ns_filename != NULL) {
3746        if (loadMem(ns_filename, &nslist, &nssize)) {
3747	    fprintf(stderr,"Error: unable to evaluate xpath expression\n");
3748	    if(xpath != NULL) xmlXPathFreeObject(xpath);
3749	    xmlFreeDoc(doc);
3750	    return(-1);
3751	}
3752        inclusive_namespaces = parse_list((xmlChar *) nslist);
3753    }
3754
3755    /*
3756     * Canonical form
3757     */
3758    /* fprintf(stderr,"File \"%s\" loaded: start canonization\n", xml_filename); */
3759    ret = xmlC14NDocDumpMemory(doc,
3760	    (xpath) ? xpath->nodesetval : NULL,
3761	    mode, inclusive_namespaces,
3762	    with_comments, &result);
3763    if (ret >= 0) {
3764	if(result != NULL) {
3765	    if (compareFileMem(result_file, (const char *) result, ret)) {
3766		fprintf(stderr, "Result mismatch for %s\n", xml_filename);
3767		fprintf(stderr, "RESULT:\n%s\n", (const char*)result);
3768	        ret = -1;
3769	    }
3770	}
3771    } else {
3772	fprintf(stderr,"Error: failed to canonicalize XML file \"%s\" (ret=%d)\n", xml_filename, ret);
3773	ret = -1;
3774    }
3775
3776    /*
3777     * Cleanup
3778     */
3779    if (result != NULL) xmlFree(result);
3780    if(xpath != NULL) xmlXPathFreeObject(xpath);
3781    if (inclusive_namespaces != NULL) xmlFree(inclusive_namespaces);
3782    if (nslist != NULL) free((char *) nslist);
3783    xmlFreeDoc(doc);
3784
3785    return(ret);
3786}
3787
3788static int
3789c14nCommonTest(const char *filename, int with_comments, int mode,
3790               const char *subdir) {
3791    char buf[500];
3792    char prefix[500];
3793    const char *base;
3794    int len;
3795    char *result = NULL;
3796    char *xpath = NULL;
3797    char *ns = NULL;
3798    int ret = 0;
3799
3800    base = baseFilename(filename);
3801    len = strlen(base);
3802    len -= 4;
3803    memcpy(prefix, base, len);
3804    prefix[len] = 0;
3805
3806    snprintf(buf, 499, "result/c14n/%s/%s", subdir,prefix);
3807    if (!checkTestFile(buf)) {
3808        fprintf(stderr, "Missing result file %s", buf);
3809	return(-1);
3810    }
3811    result = strdup(buf);
3812    snprintf(buf, 499, "test/c14n/%s/%s.xpath", subdir,prefix);
3813    if (checkTestFile(buf)) {
3814	xpath = strdup(buf);
3815    }
3816    snprintf(buf, 499, "test/c14n/%s/%s.ns", subdir,prefix);
3817    if (checkTestFile(buf)) {
3818	ns = strdup(buf);
3819    }
3820
3821    nb_tests++;
3822    if (c14nRunTest(filename, with_comments, mode,
3823                    xpath, ns, result) < 0)
3824        ret = 1;
3825
3826    if (result != NULL) free(result);
3827    if (xpath != NULL) free(xpath);
3828    if (ns != NULL) free(ns);
3829    return(ret);
3830}
3831
3832static int
3833c14nWithCommentTest(const char *filename,
3834                    const char *resul ATTRIBUTE_UNUSED,
3835		    const char *err ATTRIBUTE_UNUSED,
3836		    int options ATTRIBUTE_UNUSED) {
3837    return(c14nCommonTest(filename, 1, XML_C14N_1_0, "with-comments"));
3838}
3839static int
3840c14nWithoutCommentTest(const char *filename,
3841                    const char *resul ATTRIBUTE_UNUSED,
3842		    const char *err ATTRIBUTE_UNUSED,
3843		    int options ATTRIBUTE_UNUSED) {
3844    return(c14nCommonTest(filename, 0, XML_C14N_1_0, "without-comments"));
3845}
3846static int
3847c14nExcWithoutCommentTest(const char *filename,
3848                    const char *resul ATTRIBUTE_UNUSED,
3849		    const char *err ATTRIBUTE_UNUSED,
3850		    int options ATTRIBUTE_UNUSED) {
3851    return(c14nCommonTest(filename, 0, XML_C14N_EXCLUSIVE_1_0, "exc-without-comments"));
3852}
3853static int
3854c14n11WithoutCommentTest(const char *filename,
3855                    const char *resul ATTRIBUTE_UNUSED,
3856		    const char *err ATTRIBUTE_UNUSED,
3857		    int options ATTRIBUTE_UNUSED) {
3858    return(c14nCommonTest(filename, 0, XML_C14N_1_1, "1-1-without-comments"));
3859}
3860#endif
3861#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED) && defined (LIBXML_SAX1_ENABLED)
3862/************************************************************************
3863 *									*
3864 *			Catalog and threads test			*
3865 *									*
3866 ************************************************************************/
3867
3868/*
3869 * mostly a cut and paste from testThreads.c
3870 */
3871#define	MAX_ARGC	20
3872
3873static const char *catalog = "test/threads/complex.xml";
3874static const char *testfiles[] = {
3875    "test/threads/abc.xml",
3876    "test/threads/acb.xml",
3877    "test/threads/bac.xml",
3878    "test/threads/bca.xml",
3879    "test/threads/cab.xml",
3880    "test/threads/cba.xml",
3881    "test/threads/invalid.xml",
3882};
3883
3884static const char *Okay = "OK";
3885static const char *Failed = "Failed";
3886
3887#ifndef xmlDoValidityCheckingDefaultValue
3888#error xmlDoValidityCheckingDefaultValue is not a macro
3889#endif
3890#ifndef xmlGenericErrorContext
3891#error xmlGenericErrorContext is not a macro
3892#endif
3893
3894static void *
3895thread_specific_data(void *private_data)
3896{
3897    xmlDocPtr myDoc;
3898    const char *filename = (const char *) private_data;
3899    int okay = 1;
3900
3901    if (!strcmp(filename, "test/threads/invalid.xml")) {
3902        xmlDoValidityCheckingDefaultValue = 0;
3903        xmlGenericErrorContext = stdout;
3904    } else {
3905        xmlDoValidityCheckingDefaultValue = 1;
3906        xmlGenericErrorContext = stderr;
3907    }
3908    myDoc = xmlParseFile(filename);
3909    if (myDoc) {
3910        xmlFreeDoc(myDoc);
3911    } else {
3912        printf("parse failed\n");
3913        okay = 0;
3914    }
3915    if (!strcmp(filename, "test/threads/invalid.xml")) {
3916        if (xmlDoValidityCheckingDefaultValue != 0) {
3917            printf("ValidityCheckingDefaultValue override failed\n");
3918            okay = 0;
3919        }
3920        if (xmlGenericErrorContext != stdout) {
3921            printf("xmlGenericErrorContext override failed\n");
3922            okay = 0;
3923        }
3924    } else {
3925        if (xmlDoValidityCheckingDefaultValue != 1) {
3926            printf("ValidityCheckingDefaultValue override failed\n");
3927            okay = 0;
3928        }
3929        if (xmlGenericErrorContext != stderr) {
3930            printf("xmlGenericErrorContext override failed\n");
3931            okay = 0;
3932        }
3933    }
3934    if (okay == 0)
3935        return ((void *) Failed);
3936    return ((void *) Okay);
3937}
3938
3939#if defined(linux) || defined(__sun) || defined(__APPLE_CC__)
3940
3941#include <pthread.h>
3942
3943static pthread_t tid[MAX_ARGC];
3944
3945static int
3946testThread(void)
3947{
3948    unsigned int i, repeat;
3949    unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]);
3950    void *results[MAX_ARGC];
3951    int ret;
3952    int res = 0;
3953
3954    xmlInitParser();
3955
3956    for (repeat = 0; repeat < 500; repeat++) {
3957        xmlLoadCatalog(catalog);
3958        nb_tests++;
3959
3960        for (i = 0; i < num_threads; i++) {
3961            results[i] = NULL;
3962            tid[i] = (pthread_t) - 1;
3963        }
3964
3965        for (i = 0; i < num_threads; i++) {
3966            ret = pthread_create(&tid[i], 0, thread_specific_data,
3967                                 (void *) testfiles[i]);
3968            if (ret != 0) {
3969                fprintf(stderr, "pthread_create failed\n");
3970                return (1);
3971            }
3972        }
3973        for (i = 0; i < num_threads; i++) {
3974            ret = pthread_join(tid[i], &results[i]);
3975            if (ret != 0) {
3976                fprintf(stderr, "pthread_join failed\n");
3977                return (1);
3978            }
3979        }
3980
3981        xmlCatalogCleanup();
3982        for (i = 0; i < num_threads; i++)
3983            if (results[i] != (void *) Okay) {
3984                fprintf(stderr, "Thread %d handling %s failed\n",
3985                        i, testfiles[i]);
3986                res = 1;
3987            }
3988    }
3989    return (res);
3990}
3991
3992#elif defined WIN32
3993#include <windows.h>
3994#include <string.h>
3995
3996#define TEST_REPEAT_COUNT 500
3997
3998static HANDLE tid[MAX_ARGC];
3999
4000static DWORD WINAPI
4001win32_thread_specific_data(void *private_data)
4002{
4003    return((DWORD) thread_specific_data(private_data));
4004}
4005
4006static int
4007testThread(void)
4008{
4009    unsigned int i, repeat;
4010    unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]);
4011    DWORD results[MAX_ARGC];
4012    BOOL ret;
4013    int res = 0;
4014
4015    xmlInitParser();
4016    for (repeat = 0; repeat < TEST_REPEAT_COUNT; repeat++) {
4017        xmlLoadCatalog(catalog);
4018        nb_tests++;
4019
4020        for (i = 0; i < num_threads; i++) {
4021            results[i] = 0;
4022            tid[i] = (HANDLE) - 1;
4023        }
4024
4025        for (i = 0; i < num_threads; i++) {
4026            DWORD useless;
4027
4028            tid[i] = CreateThread(NULL, 0,
4029                                  win32_thread_specific_data,
4030				  (void *) testfiles[i], 0,
4031                                  &useless);
4032            if (tid[i] == NULL) {
4033                fprintf(stderr, "CreateThread failed\n");
4034                return(1);
4035            }
4036        }
4037
4038        if (WaitForMultipleObjects(num_threads, tid, TRUE, INFINITE) ==
4039            WAIT_FAILED) {
4040            fprintf(stderr, "WaitForMultipleObjects failed\n");
4041	    return(1);
4042	}
4043
4044        for (i = 0; i < num_threads; i++) {
4045            ret = GetExitCodeThread(tid[i], &results[i]);
4046            if (ret == 0) {
4047                fprintf(stderr, "GetExitCodeThread failed\n");
4048                return(1);
4049            }
4050            CloseHandle(tid[i]);
4051        }
4052
4053        xmlCatalogCleanup();
4054        for (i = 0; i < num_threads; i++) {
4055            if (results[i] != (DWORD) Okay) {
4056                fprintf(stderr, "Thread %d handling %s failed\n",
4057		        i, testfiles[i]);
4058	        res = 1;
4059	    }
4060        }
4061    }
4062
4063    return (res);
4064}
4065
4066#elif defined __BEOS__
4067#include <OS.h>
4068
4069static thread_id tid[MAX_ARGC];
4070
4071static int
4072testThread(void)
4073{
4074    unsigned int i, repeat;
4075    unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]);
4076    void *results[MAX_ARGC];
4077    status_t ret;
4078    int res = 0;
4079
4080    xmlInitParser();
4081    for (repeat = 0; repeat < 500; repeat++) {
4082        xmlLoadCatalog(catalog);
4083        for (i = 0; i < num_threads; i++) {
4084            results[i] = NULL;
4085            tid[i] = (thread_id) - 1;
4086        }
4087        for (i = 0; i < num_threads; i++) {
4088            tid[i] =
4089                spawn_thread(thread_specific_data, "xmlTestThread",
4090                             B_NORMAL_PRIORITY, (void *) testfiles[i]);
4091            if (tid[i] < B_OK) {
4092                fprintf(stderr, "beos_thread_create failed\n");
4093                return (1);
4094            }
4095            printf("beos_thread_create %d -> %d\n", i, tid[i]);
4096        }
4097        for (i = 0; i < num_threads; i++) {
4098            ret = wait_for_thread(tid[i], &results[i]);
4099            printf("beos_thread_wait %d -> %d\n", i, ret);
4100            if (ret != B_OK) {
4101                fprintf(stderr, "beos_thread_wait failed\n");
4102                return (1);
4103            }
4104        }
4105
4106        xmlCatalogCleanup();
4107        ret = B_OK;
4108        for (i = 0; i < num_threads; i++)
4109            if (results[i] != (void *) Okay) {
4110                printf("Thread %d handling %s failed\n", i, testfiles[i]);
4111                ret = B_ERROR;
4112            }
4113    }
4114    if (ret != B_OK)
4115        return(1);
4116    return (0);
4117}
4118#else
4119static int
4120testThread(void)
4121{
4122    fprintf(stderr,
4123            "Specific platform thread support not detected\n");
4124    return (-1);
4125}
4126#endif
4127static int
4128threadsTest(const char *filename ATTRIBUTE_UNUSED,
4129	    const char *resul ATTRIBUTE_UNUSED,
4130	    const char *err ATTRIBUTE_UNUSED,
4131	    int options ATTRIBUTE_UNUSED) {
4132    return(testThread());
4133}
4134#endif
4135/************************************************************************
4136 *									*
4137 *			Tests Descriptions				*
4138 *									*
4139 ************************************************************************/
4140
4141static
4142testDesc testDescriptions[] = {
4143    { "XML regression tests" ,
4144      oldParseTest, "./test/*", "result/", "", NULL,
4145      0 },
4146    { "XML regression tests on memory" ,
4147      memParseTest, "./test/*", "result/", "", NULL,
4148      0 },
4149    { "XML entity subst regression tests" ,
4150      noentParseTest, "./test/*", "result/noent/", "", NULL,
4151      XML_PARSE_NOENT },
4152    { "XML Namespaces regression tests",
4153      errParseTest, "./test/namespaces/*", "result/namespaces/", "", ".err",
4154      0 },
4155    { "Error cases regression tests",
4156      errParseTest, "./test/errors/*.xml", "result/errors/", "", ".err",
4157      0 },
4158#ifdef LIBXML_READER_ENABLED
4159    { "Error cases stream regression tests",
4160      streamParseTest, "./test/errors/*.xml", "result/errors/", NULL, ".str",
4161      0 },
4162    { "Reader regression tests",
4163      streamParseTest, "./test/*", "result/", ".rdr", NULL,
4164      0 },
4165    { "Reader entities substitution regression tests",
4166      streamParseTest, "./test/*", "result/", ".rde", NULL,
4167      XML_PARSE_NOENT },
4168    { "Reader on memory regression tests",
4169      streamMemParseTest, "./test/*", "result/", ".rdr", NULL,
4170      0 },
4171    { "Walker regression tests",
4172      walkerParseTest, "./test/*", "result/", ".rdr", NULL,
4173      0 },
4174#endif
4175#ifdef LIBXML_SAX1_ENABLED
4176    { "SAX1 callbacks regression tests" ,
4177      saxParseTest, "./test/*", "result/", ".sax", NULL,
4178      XML_PARSE_SAX1 },
4179    { "SAX2 callbacks regression tests" ,
4180      saxParseTest, "./test/*", "result/", ".sax2", NULL,
4181      0 },
4182#endif
4183#ifdef LIBXML_PUSH_ENABLED
4184    { "XML push regression tests" ,
4185      pushParseTest, "./test/*", "result/", "", NULL,
4186      0 },
4187#endif
4188#ifdef LIBXML_HTML_ENABLED
4189    { "HTML regression tests" ,
4190      errParseTest, "./test/HTML/*", "result/HTML/", "", ".err",
4191      XML_PARSE_HTML },
4192#ifdef LIBXML_PUSH_ENABLED
4193    { "Push HTML regression tests" ,
4194      pushParseTest, "./test/HTML/*", "result/HTML/", "", ".err",
4195      XML_PARSE_HTML },
4196#endif
4197#ifdef LIBXML_SAX1_ENABLED
4198    { "HTML SAX regression tests" ,
4199      saxParseTest, "./test/HTML/*", "result/HTML/", ".sax", NULL,
4200      XML_PARSE_HTML },
4201#endif
4202#endif
4203#ifdef LIBXML_VALID_ENABLED
4204    { "Valid documents regression tests" ,
4205      errParseTest, "./test/VCM/*", NULL, NULL, NULL,
4206      XML_PARSE_DTDVALID },
4207    { "Validity checking regression tests" ,
4208      errParseTest, "./test/VC/*", "result/VC/", NULL, "",
4209      XML_PARSE_DTDVALID },
4210    { "General documents valid regression tests" ,
4211      errParseTest, "./test/valid/*", "result/valid/", "", ".err",
4212      XML_PARSE_DTDVALID },
4213#endif
4214#ifdef LIBXML_XINCLUDE_ENABLED
4215    { "XInclude regression tests" ,
4216      errParseTest, "./test/XInclude/docs/*", "result/XInclude/", "", NULL,
4217      /* Ignore errors at this point ".err", */
4218      XML_PARSE_XINCLUDE },
4219#ifdef LIBXML_READER_ENABLED
4220    { "XInclude xmlReader regression tests",
4221      streamParseTest, "./test/XInclude/docs/*", "result/XInclude/", ".rdr",
4222      /* Ignore errors at this point ".err", */
4223      NULL, XML_PARSE_XINCLUDE },
4224#endif
4225    { "XInclude regression tests stripping include nodes" ,
4226      errParseTest, "./test/XInclude/docs/*", "result/XInclude/", "", NULL,
4227      /* Ignore errors at this point ".err", */
4228      XML_PARSE_XINCLUDE | XML_PARSE_NOXINCNODE },
4229#ifdef LIBXML_READER_ENABLED
4230    { "XInclude xmlReader regression tests stripping include nodes",
4231      streamParseTest, "./test/XInclude/docs/*", "result/XInclude/", ".rdr",
4232      /* Ignore errors at this point ".err", */
4233      NULL, XML_PARSE_XINCLUDE | XML_PARSE_NOXINCNODE },
4234#endif
4235#endif
4236#ifdef LIBXML_XPATH_ENABLED
4237#ifdef LIBXML_DEBUG_ENABLED
4238    { "XPath expressions regression tests" ,
4239      xpathExprTest, "./test/XPath/expr/*", "result/XPath/expr/", "", NULL,
4240      0 },
4241    { "XPath document queries regression tests" ,
4242      xpathDocTest, "./test/XPath/docs/*", NULL, NULL, NULL,
4243      0 },
4244#ifdef LIBXML_XPTR_ENABLED
4245    { "XPointer document queries regression tests" ,
4246      xptrDocTest, "./test/XPath/docs/*", NULL, NULL, NULL,
4247      0 },
4248#endif
4249    { "xml:id regression tests" ,
4250      xmlidDocTest, "./test/xmlid/*", "result/xmlid/", "", ".err",
4251      0 },
4252#endif
4253#endif
4254    { "URI parsing tests" ,
4255      uriParseTest, "./test/URI/*.uri", "result/URI/", "", NULL,
4256      0 },
4257    { "URI base composition tests" ,
4258      uriBaseTest, "./test/URI/*.data", "result/URI/", "", NULL,
4259      0 },
4260    { "Path URI conversion tests" ,
4261      uriPathTest, NULL, NULL, NULL, NULL,
4262      0 },
4263#ifdef LIBXML_SCHEMAS_ENABLED
4264    { "Schemas regression tests" ,
4265      schemasTest, "./test/schemas/*_*.xsd", NULL, NULL, NULL,
4266      0 },
4267    { "Relax-NG regression tests" ,
4268      rngTest, "./test/relaxng/*.rng", NULL, NULL, NULL,
4269      XML_PARSE_DTDATTR | XML_PARSE_NOENT },
4270#ifdef LIBXML_READER_ENABLED
4271    { "Relax-NG streaming regression tests" ,
4272      rngStreamTest, "./test/relaxng/*.rng", NULL, NULL, NULL,
4273      XML_PARSE_DTDATTR | XML_PARSE_NOENT },
4274#endif
4275#endif
4276#ifdef LIBXML_PATTERN_ENABLED
4277#ifdef LIBXML_READER_ENABLED
4278    { "Pattern regression tests" ,
4279      patternTest, "./test/pattern/*.pat", "result/pattern/", NULL, NULL,
4280      0 },
4281#endif
4282#endif
4283#ifdef LIBXML_C14N_ENABLED
4284    { "C14N with comments regression tests" ,
4285      c14nWithCommentTest, "./test/c14n/with-comments/*.xml", NULL, NULL, NULL,
4286      0 },
4287    { "C14N without comments regression tests" ,
4288      c14nWithoutCommentTest, "./test/c14n/without-comments/*.xml", NULL, NULL, NULL,
4289      0 },
4290    { "C14N exclusive without comments regression tests" ,
4291      c14nExcWithoutCommentTest, "./test/c14n/exc-without-comments/*.xml", NULL, NULL, NULL,
4292      0 },
4293    { "C14N 1.1 without comments regression tests" ,
4294      c14n11WithoutCommentTest, "./test/c14n/1-1-without-comments/*.xml", NULL, NULL, NULL,
4295      0 },
4296#endif
4297#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED) && defined(LIBXML_SAX1_ENABLED)
4298    { "Catalog and Threads regression tests" ,
4299      threadsTest, NULL, NULL, NULL, NULL,
4300      0 },
4301#endif
4302    {NULL, NULL, NULL, NULL, NULL, NULL, 0}
4303};
4304
4305/************************************************************************
4306 *									*
4307 *		The main code driving the tests				*
4308 *									*
4309 ************************************************************************/
4310
4311static int
4312launchTests(testDescPtr tst) {
4313    int res = 0, err = 0;
4314    size_t i;
4315    char *result;
4316    char *error;
4317    int mem;
4318
4319    if (tst == NULL) return(-1);
4320    if (tst->in != NULL) {
4321	glob_t globbuf;
4322
4323	globbuf.gl_offs = 0;
4324	glob(tst->in, GLOB_DOOFFS, NULL, &globbuf);
4325	for (i = 0;i < globbuf.gl_pathc;i++) {
4326	    if (!checkTestFile(globbuf.gl_pathv[i]))
4327	        continue;
4328	    if (tst->suffix != NULL) {
4329		result = resultFilename(globbuf.gl_pathv[i], tst->out,
4330					tst->suffix);
4331		if (result == NULL) {
4332		    fprintf(stderr, "Out of memory !\n");
4333		    fatalError();
4334		}
4335	    } else {
4336	        result = NULL;
4337	    }
4338	    if (tst->err != NULL) {
4339		error = resultFilename(globbuf.gl_pathv[i], tst->out,
4340		                        tst->err);
4341		if (error == NULL) {
4342		    fprintf(stderr, "Out of memory !\n");
4343		    fatalError();
4344		}
4345	    } else {
4346	        error = NULL;
4347	    }
4348	    if ((result) &&(!checkTestFile(result))) {
4349	        fprintf(stderr, "Missing result file %s\n", result);
4350	    } else if ((error) &&(!checkTestFile(error))) {
4351	        fprintf(stderr, "Missing error file %s\n", error);
4352	    } else {
4353		mem = xmlMemUsed();
4354		extraMemoryFromResolver = 0;
4355		testErrorsSize = 0;
4356		testErrors[0] = 0;
4357		res = tst->func(globbuf.gl_pathv[i], result, error,
4358		                tst->options | XML_PARSE_COMPACT);
4359		xmlResetLastError();
4360		if (res != 0) {
4361		    fprintf(stderr, "File %s generated an error\n",
4362		            globbuf.gl_pathv[i]);
4363		    nb_errors++;
4364		    err++;
4365		}
4366		else if (xmlMemUsed() != mem) {
4367		    if ((xmlMemUsed() != mem) &&
4368		        (extraMemoryFromResolver == 0)) {
4369			fprintf(stderr, "File %s leaked %d bytes\n",
4370				globbuf.gl_pathv[i], xmlMemUsed() - mem);
4371			nb_leaks++;
4372			err++;
4373		    }
4374		}
4375		testErrorsSize = 0;
4376	    }
4377	    if (result)
4378		free(result);
4379	    if (error)
4380		free(error);
4381	}
4382	globfree(&globbuf);
4383    } else {
4384        testErrorsSize = 0;
4385	testErrors[0] = 0;
4386	extraMemoryFromResolver = 0;
4387        res = tst->func(NULL, NULL, NULL, tst->options);
4388	if (res != 0) {
4389	    nb_errors++;
4390	    err++;
4391	}
4392    }
4393    return(err);
4394}
4395
4396static int verbose = 0;
4397static int tests_quiet = 0;
4398
4399static int
4400runtest(int i) {
4401    int ret = 0, res;
4402    int old_errors, old_tests, old_leaks;
4403
4404    old_errors = nb_errors;
4405    old_tests = nb_tests;
4406    old_leaks = nb_leaks;
4407    if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL))
4408	printf("## %s\n", testDescriptions[i].desc);
4409    res = launchTests(&testDescriptions[i]);
4410    if (res != 0)
4411	ret++;
4412    if (verbose) {
4413	if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
4414	    printf("Ran %d tests, no errors\n", nb_tests - old_tests);
4415	else
4416	    printf("Ran %d tests, %d errors, %d leaks\n",
4417		   nb_tests - old_tests,
4418		   nb_errors - old_errors,
4419		   nb_leaks - old_leaks);
4420    }
4421    return(ret);
4422}
4423
4424int
4425main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
4426    int i, a, ret = 0;
4427    int subset = 0;
4428
4429    initializeLibxml2();
4430
4431    for (a = 1; a < argc;a++) {
4432        if (!strcmp(argv[a], "-v"))
4433	    verbose = 1;
4434        else if (!strcmp(argv[a], "-quiet"))
4435	    tests_quiet = 1;
4436	else {
4437	    for (i = 0; testDescriptions[i].func != NULL; i++) {
4438	        if (strstr(testDescriptions[i].desc, argv[a])) {
4439		    ret += runtest(i);
4440		    subset++;
4441		}
4442	    }
4443	}
4444    }
4445    if (subset == 0) {
4446	for (i = 0; testDescriptions[i].func != NULL; i++) {
4447	    ret += runtest(i);
4448	}
4449    }
4450    if ((nb_errors == 0) && (nb_leaks == 0)) {
4451        ret = 0;
4452	printf("Total %d tests, no errors\n",
4453	       nb_tests);
4454    } else {
4455        ret = 1;
4456	printf("Total %d tests, %d errors, %d leaks\n",
4457	       nb_tests, nb_errors, nb_leaks);
4458    }
4459    xmlCleanupParser();
4460    xmlMemoryDump();
4461
4462    return(ret);
4463}
4464
4465#else /* ! LIBXML_OUTPUT_ENABLED */
4466int
4467main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
4468    fprintf(stderr, "runtest requires output to be enabled in libxml2\n");
4469    return(1);
4470}
4471#endif
4472