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