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