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