Home | History | Annotate | Download | only in libxml2
      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 (at) 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 
     48 static void reset_timout(void) {
     49     timeout = 0;
     50     t0 = clock();
     51 }
     52 
     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
    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 *
    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
    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 
    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
    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
    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 *
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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; /* descripton 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
   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
   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
   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
   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 
   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
   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
   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