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
36static int verbose = 0;
37static 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
48static clock_t t0;
49int timeout = 0;
50
51static void reset_timout(void) {
52    timeout = 0;
53    t0 = clock();
54}
55
56static 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 */
77typedef struct hugeTest hugeTest;
78typedef hugeTest *hugeTestPtr;
79struct hugeTest {
80    const char *description;
81    const char *name;
82    const char *start;
83    const char *end;
84};
85
86static 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
93static const char *current;
94static int rlen;
95static unsigned int currentTest = 0;
96static 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 */
106static int
107hugeMatch(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 */
122static void *
123hugeOpen(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
134found:
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 */
149static int
150hugeClose(void * context) {
151    if (context == NULL) return(-1);
152    fprintf(stderr, "\n");
153    return(0);
154}
155
156#define CHUNK 4096
157
158char filling[CHUNK + 1];
159
160static 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
169size_t maxlen = 64 * 1024 * 1024;
170size_t curlen = 0;
171size_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 */
183static int
184hugeRead(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
239unsigned int crazy_indx = 0;
240
241const 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 -->\
255foo\
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 */
269static int
270crazyMatch(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 */
285static void *
286crazyOpen(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 */
307static int
308crazyClose(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 */
324static int
325crazyRead(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
379static int nb_tests = 0;
380static int nb_errors = 0;
381static int nb_leaks = 0;
382static 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 */
389static xmlParserInputPtr
390testExternalEntityLoader(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 */
405static char testErrors[32769];
406static int testErrorsSize = 0;
407
408static void XMLCDECL
409channel(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
437static void
438xmlParserPrintFileContextInternal(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
486static void
487testStructuredErrorHandler(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
668static void
669initializeLibxml2(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
698unsigned 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 */
708static int
709isStandaloneCallback(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 */
723static int
724hasInternalSubsetCallback(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 */
738static int
739hasExternalSubsetCallback(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 */
751static void
752internalSubsetCallback(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 */
767static void
768externalSubsetCallback(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 */
791static xmlParserInputPtr
792resolveEntityCallback(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 */
809static xmlEntityPtr
810getEntityCallback(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 */
826static xmlEntityPtr
827getParameterEntityCallback(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 */
846static void
847entityDeclCallback(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 */
866static void
867attributeDeclCallback(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 */
887static void
888elementDeclCallback(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 */
906static void
907notationDeclCallback(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 */
926static void
927unparsedEntityDeclCallback(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 */
945static void
946setDocumentLocatorCallback(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 */
959static void
960startDocumentCallback(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 */
972static void
973endDocumentCallback(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 */
987static void
988startElementCallback(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 */
1003static void
1004endElementCallback(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 */
1021static void
1022charactersCallback(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 */
1037static void
1038referenceCallback(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 */
1055static void
1056ignorableWhitespaceCallback(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 */
1073static void
1074processingInstructionCallback(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 */
1090static void
1091cdataBlockCallback(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 */
1106static void
1107commentCallback(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 */
1123static void XMLCDECL
1124warningCallback(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 */
1140static void XMLCDECL
1141errorCallback(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 */
1157static void XMLCDECL
1158fatalErrorCallback(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 */
1176static void
1177startElementNsCallback(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 */
1198static void
1199endElementNsCallback(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
1208static 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
1243static 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 */
1262static int
1263saxTest(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 */
1318static int
1319readerTest(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
1374typedef int (*functest) (const char *filename, size_t limit, int options,
1375                         int fail);
1376
1377typedef struct limitDesc limitDesc;
1378typedef limitDesc *limitDescPtr;
1379struct 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
1386static 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
1405typedef struct testDesc testDesc;
1406typedef testDesc *testDescPtr;
1407struct testDesc {
1408    const char *desc; /* descripton of the test */
1409    functest    func; /* function implementing the test */
1410};
1411
1412static
1413testDesc 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
1422typedef struct testException testException;
1423typedef testException *testExceptionPtr;
1424struct 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
1431static
1432testException testExceptions[] = {
1433    /* the SAX parser doesn't hit a limit of XML_MAX_TEXT_LENGTH text nodes */
1434    { 0, 1, 0, 0},
1435};
1436
1437static int
1438launchTests(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
1473static int
1474runtest(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
1498static int
1499launchCrazySAX(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
1515static int
1516launchCrazy(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
1532static 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
1559static int
1560runcrazy(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
1599int
1600main(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}
1635