• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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,
1288                     (unsigned long) limit);
1289             res = 1;
1290         }
1291     } else {
1292         if (fail) {
1293             fprintf(stderr, "Failed to get failure for '%s' %lu\n",
1294                     filename, (unsigned long) limit);
1295             res = 1;
1296         } else
1297             res = 0;
1298     }
1299     ctxt->sax = old_sax;
1300     xmlFreeParserCtxt(ctxt);
1301 
1302     return(res);
1303 }
1304 #ifdef LIBXML_READER_ENABLED
1305 /**
1306  * readerTest:
1307  * @filename: the file to parse
1308  * @max_size: size of the limit to test
1309  * @options: parsing options
1310  * @fail: should a failure be reported
1311  *
1312  * Parse a memory generated file using the xmlReader
1313  *
1314  * Returns 0 in case of success, an error code otherwise
1315  */
1316 static int
readerTest(const char * filename,size_t limit,int options,int fail)1317 readerTest(const char *filename, size_t limit, int options, int fail) {
1318     xmlTextReaderPtr reader;
1319     int res = 0;
1320     int ret;
1321 
1322     nb_tests++;
1323 
1324     maxlen = limit;
1325     reader = xmlReaderForFile(filename , NULL, options);
1326     if (reader == NULL) {
1327         fprintf(stderr, "Failed to open '%s' test\n", filename);
1328 	return(1);
1329     }
1330     ret = xmlTextReaderRead(reader);
1331     while (ret == 1) {
1332         ret = xmlTextReaderRead(reader);
1333     }
1334     if (ret != 0) {
1335         if (fail)
1336             res = 0;
1337         else {
1338             if (strncmp(filename, "crazy:", 6) == 0)
1339                 fprintf(stderr, "Failed to parse '%s' %u\n",
1340                         filename, crazy_indx);
1341             else
1342                 fprintf(stderr, "Failed to parse '%s' %lu\n",
1343                         filename, (unsigned long) limit);
1344             res = 1;
1345         }
1346     } else {
1347         if (fail) {
1348             if (strncmp(filename, "crazy:", 6) == 0)
1349                 fprintf(stderr, "Failed to get failure for '%s' %u\n",
1350                         filename, crazy_indx);
1351             else
1352                 fprintf(stderr, "Failed to get failure for '%s' %lu\n",
1353                         filename, (unsigned long) limit);
1354             res = 1;
1355         } else
1356             res = 0;
1357     }
1358     if (timeout)
1359         res = 1;
1360     xmlFreeTextReader(reader);
1361 
1362     return(res);
1363 }
1364 #endif
1365 
1366 /************************************************************************
1367  *									*
1368  *			Tests descriptions				*
1369  *									*
1370  ************************************************************************/
1371 
1372 typedef int (*functest) (const char *filename, size_t limit, int options,
1373                          int fail);
1374 
1375 typedef struct limitDesc limitDesc;
1376 typedef limitDesc *limitDescPtr;
1377 struct limitDesc {
1378     const char *name; /* the huge generator name */
1379     size_t limit;     /* the limit to test */
1380     int options;      /* extra parser options */
1381     int fail;         /* whether the test should fail */
1382 };
1383 
1384 static limitDesc limitDescriptions[] = {
1385     /* max length of a text node in content */
1386     {"huge:textNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1387     {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1388     {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1389     /* max length of a text node in content */
1390     {"huge:attrNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1391     {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1392     {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1393     /* max length of a comment node */
1394     {"huge:commentNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1395     {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1396     {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1397     /* max length of a PI node */
1398     {"huge:piNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1399     {"huge:piNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1400     {"huge:piNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1401 };
1402 
1403 typedef struct testDesc testDesc;
1404 typedef testDesc *testDescPtr;
1405 struct testDesc {
1406     const char *desc; /* description of the test */
1407     functest    func; /* function implementing the test */
1408 };
1409 
1410 static
1411 testDesc testDescriptions[] = {
1412     { "Parsing of huge files with the sax parser", saxTest},
1413 /*    { "Parsing of huge files with the tree parser", treeTest}, */
1414 #ifdef LIBXML_READER_ENABLED
1415     { "Parsing of huge files with the reader", readerTest},
1416 #endif
1417     {NULL, NULL}
1418 };
1419 
1420 typedef struct testException testException;
1421 typedef testException *testExceptionPtr;
1422 struct testException {
1423     unsigned int test;  /* the parser test number */
1424     unsigned int limit; /* the limit test number */
1425     int fail;           /* new fail value or -1*/
1426     size_t size;        /* new limit value or 0 */
1427 };
1428 
1429 static
1430 testException testExceptions[] = {
1431     /* the SAX parser doesn't hit a limit of XML_MAX_TEXT_LENGTH text nodes */
1432     { 0, 1, 0, 0},
1433 };
1434 
1435 static int
launchTests(testDescPtr tst,unsigned int test)1436 launchTests(testDescPtr tst, unsigned int test) {
1437     int res = 0, err = 0;
1438     unsigned int i, j;
1439     size_t limit;
1440     int fail;
1441 
1442     if (tst == NULL) return(-1);
1443 
1444     for (i = 0;i < sizeof(limitDescriptions)/sizeof(limitDescriptions[0]);i++) {
1445         limit = limitDescriptions[i].limit;
1446         fail = limitDescriptions[i].fail;
1447         /*
1448          * Handle exceptions if any
1449          */
1450         for (j = 0;j < sizeof(testExceptions)/sizeof(testExceptions[0]);j++) {
1451             if ((testExceptions[j].test == test) &&
1452                 (testExceptions[j].limit == i)) {
1453                 if (testExceptions[j].fail != -1)
1454                     fail = testExceptions[j].fail;
1455                 if (testExceptions[j].size != 0)
1456                     limit = testExceptions[j].size;
1457                 break;
1458             }
1459         }
1460         res = tst->func(limitDescriptions[i].name, limit,
1461                         limitDescriptions[i].options, fail);
1462         if (res != 0) {
1463             nb_errors++;
1464             err++;
1465         }
1466     }
1467     return(err);
1468 }
1469 
1470 
1471 static int
runtest(unsigned int i)1472 runtest(unsigned int i) {
1473     int ret = 0, res;
1474     int old_errors, old_tests, old_leaks;
1475 
1476     old_errors = nb_errors;
1477     old_tests = nb_tests;
1478     old_leaks = nb_leaks;
1479     if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL))
1480 	printf("## %s\n", testDescriptions[i].desc);
1481     res = launchTests(&testDescriptions[i], i);
1482     if (res != 0)
1483 	ret++;
1484     if (verbose) {
1485 	if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1486 	    printf("Ran %d tests, no errors\n", nb_tests - old_tests);
1487 	else
1488 	    printf("Ran %d tests, %d errors, %d leaks\n",
1489 		   nb_tests - old_tests,
1490 		   nb_errors - old_errors,
1491 		   nb_leaks - old_leaks);
1492     }
1493     return(ret);
1494 }
1495 
1496 static int
launchCrazySAX(unsigned int test,int fail)1497 launchCrazySAX(unsigned int test, int fail) {
1498     int res = 0, err = 0;
1499 
1500     crazy_indx = test;
1501 
1502     res = saxTest("crazy::test", XML_MAX_LOOKUP_LIMIT - CHUNK, 0, fail);
1503     if (res != 0) {
1504         nb_errors++;
1505         err++;
1506     }
1507     if (tests_quiet == 0)
1508         fprintf(stderr, "%c", crazy[test]);
1509 
1510     return(err);
1511 }
1512 
1513 #ifdef LIBXML_READER_ENABLED
1514 static int
launchCrazy(unsigned int test,int fail)1515 launchCrazy(unsigned int test, int fail) {
1516     int res = 0, err = 0;
1517 
1518     crazy_indx = test;
1519 
1520     res = readerTest("crazy::test", XML_MAX_LOOKUP_LIMIT - CHUNK, 0, fail);
1521     if (res != 0) {
1522         nb_errors++;
1523         err++;
1524     }
1525     if (tests_quiet == 0)
1526         fprintf(stderr, "%c", crazy[test]);
1527 
1528     return(err);
1529 }
1530 #endif
1531 
get_crazy_fail(int test)1532 static int get_crazy_fail(int test) {
1533     /*
1534      * adding 1000000 of character 'a' leads to parser failure mostly
1535      * everywhere except in those special spots. Need to be updated
1536      * each time crazy is updated
1537      */
1538     int fail = 1;
1539     if ((test == 44) || /* PI in Misc */
1540         ((test >= 50) && (test <= 55)) || /* Comment in Misc */
1541         (test == 79) || /* PI in DTD */
1542         ((test >= 85) && (test <= 90)) || /* Comment in DTD */
1543         (test == 154) || /* PI in Misc */
1544         ((test >= 160) && (test <= 165)) || /* Comment in Misc */
1545         ((test >= 178) && (test <= 181)) || /* attribute value */
1546         (test == 183) || /* Text */
1547         (test == 189) || /* PI in Content */
1548         (test == 191) || /* Text */
1549         ((test >= 195) && (test <= 200)) || /* Comment in Content */
1550         ((test >= 203) && (test <= 206)) || /* Text */
1551         (test == 215) || (test == 216) || /* in CDATA */
1552         (test == 219) || /* Text */
1553         (test == 231) || /* PI in Misc */
1554         ((test >= 237) && (test <= 242))) /* Comment in Misc */
1555         fail = 0;
1556     return(fail);
1557 }
1558 
1559 static int
runcrazy(void)1560 runcrazy(void) {
1561     int ret = 0, res = 0;
1562     int old_errors, old_tests, old_leaks;
1563     unsigned int i;
1564 
1565     old_errors = nb_errors;
1566     old_tests = nb_tests;
1567     old_leaks = nb_leaks;
1568 
1569 #ifdef LIBXML_READER_ENABLED
1570     if (tests_quiet == 0) {
1571 	printf("## Crazy tests on reader\n");
1572     }
1573     for (i = 0;i < strlen(crazy);i++) {
1574         res += launchCrazy(i, get_crazy_fail(i));
1575         if (res != 0)
1576             ret++;
1577     }
1578 #endif
1579 
1580     if (tests_quiet == 0) {
1581 	printf("\n## Crazy tests on SAX\n");
1582     }
1583     for (i = 0;i < strlen(crazy);i++) {
1584         res += launchCrazySAX(i, get_crazy_fail(i));
1585         if (res != 0)
1586             ret++;
1587     }
1588     if (tests_quiet == 0)
1589         fprintf(stderr, "\n");
1590     if (verbose) {
1591 	if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1592 	    printf("Ran %d tests, no errors\n", nb_tests - old_tests);
1593 	else
1594 	    printf("Ran %d tests, %d errors, %d leaks\n",
1595 		   nb_tests - old_tests,
1596 		   nb_errors - old_errors,
1597 		   nb_leaks - old_leaks);
1598     }
1599     return(ret);
1600 }
1601 
1602 
1603 int
main(int argc ATTRIBUTE_UNUSED,char ** argv ATTRIBUTE_UNUSED)1604 main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
1605     int i, a, ret = 0;
1606     int subset = 0;
1607 
1608     fillFilling();
1609     initializeLibxml2();
1610 
1611     for (a = 1; a < argc;a++) {
1612         if (!strcmp(argv[a], "-v"))
1613 	    verbose = 1;
1614         else if (!strcmp(argv[a], "-quiet"))
1615 	    tests_quiet = 1;
1616         else if (!strcmp(argv[a], "-crazy"))
1617 	    subset = 1;
1618     }
1619     if (subset == 0) {
1620 	for (i = 0; testDescriptions[i].func != NULL; i++) {
1621 	    ret += runtest(i);
1622 	}
1623     }
1624     ret += runcrazy();
1625     if ((nb_errors == 0) && (nb_leaks == 0)) {
1626         ret = 0;
1627 	printf("Total %d tests, no errors\n",
1628 	       nb_tests);
1629     } else {
1630         ret = 1;
1631 	printf("Total %d tests, %d errors, %d leaks\n",
1632 	       nb_tests, nb_errors, nb_leaks);
1633     }
1634     xmlCleanupParser();
1635     xmlMemoryDump();
1636 
1637     return(ret);
1638 }
1639