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