add packaging
[platform/upstream/libxml2.git] / testrecurse.c
1 /*
2  * testrecurse.c: C program to run libxml2 regression tests checking entities
3  *            recursions
4  *
5  * To compile on Unixes:
6  * cc -o testrecurse `xml2-config --cflags` testrecurse.c `xml2-config --libs` -lpthread
7  *
8  * See Copyright for the status of this software.
9  *
10  * daniel@veillard.com
11  */
12
13 #ifdef HAVE_CONFIG_H
14 #include "libxml.h"
15 #else
16 #include <stdio.h>
17 #endif
18
19 #if !defined(_WIN32) || defined(__CYGWIN__)
20 #include <unistd.h>
21 #endif
22 #include <string.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26
27 #include <libxml/parser.h>
28 #include <libxml/tree.h>
29 #include <libxml/uri.h>
30 #ifdef LIBXML_READER_ENABLED
31 #include <libxml/xmlreader.h>
32 #endif
33
34 /*
35  * O_BINARY is just for Windows compatibility - if it isn't defined
36  * on this system, avoid any compilation error
37  */
38 #ifdef  O_BINARY
39 #define RD_FLAGS        O_RDONLY | O_BINARY
40 #else
41 #define RD_FLAGS        O_RDONLY
42 #endif
43
44 typedef int (*functest) (const char *filename, const char *result,
45                          const char *error, int options);
46
47 typedef struct testDesc testDesc;
48 typedef testDesc *testDescPtr;
49 struct testDesc {
50     const char *desc; /* descripton of the test */
51     functest    func; /* function implementing the test */
52     const char *in;   /* glob to path for input files */
53     const char *out;  /* output directory */
54     const char *suffix;/* suffix for output files */
55     const char *err;  /* suffix for error output files */
56     int     options;  /* parser options for the test */
57 };
58
59 static int checkTestFile(const char *filename);
60
61
62 #if defined(_WIN32) && !defined(__CYGWIN__)
63
64 #include <windows.h>
65 #include <io.h>
66
67 typedef struct
68 {
69       size_t gl_pathc;    /* Count of paths matched so far  */
70       char **gl_pathv;    /* List of matched pathnames.  */
71       size_t gl_offs;     /* Slots to reserve in 'gl_pathv'.  */
72 } glob_t;
73
74 #define GLOB_DOOFFS 0
75 static int glob(const char *pattern, int flags,
76                 int errfunc(const char *epath, int eerrno),
77                 glob_t *pglob) {
78     glob_t *ret;
79     WIN32_FIND_DATA FindFileData;
80     HANDLE hFind;
81     unsigned int nb_paths = 0;
82     char directory[500];
83     int len;
84
85     if ((pattern == NULL) || (pglob == NULL)) return(-1);
86
87     strncpy(directory, pattern, 499);
88     for (len = strlen(directory);len >= 0;len--) {
89         if (directory[len] == '/') {
90             len++;
91             directory[len] = 0;
92             break;
93         }
94     }
95     if (len <= 0)
96         len = 0;
97
98
99     ret = pglob;
100     memset(ret, 0, sizeof(glob_t));
101
102     hFind = FindFirstFileA(pattern, &FindFileData);
103     if (hFind == INVALID_HANDLE_VALUE)
104         return(0);
105     nb_paths = 20;
106     ret->gl_pathv = (char **) malloc(nb_paths * sizeof(char *));
107     if (ret->gl_pathv == NULL) {
108         FindClose(hFind);
109         return(-1);
110     }
111     strncpy(directory + len, FindFileData.cFileName, 499 - len);
112     ret->gl_pathv[ret->gl_pathc] = strdup(directory);
113     if (ret->gl_pathv[ret->gl_pathc] == NULL)
114         goto done;
115     ret->gl_pathc++;
116     while(FindNextFileA(hFind, &FindFileData)) {
117         if (FindFileData.cFileName[0] == '.')
118             continue;
119         if (ret->gl_pathc + 2 > nb_paths) {
120             char **tmp = realloc(ret->gl_pathv, nb_paths * 2 * sizeof(char *));
121             if (tmp == NULL)
122                 break;
123             ret->gl_pathv = tmp;
124             nb_paths *= 2;
125         }
126         strncpy(directory + len, FindFileData.cFileName, 499 - len);
127         ret->gl_pathv[ret->gl_pathc] = strdup(directory);
128         if (ret->gl_pathv[ret->gl_pathc] == NULL)
129             break;
130         ret->gl_pathc++;
131     }
132     ret->gl_pathv[ret->gl_pathc] = NULL;
133
134 done:
135     FindClose(hFind);
136     return(0);
137 }
138
139
140
141 static void globfree(glob_t *pglob) {
142     unsigned int i;
143     if (pglob == NULL)
144         return;
145
146     for (i = 0;i < pglob->gl_pathc;i++) {
147          if (pglob->gl_pathv[i] != NULL)
148              free(pglob->gl_pathv[i]);
149     }
150 }
151
152 #if !defined(__MINGW32__)
153 #define vsnprintf _vsnprintf
154 #define snprintf _snprintf
155 #endif
156 #else
157 #include <glob.h>
158 #endif
159
160 /************************************************************************
161  *                                                                      *
162  *              Huge document generator                                 *
163  *                                                                      *
164  ************************************************************************/
165
166 #include <libxml/xmlIO.h>
167
168
169 static const char *start = "<!DOCTYPE foo [\
170 <!ENTITY f 'some internal data'> \
171 <!ENTITY e '&f;&f;'> \
172 <!ENTITY d '&e;&e;'> \
173 ]> \
174 <foo>";
175
176 static const char *segment = "  <bar>&e; &f; &d;</bar>\n";
177 static const char *finish = "</foo>";
178
179 static int curseg = 0;
180 static const char *current;
181 static int rlen;
182
183 /**
184  * hugeMatch:
185  * @URI: an URI to test
186  *
187  * Check for an huge: query
188  *
189  * Returns 1 if yes and 0 if another Input module should be used
190  */
191 static int
192 hugeMatch(const char * URI) {
193     if ((URI != NULL) && (!strncmp(URI, "huge:", 4)))
194         return(1);
195     return(0);
196 }
197
198 /**
199  * hugeOpen:
200  * @URI: an URI to test
201  *
202  * Return a pointer to the huge: query handler, in this example simply
203  * the current pointer...
204  *
205  * Returns an Input context or NULL in case or error
206  */
207 static void *
208 hugeOpen(const char * URI) {
209     if ((URI == NULL) || (strncmp(URI, "huge:", 4)))
210         return(NULL);
211     rlen = strlen(start);
212     current = start;
213     return((void *) current);
214 }
215
216 /**
217  * hugeClose:
218  * @context: the read context
219  *
220  * Close the huge: query handler
221  *
222  * Returns 0 or -1 in case of error
223  */
224 static int
225 hugeClose(void * context) {
226     if (context == NULL) return(-1);
227     return(0);
228 }
229
230 #define MAX_NODES 1000000
231
232 /**
233  * hugeRead:
234  * @context: the read context
235  * @buffer: where to store data
236  * @len: number of bytes to read
237  *
238  * Implement an huge: query read.
239  *
240  * Returns the number of bytes read or -1 in case of error
241  */
242 static int
243 hugeRead(void *context, char *buffer, int len)
244 {
245     if ((context == NULL) || (buffer == NULL) || (len < 0))
246         return (-1);
247
248     if (len >= rlen) {
249         if (curseg >= MAX_NODES + 1) {
250             rlen = 0;
251             return(0);
252         }
253         len = rlen;
254         rlen = 0;
255         memcpy(buffer, current, len);
256         curseg ++;
257         if (curseg == MAX_NODES) {
258             fprintf(stderr, "\n");
259             rlen = strlen(finish);
260             current = finish;
261         } else {
262             if (curseg % (MAX_NODES / 10) == 0)
263                 fprintf(stderr, ".");
264             rlen = strlen(segment);
265             current = segment;
266         }
267     } else {
268         memcpy(buffer, current, len);
269         rlen -= len;
270         current += len;
271     }
272     return (len);
273 }
274
275 /************************************************************************
276  *                                                                      *
277  *              Libxml2 specific routines                               *
278  *                                                                      *
279  ************************************************************************/
280
281 static int nb_tests = 0;
282 static int nb_errors = 0;
283 static int nb_leaks = 0;
284 static int extraMemoryFromResolver = 0;
285
286 static int
287 fatalError(void) {
288     fprintf(stderr, "Exitting tests on fatal error\n");
289     exit(1);
290 }
291
292 /*
293  * We need to trap calls to the resolver to not account memory for the catalog
294  * which is shared to the current running test. We also don't want to have
295  * network downloads modifying tests.
296  */
297 static xmlParserInputPtr
298 testExternalEntityLoader(const char *URL, const char *ID,
299                          xmlParserCtxtPtr ctxt) {
300     xmlParserInputPtr ret;
301
302     if (checkTestFile(URL)) {
303         ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
304     } else {
305         int memused = xmlMemUsed();
306         ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
307         extraMemoryFromResolver += xmlMemUsed() - memused;
308     }
309
310     return(ret);
311 }
312
313 /*
314  * Trapping the error messages at the generic level to grab the equivalent of
315  * stderr messages on CLI tools.
316  */
317 static char testErrors[32769];
318 static int testErrorsSize = 0;
319
320 static void XMLCDECL
321 channel(void *ctx  ATTRIBUTE_UNUSED, const char *msg, ...) {
322     va_list args;
323     int res;
324
325     if (testErrorsSize >= 32768)
326         return;
327     va_start(args, msg);
328     res = vsnprintf(&testErrors[testErrorsSize],
329                     32768 - testErrorsSize,
330                     msg, args);
331     va_end(args);
332     if (testErrorsSize + res >= 32768) {
333         /* buffer is full */
334         testErrorsSize = 32768;
335         testErrors[testErrorsSize] = 0;
336     } else {
337         testErrorsSize += res;
338     }
339     testErrors[testErrorsSize] = 0;
340 }
341
342 /**
343  * xmlParserPrintFileContext:
344  * @input:  an xmlParserInputPtr input
345  *
346  * Displays current context within the input content for error tracking
347  */
348
349 static void
350 xmlParserPrintFileContextInternal(xmlParserInputPtr input ,
351                 xmlGenericErrorFunc chanl, void *data ) {
352     const xmlChar *cur, *base;
353     unsigned int n, col;        /* GCC warns if signed, because compared with sizeof() */
354     xmlChar  content[81]; /* space for 80 chars + line terminator */
355     xmlChar *ctnt;
356
357     if (input == NULL) return;
358     cur = input->cur;
359     base = input->base;
360     /* skip backwards over any end-of-lines */
361     while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) {
362         cur--;
363     }
364     n = 0;
365     /* search backwards for beginning-of-line (to max buff size) */
366     while ((n++ < (sizeof(content)-1)) && (cur > base) &&
367    (*(cur) != '\n') && (*(cur) != '\r'))
368         cur--;
369     if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;
370     /* calculate the error position in terms of the current position */
371     col = input->cur - cur;
372     /* search forward for end-of-line (to max buff size) */
373     n = 0;
374     ctnt = content;
375     /* copy selected text to our buffer */
376     while ((*cur != 0) && (*(cur) != '\n') &&
377    (*(cur) != '\r') && (n < sizeof(content)-1)) {
378                 *ctnt++ = *cur++;
379         n++;
380     }
381     *ctnt = 0;
382     /* print out the selected text */
383     chanl(data ,"%s\n", content);
384     /* create blank line with problem pointer */
385     n = 0;
386     ctnt = content;
387     /* (leave buffer space for pointer + line terminator) */
388     while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) {
389         if (*(ctnt) != '\t')
390             *(ctnt) = ' ';
391         ctnt++;
392     }
393     *ctnt++ = '^';
394     *ctnt = 0;
395     chanl(data ,"%s\n", content);
396 }
397
398 static void
399 testStructuredErrorHandler(void *ctx  ATTRIBUTE_UNUSED, xmlErrorPtr err) {
400     char *file = NULL;
401     int line = 0;
402     int code = -1;
403     int domain;
404     void *data = NULL;
405     const char *str;
406     const xmlChar *name = NULL;
407     xmlNodePtr node;
408     xmlErrorLevel level;
409     xmlParserInputPtr input = NULL;
410     xmlParserInputPtr cur = NULL;
411     xmlParserCtxtPtr ctxt = NULL;
412
413     if (err == NULL)
414         return;
415
416     file = err->file;
417     line = err->line;
418     code = err->code;
419     domain = err->domain;
420     level = err->level;
421     node = err->node;
422     if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) ||
423         (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) ||
424         (domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) {
425         ctxt = err->ctxt;
426     }
427     str = err->message;
428
429     if (code == XML_ERR_OK)
430         return;
431
432     if ((node != NULL) && (node->type == XML_ELEMENT_NODE))
433         name = node->name;
434
435     /*
436      * Maintain the compatibility with the legacy error handling
437      */
438     if (ctxt != NULL) {
439         input = ctxt->input;
440         if ((input != NULL) && (input->filename == NULL) &&
441             (ctxt->inputNr > 1)) {
442             cur = input;
443             input = ctxt->inputTab[ctxt->inputNr - 2];
444         }
445         if (input != NULL) {
446             if (input->filename)
447                 channel(data, "%s:%d: ", input->filename, input->line);
448             else if ((line != 0) && (domain == XML_FROM_PARSER))
449                 channel(data, "Entity: line %d: ", input->line);
450         }
451     } else {
452         if (file != NULL)
453             channel(data, "%s:%d: ", file, line);
454         else if ((line != 0) && (domain == XML_FROM_PARSER))
455             channel(data, "Entity: line %d: ", line);
456     }
457     if (name != NULL) {
458         channel(data, "element %s: ", name);
459     }
460     if (code == XML_ERR_OK)
461         return;
462     switch (domain) {
463         case XML_FROM_PARSER:
464             channel(data, "parser ");
465             break;
466         case XML_FROM_NAMESPACE:
467             channel(data, "namespace ");
468             break;
469         case XML_FROM_DTD:
470         case XML_FROM_VALID:
471             channel(data, "validity ");
472             break;
473         case XML_FROM_HTML:
474             channel(data, "HTML parser ");
475             break;
476         case XML_FROM_MEMORY:
477             channel(data, "memory ");
478             break;
479         case XML_FROM_OUTPUT:
480             channel(data, "output ");
481             break;
482         case XML_FROM_IO:
483             channel(data, "I/O ");
484             break;
485         case XML_FROM_XINCLUDE:
486             channel(data, "XInclude ");
487             break;
488         case XML_FROM_XPATH:
489             channel(data, "XPath ");
490             break;
491         case XML_FROM_XPOINTER:
492             channel(data, "parser ");
493             break;
494         case XML_FROM_REGEXP:
495             channel(data, "regexp ");
496             break;
497         case XML_FROM_MODULE:
498             channel(data, "module ");
499             break;
500         case XML_FROM_SCHEMASV:
501             channel(data, "Schemas validity ");
502             break;
503         case XML_FROM_SCHEMASP:
504             channel(data, "Schemas parser ");
505             break;
506         case XML_FROM_RELAXNGP:
507             channel(data, "Relax-NG parser ");
508             break;
509         case XML_FROM_RELAXNGV:
510             channel(data, "Relax-NG validity ");
511             break;
512         case XML_FROM_CATALOG:
513             channel(data, "Catalog ");
514             break;
515         case XML_FROM_C14N:
516             channel(data, "C14N ");
517             break;
518         case XML_FROM_XSLT:
519             channel(data, "XSLT ");
520             break;
521         default:
522             break;
523     }
524     if (code == XML_ERR_OK)
525         return;
526     switch (level) {
527         case XML_ERR_NONE:
528             channel(data, ": ");
529             break;
530         case XML_ERR_WARNING:
531             channel(data, "warning : ");
532             break;
533         case XML_ERR_ERROR:
534             channel(data, "error : ");
535             break;
536         case XML_ERR_FATAL:
537             channel(data, "error : ");
538             break;
539     }
540     if (code == XML_ERR_OK)
541         return;
542     if (str != NULL) {
543         int len;
544         len = xmlStrlen((const xmlChar *)str);
545         if ((len > 0) && (str[len - 1] != '\n'))
546             channel(data, "%s\n", str);
547         else
548             channel(data, "%s", str);
549     } else {
550         channel(data, "%s\n", "out of memory error");
551     }
552     if (code == XML_ERR_OK)
553         return;
554
555     if (ctxt != NULL) {
556         xmlParserPrintFileContextInternal(input, channel, data);
557         if (cur != NULL) {
558             if (cur->filename)
559                 channel(data, "%s:%d: \n", cur->filename, cur->line);
560             else if ((line != 0) && (domain == XML_FROM_PARSER))
561                 channel(data, "Entity: line %d: \n", cur->line);
562             xmlParserPrintFileContextInternal(cur, channel, data);
563         }
564     }
565     if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) &&
566         (err->int1 < 100) &&
567         (err->int1 < xmlStrlen((const xmlChar *)err->str1))) {
568         xmlChar buf[150];
569         int i;
570
571         channel(data, "%s\n", err->str1);
572         for (i=0;i < err->int1;i++)
573              buf[i] = ' ';
574         buf[i++] = '^';
575         buf[i] = 0;
576         channel(data, "%s\n", buf);
577     }
578 }
579
580 static void
581 initializeLibxml2(void) {
582     xmlGetWarningsDefaultValue = 0;
583     xmlPedanticParserDefault(0);
584
585     xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
586     xmlInitParser();
587     xmlSetExternalEntityLoader(testExternalEntityLoader);
588     xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
589     /*
590      * register the new I/O handlers
591      */
592     if (xmlRegisterInputCallbacks(hugeMatch, hugeOpen,
593                                   hugeRead, hugeClose) < 0) {
594         fprintf(stderr, "failed to register Huge handler\n");
595         exit(1);
596     }
597 }
598
599 /************************************************************************
600  *                                                                      *
601  *              File name and path utilities                            *
602  *                                                                      *
603  ************************************************************************/
604
605 static const char *baseFilename(const char *filename) {
606     const char *cur;
607     if (filename == NULL)
608         return(NULL);
609     cur = &filename[strlen(filename)];
610     while ((cur > filename) && (*cur != '/'))
611         cur--;
612     if (*cur == '/')
613         return(cur + 1);
614     return(cur);
615 }
616
617 static char *resultFilename(const char *filename, const char *out,
618                             const char *suffix) {
619     const char *base;
620     char res[500];
621     char suffixbuff[500];
622
623 /*************
624     if ((filename[0] == 't') && (filename[1] == 'e') &&
625         (filename[2] == 's') && (filename[3] == 't') &&
626         (filename[4] == '/'))
627         filename = &filename[5];
628  *************/
629
630     base = baseFilename(filename);
631     if (suffix == NULL)
632         suffix = ".tmp";
633     if (out == NULL)
634         out = "";
635
636     strncpy(suffixbuff,suffix,499);
637 #ifdef VMS
638     if(strstr(base,".") && suffixbuff[0]=='.')
639       suffixbuff[0]='_';
640 #endif
641
642     snprintf(res, 499, "%s%s%s", out, base, suffixbuff);
643     res[499] = 0;
644     return(strdup(res));
645 }
646
647 static int checkTestFile(const char *filename) {
648     struct stat buf;
649
650     if (stat(filename, &buf) == -1)
651         return(0);
652
653 #if defined(_WIN32) && !defined(__CYGWIN__)
654     if (!(buf.st_mode & _S_IFREG))
655         return(0);
656 #else
657     if (!S_ISREG(buf.st_mode))
658         return(0);
659 #endif
660
661     return(1);
662 }
663
664
665
666 /************************************************************************
667  *                                                                      *
668  *              Test to detect or not recursive entities                *
669  *                                                                      *
670  ************************************************************************/
671 /**
672  * recursiveDetectTest:
673  * @filename: the file to parse
674  * @result: the file with expected result
675  * @err: the file with error messages: unused
676  *
677  * Parse a file loading DTD and replacing entities check it fails for
678  * lol cases
679  *
680  * Returns 0 in case of success, an error code otherwise
681  */
682 static int
683 recursiveDetectTest(const char *filename,
684              const char *result ATTRIBUTE_UNUSED,
685              const char *err ATTRIBUTE_UNUSED,
686              int options ATTRIBUTE_UNUSED) {
687     xmlDocPtr doc;
688     xmlParserCtxtPtr ctxt;
689     int res = 0;
690     int mem;
691
692     nb_tests++;
693
694     ctxt = xmlNewParserCtxt();
695     mem = xmlMemUsed();
696     /*
697      * base of the test, parse with the old API
698      */
699     doc = xmlCtxtReadFile(ctxt, filename, NULL,
700                           XML_PARSE_NOENT | XML_PARSE_DTDLOAD);
701     if ((doc != NULL) || (ctxt->lastError.code != XML_ERR_ENTITY_LOOP)) {
702         fprintf(stderr, "Failed to detect recursion in %s\n", filename);
703         xmlFreeParserCtxt(ctxt);
704         xmlFreeDoc(doc);
705         return(1);
706     }
707     xmlFreeParserCtxt(ctxt);
708
709     return(res);
710 }
711
712 /**
713  * notRecursiveDetectTest:
714  * @filename: the file to parse
715  * @result: the file with expected result
716  * @err: the file with error messages: unused
717  *
718  * Parse a file loading DTD and replacing entities check it works for
719  * good cases
720  *
721  * Returns 0 in case of success, an error code otherwise
722  */
723 static int
724 notRecursiveDetectTest(const char *filename,
725              const char *result ATTRIBUTE_UNUSED,
726              const char *err ATTRIBUTE_UNUSED,
727              int options ATTRIBUTE_UNUSED) {
728     xmlDocPtr doc;
729     xmlParserCtxtPtr ctxt;
730     int res = 0;
731     int mem;
732
733     nb_tests++;
734
735     ctxt = xmlNewParserCtxt();
736     mem = xmlMemUsed();
737     /*
738      * base of the test, parse with the old API
739      */
740     doc = xmlCtxtReadFile(ctxt, filename, NULL,
741                           XML_PARSE_NOENT | XML_PARSE_DTDLOAD);
742     if (doc == NULL) {
743         fprintf(stderr, "Failed to parse correct file %s\n", filename);
744         xmlFreeParserCtxt(ctxt);
745         return(1);
746     }
747     xmlFreeDoc(doc);
748     xmlFreeParserCtxt(ctxt);
749
750     return(res);
751 }
752
753 #ifdef LIBXML_READER_ENABLED
754 /**
755  * notRecursiveHugeTest:
756  * @filename: the file to parse
757  * @result: the file with expected result
758  * @err: the file with error messages: unused
759  *
760  * Parse a memory generated file
761  * good cases
762  *
763  * Returns 0 in case of success, an error code otherwise
764  */
765 static int
766 notRecursiveHugeTest(const char *filename ATTRIBUTE_UNUSED,
767              const char *result ATTRIBUTE_UNUSED,
768              const char *err ATTRIBUTE_UNUSED,
769              int options ATTRIBUTE_UNUSED) {
770     xmlTextReaderPtr reader;
771     int res = 0;
772     int ret;
773
774     nb_tests++;
775
776     reader = xmlReaderForFile("huge:test" , NULL,
777                               XML_PARSE_NOENT | XML_PARSE_DTDLOAD);
778     if (reader == NULL) {
779         fprintf(stderr, "Failed to open huge:test\n");
780         return(1);
781     }
782     ret = xmlTextReaderRead(reader);
783     while (ret == 1) {
784         ret = xmlTextReaderRead(reader);
785     }
786     if (ret != 0) {
787         fprintf(stderr, "Failed to parser huge:test with entities\n");
788         res = 1;
789     }
790     xmlFreeTextReader(reader);
791
792     return(res);
793 }
794 #endif
795
796 /************************************************************************
797  *                                                                      *
798  *                      Tests Descriptions                              *
799  *                                                                      *
800  ************************************************************************/
801
802 static
803 testDesc testDescriptions[] = {
804     { "Parsing recursive test cases" ,
805       recursiveDetectTest, "./test/recurse/lol*.xml", NULL, NULL, NULL,
806       0 },
807     { "Parsing non-recursive test cases" ,
808       notRecursiveDetectTest, "./test/recurse/good*.xml", NULL, NULL, NULL,
809       0 },
810 #ifdef LIBXML_READER_ENABLED
811     { "Parsing non-recursive huge case" ,
812       notRecursiveHugeTest, NULL, NULL, NULL, NULL,
813       0 },
814 #endif
815     {NULL, NULL, NULL, NULL, NULL, NULL, 0}
816 };
817
818 /************************************************************************
819  *                                                                      *
820  *              The main code driving the tests                         *
821  *                                                                      *
822  ************************************************************************/
823
824 static int
825 launchTests(testDescPtr tst) {
826     int res = 0, err = 0;
827     size_t i;
828     char *result;
829     char *error;
830     int mem;
831
832     if (tst == NULL) return(-1);
833     if (tst->in != NULL) {
834         glob_t globbuf;
835
836         globbuf.gl_offs = 0;
837         glob(tst->in, GLOB_DOOFFS, NULL, &globbuf);
838         for (i = 0;i < globbuf.gl_pathc;i++) {
839             if (!checkTestFile(globbuf.gl_pathv[i]))
840                 continue;
841             if (tst->suffix != NULL) {
842                 result = resultFilename(globbuf.gl_pathv[i], tst->out,
843                                         tst->suffix);
844                 if (result == NULL) {
845                     fprintf(stderr, "Out of memory !\n");
846                     fatalError();
847                 }
848             } else {
849                 result = NULL;
850             }
851             if (tst->err != NULL) {
852                 error = resultFilename(globbuf.gl_pathv[i], tst->out,
853                                         tst->err);
854                 if (error == NULL) {
855                     fprintf(stderr, "Out of memory !\n");
856                     fatalError();
857                 }
858             } else {
859                 error = NULL;
860             }
861             if ((result) &&(!checkTestFile(result))) {
862                 fprintf(stderr, "Missing result file %s\n", result);
863             } else if ((error) &&(!checkTestFile(error))) {
864                 fprintf(stderr, "Missing error file %s\n", error);
865             } else {
866                 mem = xmlMemUsed();
867                 extraMemoryFromResolver = 0;
868                 testErrorsSize = 0;
869                 testErrors[0] = 0;
870                 res = tst->func(globbuf.gl_pathv[i], result, error,
871                                 tst->options | XML_PARSE_COMPACT);
872                 xmlResetLastError();
873                 if (res != 0) {
874                     fprintf(stderr, "File %s generated an error\n",
875                             globbuf.gl_pathv[i]);
876                     nb_errors++;
877                     err++;
878                 }
879                 else if (xmlMemUsed() != mem) {
880                     if ((xmlMemUsed() != mem) &&
881                         (extraMemoryFromResolver == 0)) {
882                         fprintf(stderr, "File %s leaked %d bytes\n",
883                                 globbuf.gl_pathv[i], xmlMemUsed() - mem);
884                         nb_leaks++;
885                         err++;
886                     }
887                 }
888                 testErrorsSize = 0;
889             }
890             if (result)
891                 free(result);
892             if (error)
893                 free(error);
894         }
895         globfree(&globbuf);
896     } else {
897         testErrorsSize = 0;
898         testErrors[0] = 0;
899         extraMemoryFromResolver = 0;
900         res = tst->func(NULL, NULL, NULL, tst->options);
901         if (res != 0) {
902             nb_errors++;
903             err++;
904         }
905     }
906     return(err);
907 }
908
909 static int verbose = 0;
910 static int tests_quiet = 0;
911
912 static int
913 runtest(int i) {
914     int ret = 0, res;
915     int old_errors, old_tests, old_leaks;
916
917     old_errors = nb_errors;
918     old_tests = nb_tests;
919     old_leaks = nb_leaks;
920     if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL))
921         printf("## %s\n", testDescriptions[i].desc);
922     res = launchTests(&testDescriptions[i]);
923     if (res != 0)
924         ret++;
925     if (verbose) {
926         if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
927             printf("Ran %d tests, no errors\n", nb_tests - old_tests);
928         else
929             printf("Ran %d tests, %d errors, %d leaks\n",
930                    nb_tests - old_tests,
931                    nb_errors - old_errors,
932                    nb_leaks - old_leaks);
933     }
934     return(ret);
935 }
936
937 int
938 main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
939     int i, a, ret = 0;
940     int subset = 0;
941
942     initializeLibxml2();
943
944     for (a = 1; a < argc;a++) {
945         if (!strcmp(argv[a], "-v"))
946             verbose = 1;
947         else if (!strcmp(argv[a], "-quiet"))
948             tests_quiet = 1;
949         else {
950             for (i = 0; testDescriptions[i].func != NULL; i++) {
951                 if (strstr(testDescriptions[i].desc, argv[a])) {
952                     ret += runtest(i);
953                     subset++;
954                 }
955             }
956         }
957     }
958     if (subset == 0) {
959         for (i = 0; testDescriptions[i].func != NULL; i++) {
960             ret += runtest(i);
961         }
962     }
963     if ((nb_errors == 0) && (nb_leaks == 0)) {
964         ret = 0;
965         printf("Total %d tests, no errors\n",
966                nb_tests);
967     } else {
968         ret = 1;
969         printf("Total %d tests, %d errors, %d leaks\n",
970                nb_tests, nb_errors, nb_leaks);
971     }
972     xmlCleanupParser();
973     xmlMemoryDump();
974
975     return(ret);
976 }