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