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