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