rolled back to 2.9.1 because 2.9.2 doesn't work with connecting net
[platform/upstream/libxml2.git] / testlimits.c
1 /*
2  * testlimits.c: C program to run libxml2 regression tests checking various
3  *       limits in document size. Will consume a lot of RAM and CPU cycles
4  *
5  * To compile on Unixes:
6  * cc -o testlimits `xml2-config --cflags` testlimits.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 #include <time.h>
27
28 #include <libxml/parser.h>
29 #include <libxml/parserInternals.h>
30 #include <libxml/tree.h>
31 #include <libxml/uri.h>
32 #ifdef LIBXML_READER_ENABLED
33 #include <libxml/xmlreader.h>
34 #endif
35
36 static int verbose = 0;
37 static int tests_quiet = 0;
38
39 /************************************************************************
40  *                                                                      *
41  *              time handling                                           *
42  *                                                                      *
43  ************************************************************************/
44
45 /* maximum time for one parsing before declaring a timeout */
46 #define MAX_TIME 2 /* seconds */
47
48 static clock_t t0;
49 int timeout = 0;
50
51 static void reset_timout(void) {
52     timeout = 0;
53     t0 = clock();
54 }
55
56 static int check_time(void) {
57     clock_t tnow = clock();
58     if (((tnow - t0) / CLOCKS_PER_SEC) > MAX_TIME) {
59         timeout = 1;
60         return(0);
61     }
62     return(1);
63 }
64
65 /************************************************************************
66  *                                                                      *
67  *              Huge document generator                                 *
68  *                                                                      *
69  ************************************************************************/
70
71 #include <libxml/xmlIO.h>
72
73 /*
74  * Huge documents are built using fixed start and end chunks
75  * and filling between the two an unconventional amount of char data
76  */
77 typedef struct hugeTest hugeTest;
78 typedef hugeTest *hugeTestPtr;
79 struct hugeTest {
80     const char *description;
81     const char *name;
82     const char *start;
83     const char *end;
84 };
85
86 static struct hugeTest hugeTests[] = {
87     { "Huge text node", "huge:textNode", "<foo>", "</foo>" },
88     { "Huge attribute node", "huge:attrNode", "<foo bar='", "'/>" },
89     { "Huge comment node", "huge:commentNode", "<foo><!--", "--></foo>" },
90     { "Huge PI node", "huge:piNode", "<foo><?bar ", "?></foo>" },
91 };
92
93 static const char *current;
94 static int rlen;
95 static unsigned int currentTest = 0;
96 static int instate = 0;
97
98 /**
99  * hugeMatch:
100  * @URI: an URI to test
101  *
102  * Check for an huge: query
103  *
104  * Returns 1 if yes and 0 if another Input module should be used
105  */
106 static int
107 hugeMatch(const char * URI) {
108     if ((URI != NULL) && (!strncmp(URI, "huge:", 5)))
109         return(1);
110     return(0);
111 }
112
113 /**
114  * hugeOpen:
115  * @URI: an URI to test
116  *
117  * Return a pointer to the huge: query handler, in this example simply
118  * the current pointer...
119  *
120  * Returns an Input context or NULL in case or error
121  */
122 static void *
123 hugeOpen(const char * URI) {
124     if ((URI == NULL) || (strncmp(URI, "huge:", 5)))
125         return(NULL);
126
127     for (currentTest = 0;currentTest < sizeof(hugeTests)/sizeof(hugeTests[0]);
128          currentTest++)
129          if (!strcmp(hugeTests[currentTest].name, URI))
130              goto found;
131
132     return(NULL);
133
134 found:
135     rlen = strlen(hugeTests[currentTest].start);
136     current = hugeTests[currentTest].start;
137     instate = 0;
138     return((void *) current);
139 }
140
141 /**
142  * hugeClose:
143  * @context: the read context
144  *
145  * Close the huge: query handler
146  *
147  * Returns 0 or -1 in case of error
148  */
149 static int
150 hugeClose(void * context) {
151     if (context == NULL) return(-1);
152     fprintf(stderr, "\n");
153     return(0);
154 }
155
156 #define CHUNK 4096
157
158 char filling[CHUNK + 1];
159
160 static void fillFilling(void) {
161     int i;
162
163     for (i = 0;i < CHUNK;i++) {
164         filling[i] = 'a';
165     }
166     filling[CHUNK] = 0;
167 }
168
169 size_t maxlen = 64 * 1024 * 1024;
170 size_t curlen = 0;
171 size_t dotlen;
172
173 /**
174  * hugeRead:
175  * @context: the read context
176  * @buffer: where to store data
177  * @len: number of bytes to read
178  *
179  * Implement an huge: query read.
180  *
181  * Returns the number of bytes read or -1 in case of error
182  */
183 static int
184 hugeRead(void *context, char *buffer, int len)
185 {
186     if ((context == NULL) || (buffer == NULL) || (len < 0))
187         return (-1);
188
189     if (instate == 0) {
190         if (len >= rlen) {
191             len = rlen;
192             rlen = 0;
193             memcpy(buffer, current, len);
194             instate = 1;
195             curlen = 0;
196             dotlen = maxlen / 10;
197         } else {
198             memcpy(buffer, current, len);
199             rlen -= len;
200             current += len;
201         }
202     } else if (instate == 2) {
203         if (len >= rlen) {
204             len = rlen;
205             rlen = 0;
206             memcpy(buffer, current, len);
207             instate = 3;
208             curlen = 0;
209         } else {
210             memcpy(buffer, current, len);
211             rlen -= len;
212             current += len;
213         }
214     } else if (instate == 1) {
215         if (len > CHUNK) len = CHUNK;
216         memcpy(buffer, &filling[0], len);
217         curlen += len;
218         if (curlen >= maxlen) {
219             rlen = strlen(hugeTests[currentTest].end);
220             current = hugeTests[currentTest].end;
221             instate = 2;
222         } else {
223             if (curlen > dotlen) {
224                 fprintf(stderr, ".");
225                 dotlen += maxlen / 10;
226             }
227         }
228     } else
229       len = 0;
230     return (len);
231 }
232
233 /************************************************************************
234  *                                                                      *
235  *              Crazy document generator                                *
236  *                                                                      *
237  ************************************************************************/
238
239 unsigned int crazy_indx = 0;
240
241 const char *crazy = "<?xml version='1.0' encoding='UTF-8'?>\
242 <?tst ?>\
243 <!-- tst -->\
244 <!DOCTYPE foo [\
245 <?tst ?>\
246 <!-- tst -->\
247 <!ELEMENT foo (#PCDATA)>\
248 <!ELEMENT p (#PCDATA|emph)* >\
249 ]>\
250 <?tst ?>\
251 <!-- tst -->\
252 <foo bar='foo'>\
253 <?tst ?>\
254 <!-- tst -->\
255 foo\
256 <![CDATA[ ]]>\
257 </foo>\
258 <?tst ?>\
259 <!-- tst -->";
260
261 /**
262  * crazyMatch:
263  * @URI: an URI to test
264  *
265  * Check for a crazy: query
266  *
267  * Returns 1 if yes and 0 if another Input module should be used
268  */
269 static int
270 crazyMatch(const char * URI) {
271     if ((URI != NULL) && (!strncmp(URI, "crazy:", 6)))
272         return(1);
273     return(0);
274 }
275
276 /**
277  * crazyOpen:
278  * @URI: an URI to test
279  *
280  * Return a pointer to the crazy: query handler, in this example simply
281  * the current pointer...
282  *
283  * Returns an Input context or NULL in case or error
284  */
285 static void *
286 crazyOpen(const char * URI) {
287     if ((URI == NULL) || (strncmp(URI, "crazy:", 6)))
288         return(NULL);
289
290     if (crazy_indx > strlen(crazy))
291         return(NULL);
292     reset_timout();
293     rlen = crazy_indx;
294     current = &crazy[0];
295     instate = 0;
296     return((void *) current);
297 }
298
299 /**
300  * crazyClose:
301  * @context: the read context
302  *
303  * Close the crazy: query handler
304  *
305  * Returns 0 or -1 in case of error
306  */
307 static int
308 crazyClose(void * context) {
309     if (context == NULL) return(-1);
310     return(0);
311 }
312
313
314 /**
315  * crazyRead:
316  * @context: the read context
317  * @buffer: where to store data
318  * @len: number of bytes to read
319  *
320  * Implement an crazy: query read.
321  *
322  * Returns the number of bytes read or -1 in case of error
323  */
324 static int
325 crazyRead(void *context, char *buffer, int len)
326 {
327     if ((context == NULL) || (buffer == NULL) || (len < 0))
328         return (-1);
329
330     if ((check_time() <= 0) && (instate == 1)) {
331         fprintf(stderr, "\ntimeout in crazy(%d)\n", crazy_indx);
332         rlen = strlen(crazy) - crazy_indx;
333         current = &crazy[crazy_indx];
334         instate = 2;
335     }
336     if (instate == 0) {
337         if (len >= rlen) {
338             len = rlen;
339             rlen = 0;
340             memcpy(buffer, current, len);
341             instate = 1;
342             curlen = 0;
343         } else {
344             memcpy(buffer, current, len);
345             rlen -= len;
346             current += len;
347         }
348     } else if (instate == 2) {
349         if (len >= rlen) {
350             len = rlen;
351             rlen = 0;
352             memcpy(buffer, current, len);
353             instate = 3;
354             curlen = 0;
355         } else {
356             memcpy(buffer, current, len);
357             rlen -= len;
358             current += len;
359         }
360     } else if (instate == 1) {
361         if (len > CHUNK) len = CHUNK;
362         memcpy(buffer, &filling[0], len);
363         curlen += len;
364         if (curlen >= maxlen) {
365             rlen = strlen(crazy) - crazy_indx;
366             current = &crazy[crazy_indx];
367             instate = 2;
368         }
369     } else
370       len = 0;
371     return (len);
372 }
373 /************************************************************************
374  *                                                                      *
375  *              Libxml2 specific routines                               *
376  *                                                                      *
377  ************************************************************************/
378
379 static int nb_tests = 0;
380 static int nb_errors = 0;
381 static int nb_leaks = 0;
382 static int extraMemoryFromResolver = 0;
383
384 /*
385  * We need to trap calls to the resolver to not account memory for the catalog
386  * which is shared to the current running test. We also don't want to have
387  * network downloads modifying tests.
388  */
389 static xmlParserInputPtr
390 testExternalEntityLoader(const char *URL, const char *ID,
391                          xmlParserCtxtPtr ctxt) {
392     xmlParserInputPtr ret;
393     int memused = xmlMemUsed();
394
395     ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
396     extraMemoryFromResolver += xmlMemUsed() - memused;
397
398     return(ret);
399 }
400
401 /*
402  * Trapping the error messages at the generic level to grab the equivalent of
403  * stderr messages on CLI tools.
404  */
405 static char testErrors[32769];
406 static int testErrorsSize = 0;
407
408 static void XMLCDECL
409 channel(void *ctx  ATTRIBUTE_UNUSED, const char *msg, ...) {
410     va_list args;
411     int res;
412
413     if (testErrorsSize >= 32768)
414         return;
415     va_start(args, msg);
416     res = vsnprintf(&testErrors[testErrorsSize],
417                     32768 - testErrorsSize,
418                     msg, args);
419     va_end(args);
420     if (testErrorsSize + res >= 32768) {
421         /* buffer is full */
422         testErrorsSize = 32768;
423         testErrors[testErrorsSize] = 0;
424     } else {
425         testErrorsSize += res;
426     }
427     testErrors[testErrorsSize] = 0;
428 }
429
430 /**
431  * xmlParserPrintFileContext:
432  * @input:  an xmlParserInputPtr input
433  *
434  * Displays current context within the input content for error tracking
435  */
436
437 static void
438 xmlParserPrintFileContextInternal(xmlParserInputPtr input ,
439                 xmlGenericErrorFunc chanl, void *data ) {
440     const xmlChar *cur, *base;
441     unsigned int n, col;        /* GCC warns if signed, because compared with sizeof() */
442     xmlChar  content[81]; /* space for 80 chars + line terminator */
443     xmlChar *ctnt;
444
445     if (input == NULL) return;
446     cur = input->cur;
447     base = input->base;
448     /* skip backwards over any end-of-lines */
449     while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) {
450         cur--;
451     }
452     n = 0;
453     /* search backwards for beginning-of-line (to max buff size) */
454     while ((n++ < (sizeof(content)-1)) && (cur > base) &&
455    (*(cur) != '\n') && (*(cur) != '\r'))
456         cur--;
457     if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;
458     /* calculate the error position in terms of the current position */
459     col = input->cur - cur;
460     /* search forward for end-of-line (to max buff size) */
461     n = 0;
462     ctnt = content;
463     /* copy selected text to our buffer */
464     while ((*cur != 0) && (*(cur) != '\n') &&
465    (*(cur) != '\r') && (n < sizeof(content)-1)) {
466                 *ctnt++ = *cur++;
467         n++;
468     }
469     *ctnt = 0;
470     /* print out the selected text */
471     chanl(data ,"%s\n", content);
472     /* create blank line with problem pointer */
473     n = 0;
474     ctnt = content;
475     /* (leave buffer space for pointer + line terminator) */
476     while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) {
477         if (*(ctnt) != '\t')
478             *(ctnt) = ' ';
479         ctnt++;
480     }
481     *ctnt++ = '^';
482     *ctnt = 0;
483     chanl(data ,"%s\n", content);
484 }
485
486 static void
487 testStructuredErrorHandler(void *ctx  ATTRIBUTE_UNUSED, xmlErrorPtr err) {
488     char *file = NULL;
489     int line = 0;
490     int code = -1;
491     int domain;
492     void *data = NULL;
493     const char *str;
494     const xmlChar *name = NULL;
495     xmlNodePtr node;
496     xmlErrorLevel level;
497     xmlParserInputPtr input = NULL;
498     xmlParserInputPtr cur = NULL;
499     xmlParserCtxtPtr ctxt = NULL;
500
501     if (err == NULL)
502         return;
503
504     file = err->file;
505     line = err->line;
506     code = err->code;
507     domain = err->domain;
508     level = err->level;
509     node = err->node;
510     if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) ||
511         (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) ||
512         (domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) {
513         ctxt = err->ctxt;
514     }
515     str = err->message;
516
517     if (code == XML_ERR_OK)
518         return;
519
520     if ((node != NULL) && (node->type == XML_ELEMENT_NODE))
521         name = node->name;
522
523     /*
524      * Maintain the compatibility with the legacy error handling
525      */
526     if (ctxt != NULL) {
527         input = ctxt->input;
528         if ((input != NULL) && (input->filename == NULL) &&
529             (ctxt->inputNr > 1)) {
530             cur = input;
531             input = ctxt->inputTab[ctxt->inputNr - 2];
532         }
533         if (input != NULL) {
534             if (input->filename)
535                 channel(data, "%s:%d: ", input->filename, input->line);
536             else if ((line != 0) && (domain == XML_FROM_PARSER))
537                 channel(data, "Entity: line %d: ", input->line);
538         }
539     } else {
540         if (file != NULL)
541             channel(data, "%s:%d: ", file, line);
542         else if ((line != 0) && (domain == XML_FROM_PARSER))
543             channel(data, "Entity: line %d: ", line);
544     }
545     if (name != NULL) {
546         channel(data, "element %s: ", name);
547     }
548     if (code == XML_ERR_OK)
549         return;
550     switch (domain) {
551         case XML_FROM_PARSER:
552             channel(data, "parser ");
553             break;
554         case XML_FROM_NAMESPACE:
555             channel(data, "namespace ");
556             break;
557         case XML_FROM_DTD:
558         case XML_FROM_VALID:
559             channel(data, "validity ");
560             break;
561         case XML_FROM_HTML:
562             channel(data, "HTML parser ");
563             break;
564         case XML_FROM_MEMORY:
565             channel(data, "memory ");
566             break;
567         case XML_FROM_OUTPUT:
568             channel(data, "output ");
569             break;
570         case XML_FROM_IO:
571             channel(data, "I/O ");
572             break;
573         case XML_FROM_XINCLUDE:
574             channel(data, "XInclude ");
575             break;
576         case XML_FROM_XPATH:
577             channel(data, "XPath ");
578             break;
579         case XML_FROM_XPOINTER:
580             channel(data, "parser ");
581             break;
582         case XML_FROM_REGEXP:
583             channel(data, "regexp ");
584             break;
585         case XML_FROM_MODULE:
586             channel(data, "module ");
587             break;
588         case XML_FROM_SCHEMASV:
589             channel(data, "Schemas validity ");
590             break;
591         case XML_FROM_SCHEMASP:
592             channel(data, "Schemas parser ");
593             break;
594         case XML_FROM_RELAXNGP:
595             channel(data, "Relax-NG parser ");
596             break;
597         case XML_FROM_RELAXNGV:
598             channel(data, "Relax-NG validity ");
599             break;
600         case XML_FROM_CATALOG:
601             channel(data, "Catalog ");
602             break;
603         case XML_FROM_C14N:
604             channel(data, "C14N ");
605             break;
606         case XML_FROM_XSLT:
607             channel(data, "XSLT ");
608             break;
609         default:
610             break;
611     }
612     if (code == XML_ERR_OK)
613         return;
614     switch (level) {
615         case XML_ERR_NONE:
616             channel(data, ": ");
617             break;
618         case XML_ERR_WARNING:
619             channel(data, "warning : ");
620             break;
621         case XML_ERR_ERROR:
622             channel(data, "error : ");
623             break;
624         case XML_ERR_FATAL:
625             channel(data, "error : ");
626             break;
627     }
628     if (code == XML_ERR_OK)
629         return;
630     if (str != NULL) {
631         int len;
632         len = xmlStrlen((const xmlChar *)str);
633         if ((len > 0) && (str[len - 1] != '\n'))
634             channel(data, "%s\n", str);
635         else
636             channel(data, "%s", str);
637     } else {
638         channel(data, "%s\n", "out of memory error");
639     }
640     if (code == XML_ERR_OK)
641         return;
642
643     if (ctxt != NULL) {
644         xmlParserPrintFileContextInternal(input, channel, data);
645         if (cur != NULL) {
646             if (cur->filename)
647                 channel(data, "%s:%d: \n", cur->filename, cur->line);
648             else if ((line != 0) && (domain == XML_FROM_PARSER))
649                 channel(data, "Entity: line %d: \n", cur->line);
650             xmlParserPrintFileContextInternal(cur, channel, data);
651         }
652     }
653     if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) &&
654         (err->int1 < 100) &&
655         (err->int1 < xmlStrlen((const xmlChar *)err->str1))) {
656         xmlChar buf[150];
657         int i;
658
659         channel(data, "%s\n", err->str1);
660         for (i=0;i < err->int1;i++)
661              buf[i] = ' ';
662         buf[i++] = '^';
663         buf[i] = 0;
664         channel(data, "%s\n", buf);
665     }
666 }
667
668 static void
669 initializeLibxml2(void) {
670     xmlGetWarningsDefaultValue = 0;
671     xmlPedanticParserDefault(0);
672
673     xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
674     xmlInitParser();
675     xmlSetExternalEntityLoader(testExternalEntityLoader);
676     xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
677     /*
678      * register the new I/O handlers
679      */
680     if (xmlRegisterInputCallbacks(hugeMatch, hugeOpen,
681                                   hugeRead, hugeClose) < 0) {
682         fprintf(stderr, "failed to register Huge handlers\n");
683         exit(1);
684     }
685     if (xmlRegisterInputCallbacks(crazyMatch, crazyOpen,
686                                   crazyRead, crazyClose) < 0) {
687         fprintf(stderr, "failed to register Crazy handlers\n");
688         exit(1);
689     }
690 }
691
692 /************************************************************************
693  *                                                                      *
694  *              SAX empty callbacks                                     *
695  *                                                                      *
696  ************************************************************************/
697
698 unsigned long callbacks = 0;
699
700 /**
701  * isStandaloneCallback:
702  * @ctxt:  An XML parser context
703  *
704  * Is this document tagged standalone ?
705  *
706  * Returns 1 if true
707  */
708 static int
709 isStandaloneCallback(void *ctx ATTRIBUTE_UNUSED)
710 {
711     callbacks++;
712     return (0);
713 }
714
715 /**
716  * hasInternalSubsetCallback:
717  * @ctxt:  An XML parser context
718  *
719  * Does this document has an internal subset
720  *
721  * Returns 1 if true
722  */
723 static int
724 hasInternalSubsetCallback(void *ctx ATTRIBUTE_UNUSED)
725 {
726     callbacks++;
727     return (0);
728 }
729
730 /**
731  * hasExternalSubsetCallback:
732  * @ctxt:  An XML parser context
733  *
734  * Does this document has an external subset
735  *
736  * Returns 1 if true
737  */
738 static int
739 hasExternalSubsetCallback(void *ctx ATTRIBUTE_UNUSED)
740 {
741     callbacks++;
742     return (0);
743 }
744
745 /**
746  * internalSubsetCallback:
747  * @ctxt:  An XML parser context
748  *
749  * Does this document has an internal subset
750  */
751 static void
752 internalSubsetCallback(void *ctx ATTRIBUTE_UNUSED,
753                        const xmlChar * name ATTRIBUTE_UNUSED,
754                        const xmlChar * ExternalID ATTRIBUTE_UNUSED,
755                        const xmlChar * SystemID ATTRIBUTE_UNUSED)
756 {
757     callbacks++;
758     return;
759 }
760
761 /**
762  * externalSubsetCallback:
763  * @ctxt:  An XML parser context
764  *
765  * Does this document has an external subset
766  */
767 static void
768 externalSubsetCallback(void *ctx ATTRIBUTE_UNUSED,
769                        const xmlChar * name ATTRIBUTE_UNUSED,
770                        const xmlChar * ExternalID ATTRIBUTE_UNUSED,
771                        const xmlChar * SystemID ATTRIBUTE_UNUSED)
772 {
773     callbacks++;
774     return;
775 }
776
777 /**
778  * resolveEntityCallback:
779  * @ctxt:  An XML parser context
780  * @publicId: The public ID of the entity
781  * @systemId: The system ID of the entity
782  *
783  * Special entity resolver, better left to the parser, it has
784  * more context than the application layer.
785  * The default behaviour is to NOT resolve the entities, in that case
786  * the ENTITY_REF nodes are built in the structure (and the parameter
787  * values).
788  *
789  * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
790  */
791 static xmlParserInputPtr
792 resolveEntityCallback(void *ctx ATTRIBUTE_UNUSED,
793                       const xmlChar * publicId ATTRIBUTE_UNUSED,
794                       const xmlChar * systemId ATTRIBUTE_UNUSED)
795 {
796     callbacks++;
797     return (NULL);
798 }
799
800 /**
801  * getEntityCallback:
802  * @ctxt:  An XML parser context
803  * @name: The entity name
804  *
805  * Get an entity by name
806  *
807  * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
808  */
809 static xmlEntityPtr
810 getEntityCallback(void *ctx ATTRIBUTE_UNUSED,
811                   const xmlChar * name ATTRIBUTE_UNUSED)
812 {
813     callbacks++;
814     return (NULL);
815 }
816
817 /**
818  * getParameterEntityCallback:
819  * @ctxt:  An XML parser context
820  * @name: The entity name
821  *
822  * Get a parameter entity by name
823  *
824  * Returns the xmlParserInputPtr
825  */
826 static xmlEntityPtr
827 getParameterEntityCallback(void *ctx ATTRIBUTE_UNUSED,
828                            const xmlChar * name ATTRIBUTE_UNUSED)
829 {
830     callbacks++;
831     return (NULL);
832 }
833
834
835 /**
836  * entityDeclCallback:
837  * @ctxt:  An XML parser context
838  * @name:  the entity name
839  * @type:  the entity type
840  * @publicId: The public ID of the entity
841  * @systemId: The system ID of the entity
842  * @content: the entity value (without processing).
843  *
844  * An entity definition has been parsed
845  */
846 static void
847 entityDeclCallback(void *ctx ATTRIBUTE_UNUSED,
848                    const xmlChar * name ATTRIBUTE_UNUSED,
849                    int type ATTRIBUTE_UNUSED,
850                    const xmlChar * publicId ATTRIBUTE_UNUSED,
851                    const xmlChar * systemId ATTRIBUTE_UNUSED,
852                    xmlChar * content ATTRIBUTE_UNUSED)
853 {
854     callbacks++;
855     return;
856 }
857
858 /**
859  * attributeDeclCallback:
860  * @ctxt:  An XML parser context
861  * @name:  the attribute name
862  * @type:  the attribute type
863  *
864  * An attribute definition has been parsed
865  */
866 static void
867 attributeDeclCallback(void *ctx ATTRIBUTE_UNUSED,
868                       const xmlChar * elem ATTRIBUTE_UNUSED,
869                       const xmlChar * name ATTRIBUTE_UNUSED,
870                       int type ATTRIBUTE_UNUSED, int def ATTRIBUTE_UNUSED,
871                       const xmlChar * defaultValue ATTRIBUTE_UNUSED,
872                       xmlEnumerationPtr tree ATTRIBUTE_UNUSED)
873 {
874     callbacks++;
875     return;
876 }
877
878 /**
879  * elementDeclCallback:
880  * @ctxt:  An XML parser context
881  * @name:  the element name
882  * @type:  the element type
883  * @content: the element value (without processing).
884  *
885  * An element definition has been parsed
886  */
887 static void
888 elementDeclCallback(void *ctx ATTRIBUTE_UNUSED,
889                     const xmlChar * name ATTRIBUTE_UNUSED,
890                     int type ATTRIBUTE_UNUSED,
891                     xmlElementContentPtr content ATTRIBUTE_UNUSED)
892 {
893     callbacks++;
894     return;
895 }
896
897 /**
898  * notationDeclCallback:
899  * @ctxt:  An XML parser context
900  * @name: The name of the notation
901  * @publicId: The public ID of the entity
902  * @systemId: The system ID of the entity
903  *
904  * What to do when a notation declaration has been parsed.
905  */
906 static void
907 notationDeclCallback(void *ctx ATTRIBUTE_UNUSED,
908                      const xmlChar * name ATTRIBUTE_UNUSED,
909                      const xmlChar * publicId ATTRIBUTE_UNUSED,
910                      const xmlChar * systemId ATTRIBUTE_UNUSED)
911 {
912     callbacks++;
913     return;
914 }
915
916 /**
917  * unparsedEntityDeclCallback:
918  * @ctxt:  An XML parser context
919  * @name: The name of the entity
920  * @publicId: The public ID of the entity
921  * @systemId: The system ID of the entity
922  * @notationName: the name of the notation
923  *
924  * What to do when an unparsed entity declaration is parsed
925  */
926 static void
927 unparsedEntityDeclCallback(void *ctx ATTRIBUTE_UNUSED,
928                            const xmlChar * name ATTRIBUTE_UNUSED,
929                            const xmlChar * publicId ATTRIBUTE_UNUSED,
930                            const xmlChar * systemId ATTRIBUTE_UNUSED,
931                            const xmlChar * notationName ATTRIBUTE_UNUSED)
932 {
933     callbacks++;
934     return;
935 }
936
937 /**
938  * setDocumentLocatorCallback:
939  * @ctxt:  An XML parser context
940  * @loc: A SAX Locator
941  *
942  * Receive the document locator at startup, actually xmlDefaultSAXLocator
943  * Everything is available on the context, so this is useless in our case.
944  */
945 static void
946 setDocumentLocatorCallback(void *ctx ATTRIBUTE_UNUSED,
947                            xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED)
948 {
949     callbacks++;
950     return;
951 }
952
953 /**
954  * startDocumentCallback:
955  * @ctxt:  An XML parser context
956  *
957  * called when the document start being processed.
958  */
959 static void
960 startDocumentCallback(void *ctx ATTRIBUTE_UNUSED)
961 {
962     callbacks++;
963     return;
964 }
965
966 /**
967  * endDocumentCallback:
968  * @ctxt:  An XML parser context
969  *
970  * called when the document end has been detected.
971  */
972 static void
973 endDocumentCallback(void *ctx ATTRIBUTE_UNUSED)
974 {
975     callbacks++;
976     return;
977 }
978
979 #if 0
980 /**
981  * startElementCallback:
982  * @ctxt:  An XML parser context
983  * @name:  The element name
984  *
985  * called when an opening tag has been processed.
986  */
987 static void
988 startElementCallback(void *ctx ATTRIBUTE_UNUSED,
989                      const xmlChar * name ATTRIBUTE_UNUSED,
990                      const xmlChar ** atts ATTRIBUTE_UNUSED)
991 {
992     callbacks++;
993     return;
994 }
995
996 /**
997  * endElementCallback:
998  * @ctxt:  An XML parser context
999  * @name:  The element name
1000  *
1001  * called when the end of an element has been detected.
1002  */
1003 static void
1004 endElementCallback(void *ctx ATTRIBUTE_UNUSED,
1005                    const xmlChar * name ATTRIBUTE_UNUSED)
1006 {
1007     callbacks++;
1008     return;
1009 }
1010 #endif
1011
1012 /**
1013  * charactersCallback:
1014  * @ctxt:  An XML parser context
1015  * @ch:  a xmlChar string
1016  * @len: the number of xmlChar
1017  *
1018  * receiving some chars from the parser.
1019  * Question: how much at a time ???
1020  */
1021 static void
1022 charactersCallback(void *ctx ATTRIBUTE_UNUSED,
1023                    const xmlChar * ch ATTRIBUTE_UNUSED,
1024                    int len ATTRIBUTE_UNUSED)
1025 {
1026     callbacks++;
1027     return;
1028 }
1029
1030 /**
1031  * referenceCallback:
1032  * @ctxt:  An XML parser context
1033  * @name:  The entity name
1034  *
1035  * called when an entity reference is detected.
1036  */
1037 static void
1038 referenceCallback(void *ctx ATTRIBUTE_UNUSED,
1039                   const xmlChar * name ATTRIBUTE_UNUSED)
1040 {
1041     callbacks++;
1042     return;
1043 }
1044
1045 /**
1046  * ignorableWhitespaceCallback:
1047  * @ctxt:  An XML parser context
1048  * @ch:  a xmlChar string
1049  * @start: the first char in the string
1050  * @len: the number of xmlChar
1051  *
1052  * receiving some ignorable whitespaces from the parser.
1053  * Question: how much at a time ???
1054  */
1055 static void
1056 ignorableWhitespaceCallback(void *ctx ATTRIBUTE_UNUSED,
1057                             const xmlChar * ch ATTRIBUTE_UNUSED,
1058                             int len ATTRIBUTE_UNUSED)
1059 {
1060     callbacks++;
1061     return;
1062 }
1063
1064 /**
1065  * processingInstructionCallback:
1066  * @ctxt:  An XML parser context
1067  * @target:  the target name
1068  * @data: the PI data's
1069  * @len: the number of xmlChar
1070  *
1071  * A processing instruction has been parsed.
1072  */
1073 static void
1074 processingInstructionCallback(void *ctx ATTRIBUTE_UNUSED,
1075                               const xmlChar * target ATTRIBUTE_UNUSED,
1076                               const xmlChar * data ATTRIBUTE_UNUSED)
1077 {
1078     callbacks++;
1079     return;
1080 }
1081
1082 /**
1083  * cdataBlockCallback:
1084  * @ctx: the user data (XML parser context)
1085  * @value:  The pcdata content
1086  * @len:  the block length
1087  *
1088  * called when a pcdata block has been parsed
1089  */
1090 static void
1091 cdataBlockCallback(void *ctx ATTRIBUTE_UNUSED,
1092                    const xmlChar * value ATTRIBUTE_UNUSED,
1093                    int len ATTRIBUTE_UNUSED)
1094 {
1095     callbacks++;
1096     return;
1097 }
1098
1099 /**
1100  * commentCallback:
1101  * @ctxt:  An XML parser context
1102  * @value:  the comment content
1103  *
1104  * A comment has been parsed.
1105  */
1106 static void
1107 commentCallback(void *ctx ATTRIBUTE_UNUSED,
1108                 const xmlChar * value ATTRIBUTE_UNUSED)
1109 {
1110     callbacks++;
1111     return;
1112 }
1113
1114 /**
1115  * warningCallback:
1116  * @ctxt:  An XML parser context
1117  * @msg:  the message to display/transmit
1118  * @...:  extra parameters for the message display
1119  *
1120  * Display and format a warning messages, gives file, line, position and
1121  * extra parameters.
1122  */
1123 static void XMLCDECL
1124 warningCallback(void *ctx ATTRIBUTE_UNUSED,
1125                 const char *msg ATTRIBUTE_UNUSED, ...)
1126 {
1127     callbacks++;
1128     return;
1129 }
1130
1131 /**
1132  * errorCallback:
1133  * @ctxt:  An XML parser context
1134  * @msg:  the message to display/transmit
1135  * @...:  extra parameters for the message display
1136  *
1137  * Display and format a error messages, gives file, line, position and
1138  * extra parameters.
1139  */
1140 static void XMLCDECL
1141 errorCallback(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED,
1142               ...)
1143 {
1144     callbacks++;
1145     return;
1146 }
1147
1148 /**
1149  * fatalErrorCallback:
1150  * @ctxt:  An XML parser context
1151  * @msg:  the message to display/transmit
1152  * @...:  extra parameters for the message display
1153  *
1154  * Display and format a fatalError messages, gives file, line, position and
1155  * extra parameters.
1156  */
1157 static void XMLCDECL
1158 fatalErrorCallback(void *ctx ATTRIBUTE_UNUSED,
1159                    const char *msg ATTRIBUTE_UNUSED, ...)
1160 {
1161     return;
1162 }
1163
1164
1165 /*
1166  * SAX2 specific callbacks
1167  */
1168
1169 /**
1170  * startElementNsCallback:
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 startElementNsCallback(void *ctx ATTRIBUTE_UNUSED,
1178                        const xmlChar * localname ATTRIBUTE_UNUSED,
1179                        const xmlChar * prefix ATTRIBUTE_UNUSED,
1180                        const xmlChar * URI ATTRIBUTE_UNUSED,
1181                        int nb_namespaces ATTRIBUTE_UNUSED,
1182                        const xmlChar ** namespaces ATTRIBUTE_UNUSED,
1183                        int nb_attributes ATTRIBUTE_UNUSED,
1184                        int nb_defaulted ATTRIBUTE_UNUSED,
1185                        const xmlChar ** attributes ATTRIBUTE_UNUSED)
1186 {
1187     callbacks++;
1188     return;
1189 }
1190
1191 /**
1192  * endElementCallback:
1193  * @ctxt:  An XML parser context
1194  * @name:  The element name
1195  *
1196  * called when the end of an element has been detected.
1197  */
1198 static void
1199 endElementNsCallback(void *ctx ATTRIBUTE_UNUSED,
1200                      const xmlChar * localname ATTRIBUTE_UNUSED,
1201                      const xmlChar * prefix ATTRIBUTE_UNUSED,
1202                      const xmlChar * URI ATTRIBUTE_UNUSED)
1203 {
1204     callbacks++;
1205     return;
1206 }
1207
1208 static xmlSAXHandler callbackSAX2HandlerStruct = {
1209     internalSubsetCallback,
1210     isStandaloneCallback,
1211     hasInternalSubsetCallback,
1212     hasExternalSubsetCallback,
1213     resolveEntityCallback,
1214     getEntityCallback,
1215     entityDeclCallback,
1216     notationDeclCallback,
1217     attributeDeclCallback,
1218     elementDeclCallback,
1219     unparsedEntityDeclCallback,
1220     setDocumentLocatorCallback,
1221     startDocumentCallback,
1222     endDocumentCallback,
1223     NULL,
1224     NULL,
1225     referenceCallback,
1226     charactersCallback,
1227     ignorableWhitespaceCallback,
1228     processingInstructionCallback,
1229     commentCallback,
1230     warningCallback,
1231     errorCallback,
1232     fatalErrorCallback,
1233     getParameterEntityCallback,
1234     cdataBlockCallback,
1235     externalSubsetCallback,
1236     XML_SAX2_MAGIC,
1237     NULL,
1238     startElementNsCallback,
1239     endElementNsCallback,
1240     NULL
1241 };
1242
1243 static xmlSAXHandlerPtr callbackSAX2Handler = &callbackSAX2HandlerStruct;
1244
1245 /************************************************************************
1246  *                                                                      *
1247  *              The tests front-ends                                     *
1248  *                                                                      *
1249  ************************************************************************/
1250
1251 /**
1252  * readerTest:
1253  * @filename: the file to parse
1254  * @max_size: size of the limit to test
1255  * @options: parsing options
1256  * @fail: should a failure be reported
1257  *
1258  * Parse a memory generated file using SAX
1259  *
1260  * Returns 0 in case of success, an error code otherwise
1261  */
1262 static int
1263 saxTest(const char *filename, size_t limit, int options, int fail) {
1264     int res = 0;
1265     xmlParserCtxtPtr ctxt;
1266     xmlDocPtr doc;
1267     xmlSAXHandlerPtr old_sax;
1268
1269     nb_tests++;
1270
1271     maxlen = limit;
1272     ctxt = xmlNewParserCtxt();
1273     if (ctxt == NULL) {
1274         fprintf(stderr, "Failed to create parser context\n");
1275         return(1);
1276     }
1277     old_sax = ctxt->sax;
1278     ctxt->sax = callbackSAX2Handler;
1279     ctxt->userData = NULL;
1280     doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
1281
1282     if (doc != NULL) {
1283         fprintf(stderr, "SAX parsing generated a document !\n");
1284         xmlFreeDoc(doc);
1285         res = 0;
1286     } else if (ctxt->wellFormed == 0) {
1287         if (fail)
1288             res = 0;
1289         else {
1290             fprintf(stderr, "Failed to parse '%s' %lu\n", filename, limit);
1291             res = 1;
1292         }
1293     } else {
1294         if (fail) {
1295             fprintf(stderr, "Failed to get failure for '%s' %lu\n",
1296                     filename, limit);
1297             res = 1;
1298         } else
1299             res = 0;
1300     }
1301     ctxt->sax = old_sax;
1302     xmlFreeParserCtxt(ctxt);
1303
1304     return(res);
1305 }
1306 #ifdef LIBXML_READER_ENABLED
1307 /**
1308  * readerTest:
1309  * @filename: the file to parse
1310  * @max_size: size of the limit to test
1311  * @options: parsing options
1312  * @fail: should a failure be reported
1313  *
1314  * Parse a memory generated file using the xmlReader
1315  *
1316  * Returns 0 in case of success, an error code otherwise
1317  */
1318 static int
1319 readerTest(const char *filename, size_t limit, int options, int fail) {
1320     xmlTextReaderPtr reader;
1321     int res = 0;
1322     int ret;
1323
1324     nb_tests++;
1325
1326     maxlen = limit;
1327     reader = xmlReaderForFile(filename , NULL, options);
1328     if (reader == NULL) {
1329         fprintf(stderr, "Failed to open '%s' test\n", filename);
1330         return(1);
1331     }
1332     ret = xmlTextReaderRead(reader);
1333     while (ret == 1) {
1334         ret = xmlTextReaderRead(reader);
1335     }
1336     if (ret != 0) {
1337         if (fail)
1338             res = 0;
1339         else {
1340             if (strncmp(filename, "crazy:", 6) == 0)
1341                 fprintf(stderr, "Failed to parse '%s' %u\n",
1342                         filename, crazy_indx);
1343             else
1344                 fprintf(stderr, "Failed to parse '%s' %lu\n",
1345                         filename, limit);
1346             res = 1;
1347         }
1348     } else {
1349         if (fail) {
1350             if (strncmp(filename, "crazy:", 6) == 0)
1351                 fprintf(stderr, "Failed to get failure for '%s' %u\n",
1352                         filename, crazy_indx);
1353             else
1354                 fprintf(stderr, "Failed to get failure for '%s' %lu\n",
1355                         filename, limit);
1356             res = 1;
1357         } else
1358             res = 0;
1359     }
1360     if (timeout)
1361         res = 1;
1362     xmlFreeTextReader(reader);
1363
1364     return(res);
1365 }
1366 #endif
1367
1368 /************************************************************************
1369  *                                                                      *
1370  *                      Tests descriptions                              *
1371  *                                                                      *
1372  ************************************************************************/
1373
1374 typedef int (*functest) (const char *filename, size_t limit, int options,
1375                          int fail);
1376
1377 typedef struct limitDesc limitDesc;
1378 typedef limitDesc *limitDescPtr;
1379 struct limitDesc {
1380     const char *name; /* the huge generator name */
1381     size_t limit;     /* the limit to test */
1382     int options;      /* extra parser options */
1383     int fail;         /* whether the test should fail */
1384 };
1385
1386 static limitDesc limitDescriptions[] = {
1387     /* max length of a text node in content */
1388     {"huge:textNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1389     {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1390     {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1391     /* max length of a text node in content */
1392     {"huge:attrNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1393     {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1394     {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1395     /* max length of a comment node */
1396     {"huge:commentNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1397     {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1398     {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1399     /* max length of a PI node */
1400     {"huge:piNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1401     {"huge:piNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1402     {"huge:piNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1403 };
1404
1405 typedef struct testDesc testDesc;
1406 typedef testDesc *testDescPtr;
1407 struct testDesc {
1408     const char *desc; /* descripton of the test */
1409     functest    func; /* function implementing the test */
1410 };
1411
1412 static
1413 testDesc testDescriptions[] = {
1414     { "Parsing of huge files with the sax parser", saxTest},
1415 /*    { "Parsing of huge files with the tree parser", treeTest}, */
1416 #ifdef LIBXML_READER_ENABLED
1417     { "Parsing of huge files with the reader", readerTest},
1418 #endif
1419     {NULL, NULL}
1420 };
1421
1422 typedef struct testException testException;
1423 typedef testException *testExceptionPtr;
1424 struct testException {
1425     unsigned int test;  /* the parser test number */
1426     unsigned int limit; /* the limit test number */
1427     int fail;           /* new fail value or -1*/
1428     size_t size;        /* new limit value or 0 */
1429 };
1430
1431 static
1432 testException testExceptions[] = {
1433     /* the SAX parser doesn't hit a limit of XML_MAX_TEXT_LENGTH text nodes */
1434     { 0, 1, 0, 0},
1435 };
1436
1437 static int
1438 launchTests(testDescPtr tst, unsigned int test) {
1439     int res = 0, err = 0;
1440     unsigned int i, j;
1441     size_t limit;
1442     int fail;
1443
1444     if (tst == NULL) return(-1);
1445
1446     for (i = 0;i < sizeof(limitDescriptions)/sizeof(limitDescriptions[0]);i++) {
1447         limit = limitDescriptions[i].limit;
1448         fail = limitDescriptions[i].fail;
1449         /*
1450          * Handle exceptions if any
1451          */
1452         for (j = 0;j < sizeof(testExceptions)/sizeof(testExceptions[0]);j++) {
1453             if ((testExceptions[j].test == test) &&
1454                 (testExceptions[j].limit == i)) {
1455                 if (testExceptions[j].fail != -1)
1456                     fail = testExceptions[j].fail;
1457                 if (testExceptions[j].size != 0)
1458                     limit = testExceptions[j].size;
1459                 break;
1460             }
1461         }
1462         res = tst->func(limitDescriptions[i].name, limit,
1463                         limitDescriptions[i].options, fail);
1464         if (res != 0) {
1465             nb_errors++;
1466             err++;
1467         }
1468     }
1469     return(err);
1470 }
1471
1472
1473 static int
1474 runtest(unsigned int i) {
1475     int ret = 0, res;
1476     int old_errors, old_tests, old_leaks;
1477
1478     old_errors = nb_errors;
1479     old_tests = nb_tests;
1480     old_leaks = nb_leaks;
1481     if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL))
1482         printf("## %s\n", testDescriptions[i].desc);
1483     res = launchTests(&testDescriptions[i], i);
1484     if (res != 0)
1485         ret++;
1486     if (verbose) {
1487         if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1488             printf("Ran %d tests, no errors\n", nb_tests - old_tests);
1489         else
1490             printf("Ran %d tests, %d errors, %d leaks\n",
1491                    nb_tests - old_tests,
1492                    nb_errors - old_errors,
1493                    nb_leaks - old_leaks);
1494     }
1495     return(ret);
1496 }
1497
1498 static int
1499 launchCrazySAX(unsigned int test, int fail) {
1500     int res = 0, err = 0;
1501
1502     crazy_indx = test;
1503
1504     res = saxTest("crazy::test", XML_MAX_LOOKUP_LIMIT - CHUNK, 0, fail);
1505     if (res != 0) {
1506         nb_errors++;
1507         err++;
1508     }
1509     if (tests_quiet == 0)
1510         fprintf(stderr, "%c", crazy[test]);
1511
1512     return(err);
1513 }
1514
1515 static int
1516 launchCrazy(unsigned int test, int fail) {
1517     int res = 0, err = 0;
1518
1519     crazy_indx = test;
1520
1521     res = readerTest("crazy::test", XML_MAX_LOOKUP_LIMIT - CHUNK, 0, fail);
1522     if (res != 0) {
1523         nb_errors++;
1524         err++;
1525     }
1526     if (tests_quiet == 0)
1527         fprintf(stderr, "%c", crazy[test]);
1528
1529     return(err);
1530 }
1531
1532 static int get_crazy_fail(int test) {
1533     /*
1534      * adding 1000000 of character 'a' leads to parser failure mostly
1535      * everywhere except in those special spots. Need to be updated
1536      * each time crazy is updated
1537      */
1538     int fail = 1;
1539     if ((test == 44) || /* PI in Misc */
1540         ((test >= 50) && (test <= 55)) || /* Comment in Misc */
1541         (test == 79) || /* PI in DTD */
1542         ((test >= 85) && (test <= 90)) || /* Comment in DTD */
1543         (test == 154) || /* PI in Misc */
1544         ((test >= 160) && (test <= 165)) || /* Comment in Misc */
1545         ((test >= 178) && (test <= 181)) || /* attribute value */
1546         (test == 183) || /* Text */
1547         (test == 189) || /* PI in Content */
1548         (test == 191) || /* Text */
1549         ((test >= 195) && (test <= 200)) || /* Comment in Content */
1550         ((test >= 203) && (test <= 206)) || /* Text */
1551         (test == 215) || (test == 216) || /* in CDATA */
1552         (test == 219) || /* Text */
1553         (test == 231) || /* PI in Misc */
1554         ((test >= 237) && (test <= 242))) /* Comment in Misc */
1555         fail = 0;
1556     return(fail);
1557 }
1558
1559 static int
1560 runcrazy(void) {
1561     int ret = 0, res = 0;
1562     int old_errors, old_tests, old_leaks;
1563     unsigned int i;
1564
1565     old_errors = nb_errors;
1566     old_tests = nb_tests;
1567     old_leaks = nb_leaks;
1568     if (tests_quiet == 0) {
1569         printf("## Crazy tests on reader\n");
1570     }
1571     for (i = 0;i < strlen(crazy);i++) {
1572         res += launchCrazy(i, get_crazy_fail(i));
1573         if (res != 0)
1574             ret++;
1575     }
1576     if (tests_quiet == 0) {
1577         printf("\n## Crazy tests on SAX\n");
1578     }
1579     for (i = 0;i < strlen(crazy);i++) {
1580         res += launchCrazySAX(i, get_crazy_fail(i));
1581         if (res != 0)
1582             ret++;
1583     }
1584     if (tests_quiet == 0)
1585         fprintf(stderr, "\n");
1586     if (verbose) {
1587         if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1588             printf("Ran %d tests, no errors\n", nb_tests - old_tests);
1589         else
1590             printf("Ran %d tests, %d errors, %d leaks\n",
1591                    nb_tests - old_tests,
1592                    nb_errors - old_errors,
1593                    nb_leaks - old_leaks);
1594     }
1595     return(ret);
1596 }
1597
1598
1599 int
1600 main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
1601     int i, a, ret = 0;
1602     int subset = 0;
1603
1604     fillFilling();
1605     initializeLibxml2();
1606
1607     for (a = 1; a < argc;a++) {
1608         if (!strcmp(argv[a], "-v"))
1609             verbose = 1;
1610         else if (!strcmp(argv[a], "-quiet"))
1611             tests_quiet = 1;
1612         else if (!strcmp(argv[a], "-crazy"))
1613             subset = 1;
1614     }
1615     if (subset == 0) {
1616         for (i = 0; testDescriptions[i].func != NULL; i++) {
1617             ret += runtest(i);
1618         }
1619     }
1620     ret += runcrazy();
1621     if ((nb_errors == 0) && (nb_leaks == 0)) {
1622         ret = 0;
1623         printf("Total %d tests, no errors\n",
1624                nb_tests);
1625     } else {
1626         ret = 1;
1627         printf("Total %d tests, %d errors, %d leaks\n",
1628                nb_tests, nb_errors, nb_leaks);
1629     }
1630     xmlCleanupParser();
1631     xmlMemoryDump();
1632
1633     return(ret);
1634 }