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