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