(root)/
libxml2-2.12.3/
testrecurse.c
       1  /*
       2   * testrecurse.c: C program to run libxml2 regression tests checking entities
       3   *            recursions
       4   *
       5   * To compile on Unixes:
       6   * cc -o testrecurse `xml2-config --cflags` testrecurse.c `xml2-config --libs` -lpthread
       7   *
       8   * See Copyright for the status of this software.
       9   *
      10   * daniel@veillard.com
      11   */
      12  
      13  #include "config.h"
      14  #include <stdio.h>
      15  
      16  #include <stdlib.h>
      17  #include <string.h>
      18  #include <sys/stat.h>
      19  
      20  #include <libxml/parser.h>
      21  #include <libxml/parserInternals.h>
      22  #include <libxml/tree.h>
      23  #include <libxml/uri.h>
      24  
      25  /*
      26   * O_BINARY is just for Windows compatibility - if it isn't defined
      27   * on this system, avoid any compilation error
      28   */
      29  #ifdef	O_BINARY
      30  #define RD_FLAGS	O_RDONLY | O_BINARY
      31  #else
      32  #define	RD_FLAGS	O_RDONLY
      33  #endif
      34  
      35  #define OPT_SAX         (1<<0)
      36  #define OPT_NO_SUBST    (1<<1)
      37  
      38  typedef int (*functest) (const char *filename, const char *result,
      39                           const char *error, int options);
      40  
      41  typedef struct testDesc testDesc;
      42  typedef testDesc *testDescPtr;
      43  struct testDesc {
      44      const char *desc; /* description of the test */
      45      functest    func; /* function implementing the test */
      46      const char *in;   /* glob to path for input files */
      47      const char *out;  /* output directory */
      48      const char *suffix;/* suffix for output files */
      49      const char *err;  /* suffix for error output files */
      50      int     options;  /* parser options for the test */
      51  };
      52  
      53  static int checkTestFile(const char *filename);
      54  
      55  
      56  #if defined(_WIN32)
      57  
      58  #include <windows.h>
      59  
      60  typedef struct
      61  {
      62        size_t gl_pathc;    /* Count of paths matched so far  */
      63        char **gl_pathv;    /* List of matched pathnames.  */
      64        size_t gl_offs;     /* Slots to reserve in 'gl_pathv'.  */
      65  } glob_t;
      66  
      67  #define GLOB_DOOFFS 0
      68  static int glob(const char *pattern, ATTRIBUTE_UNUSED int flags,
      69                  ATTRIBUTE_UNUSED int errfunc(const char *epath, int eerrno),
      70                  glob_t *pglob) {
      71      glob_t *ret;
      72      WIN32_FIND_DATA FindFileData;
      73      HANDLE hFind;
      74      unsigned int nb_paths = 0;
      75      char directory[500];
      76      int len;
      77  
      78      if ((pattern == NULL) || (pglob == NULL)) return(-1);
      79  
      80      strncpy(directory, pattern, 499);
      81      for (len = strlen(directory);len >= 0;len--) {
      82          if (directory[len] == '/') {
      83  	    len++;
      84  	    directory[len] = 0;
      85  	    break;
      86  	}
      87      }
      88      if (len <= 0)
      89          len = 0;
      90  
      91  
      92      ret = pglob;
      93      memset(ret, 0, sizeof(glob_t));
      94  
      95      hFind = FindFirstFileA(pattern, &FindFileData);
      96      if (hFind == INVALID_HANDLE_VALUE)
      97          return(0);
      98      nb_paths = 20;
      99      ret->gl_pathv = (char **) malloc(nb_paths * sizeof(char *));
     100      if (ret->gl_pathv == NULL) {
     101  	FindClose(hFind);
     102          return(-1);
     103      }
     104      strncpy(directory + len, FindFileData.cFileName, 499 - len);
     105      ret->gl_pathv[ret->gl_pathc] = strdup(directory);
     106      if (ret->gl_pathv[ret->gl_pathc] == NULL)
     107          goto done;
     108      ret->gl_pathc++;
     109      while(FindNextFileA(hFind, &FindFileData)) {
     110          if (FindFileData.cFileName[0] == '.')
     111  	    continue;
     112          if (ret->gl_pathc + 2 > nb_paths) {
     113              char **tmp = realloc(ret->gl_pathv, nb_paths * 2 * sizeof(char *));
     114              if (tmp == NULL)
     115                  break;
     116              ret->gl_pathv = tmp;
     117              nb_paths *= 2;
     118  	}
     119  	strncpy(directory + len, FindFileData.cFileName, 499 - len);
     120  	ret->gl_pathv[ret->gl_pathc] = strdup(directory);
     121          if (ret->gl_pathv[ret->gl_pathc] == NULL)
     122              break;
     123          ret->gl_pathc++;
     124      }
     125      ret->gl_pathv[ret->gl_pathc] = NULL;
     126  
     127  done:
     128      FindClose(hFind);
     129      return(0);
     130  }
     131  
     132  
     133  
     134  static void globfree(glob_t *pglob) {
     135      unsigned int i;
     136      if (pglob == NULL)
     137          return;
     138  
     139      for (i = 0;i < pglob->gl_pathc;i++) {
     140           if (pglob->gl_pathv[i] != NULL)
     141               free(pglob->gl_pathv[i]);
     142      }
     143  }
     144  
     145  #else
     146  #include <glob.h>
     147  #endif
     148  
     149  /************************************************************************
     150   *									*
     151   *		Huge document generator					*
     152   *									*
     153   ************************************************************************/
     154  
     155  #include <libxml/xmlIO.h>
     156  
     157  typedef struct {
     158      const char *URL;
     159      const char *start;
     160      const char *segment;
     161      const char *finish;
     162  } xmlHugeDocParts;
     163  
     164  static const xmlHugeDocParts hugeDocTable[] = {
     165      {
     166          "test/recurse/huge.xml",
     167  
     168          "<!DOCTYPE foo ["
     169          "<!ELEMENT foo (bar*)> "
     170          "<!ELEMENT bar (#PCDATA)> "
     171          "<!ATTLIST bar attr CDATA #IMPLIED> "
     172          "<!ENTITY a SYSTEM 'ga.ent'> "
     173          "<!ENTITY b SYSTEM 'gb.ent'> "
     174          "<!ENTITY c SYSTEM 'gc.ent'> "
     175          "<!ENTITY f 'some internal data'> "
     176          "<!ENTITY e '&f;&f;'> "
     177          "<!ENTITY d '&e;&e;'> "
     178          "]> "
     179          "<foo>",
     180  
     181          "  <bar attr='&e; &f; &d;'>&a; &b; &c; &e; &f; &d;</bar>\n"
     182          "  <bar>_123456789_123456789_123456789_123456789</bar>\n"
     183          "  <bar>_123456789_123456789_123456789_123456789</bar>\n"
     184          "  <bar>_123456789_123456789_123456789_123456789</bar>\n"
     185          "  <bar>_123456789_123456789_123456789_123456789</bar>\n",
     186  
     187          "</foo>"
     188      },
     189      {
     190          "test/recurse/huge_dtd.dtd",
     191  
     192          "<!ELEMENT foo (#PCDATA)>\n"
     193          "<!ENTITY ent 'success'>\n"
     194          "<!ENTITY % a SYSTEM 'pa.ent'>\n"
     195          "<!ENTITY % b SYSTEM 'pb.ent'>\n"
     196          "<!ENTITY % c SYSTEM 'pc.ent'>\n"
     197          "<!ENTITY % d '<!-- comment -->'>\n"
     198          "<!ENTITY % e '%d;%d;'>\n"
     199          "<!ENTITY % f '%e;%e;'>\n",
     200  
     201          "<!ENTITY ent '%a; %b; %c; %d; %e; %f;'>\n"
     202          "%a; %b; %c; %d; %e; %f;\n"
     203          "<!-- _123456789_123456789_123456789_123456789 -->\n"
     204          "<!-- _123456789_123456789_123456789_123456789 -->\n"
     205          "<!-- _123456789_123456789_123456789_123456789 -->\n",
     206  
     207          ""
     208      },
     209      { NULL, NULL, NULL, NULL }
     210  };
     211  
     212  static const xmlHugeDocParts *hugeDocParts;
     213  static int curseg = 0;
     214  static const char *current;
     215  static int rlen;
     216  
     217  /**
     218   * hugeMatch:
     219   * @URI: an URI to test
     220   *
     221   * Check for a huge query
     222   *
     223   * Returns 1 if yes and 0 if another Input module should be used
     224   */
     225  static int
     226  hugeMatch(const char * URI) {
     227      int i;
     228  
     229      if (URI == NULL)
     230          return(0);
     231  
     232      for (i = 0; hugeDocTable[i].URL; i++) {
     233          if (strcmp(URI, hugeDocTable[i].URL) == 0)
     234              return(1);
     235      }
     236  
     237      return(0);
     238  }
     239  
     240  /**
     241   * hugeOpen:
     242   * @URI: an URI to test
     243   *
     244   * Return a pointer to the huge query handler, in this example simply
     245   * the current pointer...
     246   *
     247   * Returns an Input context or NULL in case or error
     248   */
     249  static void *
     250  hugeOpen(const char * URI) {
     251      int i;
     252  
     253      if (URI == NULL)
     254          return(NULL);
     255  
     256      for (i = 0; hugeDocTable[i].URL; i++) {
     257          if (strcmp(URI, hugeDocTable[i].URL) == 0) {
     258              hugeDocParts = hugeDocTable + i;
     259              curseg = 0;
     260              current = hugeDocParts->start;
     261              rlen = strlen(current);
     262              return((void *) current);
     263          }
     264      }
     265  
     266      return(NULL);
     267  }
     268  
     269  /**
     270   * hugeClose:
     271   * @context: the read context
     272   *
     273   * Close the huge query handler
     274   *
     275   * Returns 0 or -1 in case of error
     276   */
     277  static int
     278  hugeClose(void * context) {
     279      if (context == NULL) return(-1);
     280      return(0);
     281  }
     282  
     283  #define MAX_NODES 1000
     284  
     285  /**
     286   * hugeRead:
     287   * @context: the read context
     288   * @buffer: where to store data
     289   * @len: number of bytes to read
     290   *
     291   * Implement an huge query read.
     292   *
     293   * Returns the number of bytes read or -1 in case of error
     294   */
     295  static int
     296  hugeRead(void *context, char *buffer, int len)
     297  {
     298      if ((context == NULL) || (buffer == NULL) || (len < 0))
     299          return (-1);
     300  
     301      if (len >= rlen) {
     302          if (curseg >= MAX_NODES + 1) {
     303              rlen = 0;
     304              return(0);
     305          }
     306          len = rlen;
     307          rlen = 0;
     308  	memcpy(buffer, current, len);
     309          curseg ++;
     310          if (curseg == MAX_NODES) {
     311              current = hugeDocParts->finish;
     312  	} else {
     313              current = hugeDocParts->segment;
     314  	}
     315          rlen = strlen(current);
     316      } else {
     317  	memcpy(buffer, current, len);
     318  	rlen -= len;
     319          current += len;
     320      }
     321      return (len);
     322  }
     323  
     324  /************************************************************************
     325   *									*
     326   *		Libxml2 specific routines				*
     327   *									*
     328   ************************************************************************/
     329  
     330  static int nb_tests = 0;
     331  static int nb_errors = 0;
     332  static int nb_leaks = 0;
     333  static int extraMemoryFromResolver = 0;
     334  
     335  static int
     336  fatalError(void) {
     337      fprintf(stderr, "Exitting tests on fatal error\n");
     338      exit(1);
     339  }
     340  
     341  /*
     342   * We need to trap calls to the resolver to not account memory for the catalog
     343   * which is shared to the current running test. We also don't want to have
     344   * network downloads modifying tests.
     345   */
     346  static xmlParserInputPtr
     347  testExternalEntityLoader(const char *URL, const char *ID,
     348  			 xmlParserCtxtPtr ctxt) {
     349      xmlParserInputPtr ret;
     350  
     351      if (checkTestFile(URL)) {
     352  	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
     353      } else {
     354  	int memused = xmlMemUsed();
     355  	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
     356  	extraMemoryFromResolver += xmlMemUsed() - memused;
     357      }
     358  
     359      return(ret);
     360  }
     361  
     362  /*
     363   * Trapping the error messages at the generic level to grab the equivalent of
     364   * stderr messages on CLI tools.
     365   */
     366  static char testErrors[32769];
     367  static int testErrorsSize = 0;
     368  
     369  static void
     370  channel(void *ctx  ATTRIBUTE_UNUSED, const char *msg, ...) {
     371      va_list args;
     372      int res;
     373  
     374      if (testErrorsSize >= 32768)
     375          return;
     376      va_start(args, msg);
     377      res = vsnprintf(&testErrors[testErrorsSize],
     378                      32768 - testErrorsSize,
     379  		    msg, args);
     380      va_end(args);
     381      if (testErrorsSize + res >= 32768) {
     382          /* buffer is full */
     383  	testErrorsSize = 32768;
     384  	testErrors[testErrorsSize] = 0;
     385      } else {
     386          testErrorsSize += res;
     387      }
     388      testErrors[testErrorsSize] = 0;
     389  }
     390  
     391  /**
     392   * xmlParserPrintFileContext:
     393   * @input:  an xmlParserInputPtr input
     394   *
     395   * Displays current context within the input content for error tracking
     396   */
     397  
     398  static void
     399  xmlParserPrintFileContextInternal(xmlParserInputPtr input ,
     400  		xmlGenericErrorFunc chanl, void *data ) {
     401      const xmlChar *cur, *base;
     402      unsigned int n, col;	/* GCC warns if signed, because compared with sizeof() */
     403      xmlChar  content[81]; /* space for 80 chars + line terminator */
     404      xmlChar *ctnt;
     405  
     406      if (input == NULL) return;
     407      cur = input->cur;
     408      base = input->base;
     409      /* skip backwards over any end-of-lines */
     410      while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) {
     411  	cur--;
     412      }
     413      n = 0;
     414      /* search backwards for beginning-of-line (to max buff size) */
     415      while ((n++ < (sizeof(content)-1)) && (cur > base) &&
     416     (*(cur) != '\n') && (*(cur) != '\r'))
     417          cur--;
     418      if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;
     419      /* calculate the error position in terms of the current position */
     420      col = input->cur - cur;
     421      /* search forward for end-of-line (to max buff size) */
     422      n = 0;
     423      ctnt = content;
     424      /* copy selected text to our buffer */
     425      while ((*cur != 0) && (*(cur) != '\n') &&
     426     (*(cur) != '\r') && (n < sizeof(content)-1)) {
     427  		*ctnt++ = *cur++;
     428  	n++;
     429      }
     430      *ctnt = 0;
     431      /* print out the selected text */
     432      chanl(data ,"%s\n", content);
     433      /* create blank line with problem pointer */
     434      n = 0;
     435      ctnt = content;
     436      /* (leave buffer space for pointer + line terminator) */
     437      while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) {
     438  	if (*(ctnt) != '\t')
     439  	    *(ctnt) = ' ';
     440  	ctnt++;
     441      }
     442      *ctnt++ = '^';
     443      *ctnt = 0;
     444      chanl(data ,"%s\n", content);
     445  }
     446  
     447  static void
     448  testStructuredErrorHandler(void *ctx ATTRIBUTE_UNUSED, const xmlError *err) {
     449      char *file = NULL;
     450      int line = 0;
     451      int code = -1;
     452      int domain;
     453      void *data = NULL;
     454      const char *str;
     455      const xmlChar *name = NULL;
     456      xmlNodePtr node;
     457      xmlErrorLevel level;
     458      xmlParserInputPtr input = NULL;
     459      xmlParserInputPtr cur = NULL;
     460      xmlParserCtxtPtr ctxt = NULL;
     461  
     462      if (err == NULL)
     463          return;
     464  
     465      file = err->file;
     466      line = err->line;
     467      code = err->code;
     468      domain = err->domain;
     469      level = err->level;
     470      node = err->node;
     471      if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) ||
     472          (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) ||
     473  	(domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) {
     474  	ctxt = err->ctxt;
     475      }
     476      str = err->message;
     477  
     478      if (code == XML_ERR_OK)
     479          return;
     480  
     481      if ((node != NULL) && (node->type == XML_ELEMENT_NODE))
     482          name = node->name;
     483  
     484      /*
     485       * Maintain the compatibility with the legacy error handling
     486       */
     487      if (ctxt != NULL) {
     488          input = ctxt->input;
     489          if ((input != NULL) && (input->filename == NULL) &&
     490              (ctxt->inputNr > 1)) {
     491              cur = input;
     492              input = ctxt->inputTab[ctxt->inputNr - 2];
     493          }
     494          if (input != NULL) {
     495              if (input->filename)
     496                  channel(data, "%s:%d: ", input->filename, input->line);
     497              else if ((line != 0) && (domain == XML_FROM_PARSER))
     498                  channel(data, "Entity: line %d: ", input->line);
     499          }
     500      } else {
     501          if (file != NULL)
     502              channel(data, "%s:%d: ", file, line);
     503          else if ((line != 0) && (domain == XML_FROM_PARSER))
     504              channel(data, "Entity: line %d: ", line);
     505      }
     506      if (name != NULL) {
     507          channel(data, "element %s: ", name);
     508      }
     509      if (code == XML_ERR_OK)
     510          return;
     511      switch (domain) {
     512          case XML_FROM_PARSER:
     513              channel(data, "parser ");
     514              break;
     515          case XML_FROM_NAMESPACE:
     516              channel(data, "namespace ");
     517              break;
     518          case XML_FROM_DTD:
     519          case XML_FROM_VALID:
     520              channel(data, "validity ");
     521              break;
     522          case XML_FROM_HTML:
     523              channel(data, "HTML parser ");
     524              break;
     525          case XML_FROM_MEMORY:
     526              channel(data, "memory ");
     527              break;
     528          case XML_FROM_OUTPUT:
     529              channel(data, "output ");
     530              break;
     531          case XML_FROM_IO:
     532              channel(data, "I/O ");
     533              break;
     534          case XML_FROM_XINCLUDE:
     535              channel(data, "XInclude ");
     536              break;
     537          case XML_FROM_XPATH:
     538              channel(data, "XPath ");
     539              break;
     540          case XML_FROM_XPOINTER:
     541              channel(data, "parser ");
     542              break;
     543          case XML_FROM_REGEXP:
     544              channel(data, "regexp ");
     545              break;
     546          case XML_FROM_MODULE:
     547              channel(data, "module ");
     548              break;
     549          case XML_FROM_SCHEMASV:
     550              channel(data, "Schemas validity ");
     551              break;
     552          case XML_FROM_SCHEMASP:
     553              channel(data, "Schemas parser ");
     554              break;
     555          case XML_FROM_RELAXNGP:
     556              channel(data, "Relax-NG parser ");
     557              break;
     558          case XML_FROM_RELAXNGV:
     559              channel(data, "Relax-NG validity ");
     560              break;
     561          case XML_FROM_CATALOG:
     562              channel(data, "Catalog ");
     563              break;
     564          case XML_FROM_C14N:
     565              channel(data, "C14N ");
     566              break;
     567          case XML_FROM_XSLT:
     568              channel(data, "XSLT ");
     569              break;
     570          default:
     571              break;
     572      }
     573      if (code == XML_ERR_OK)
     574          return;
     575      switch (level) {
     576          case XML_ERR_NONE:
     577              channel(data, ": ");
     578              break;
     579          case XML_ERR_WARNING:
     580              channel(data, "warning : ");
     581              break;
     582          case XML_ERR_ERROR:
     583              channel(data, "error : ");
     584              break;
     585          case XML_ERR_FATAL:
     586              channel(data, "error : ");
     587              break;
     588      }
     589      if (code == XML_ERR_OK)
     590          return;
     591      if (str != NULL) {
     592          int len;
     593  	len = xmlStrlen((const xmlChar *)str);
     594  	if ((len > 0) && (str[len - 1] != '\n'))
     595  	    channel(data, "%s\n", str);
     596  	else
     597  	    channel(data, "%s", str);
     598      } else {
     599          channel(data, "%s\n", "out of memory error");
     600      }
     601      if (code == XML_ERR_OK)
     602          return;
     603  
     604      if (ctxt != NULL) {
     605          xmlParserPrintFileContextInternal(input, channel, data);
     606          if (cur != NULL) {
     607              if (cur->filename)
     608                  channel(data, "%s:%d: \n", cur->filename, cur->line);
     609              else if ((line != 0) && (domain == XML_FROM_PARSER))
     610                  channel(data, "Entity: line %d: \n", cur->line);
     611              xmlParserPrintFileContextInternal(cur, channel, data);
     612          }
     613      }
     614      if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) &&
     615          (err->int1 < 100) &&
     616  	(err->int1 < xmlStrlen((const xmlChar *)err->str1))) {
     617  	xmlChar buf[150];
     618  	int i;
     619  
     620  	channel(data, "%s\n", err->str1);
     621  	for (i=0;i < err->int1;i++)
     622  	     buf[i] = ' ';
     623  	buf[i++] = '^';
     624  	buf[i] = 0;
     625  	channel(data, "%s\n", buf);
     626      }
     627  }
     628  
     629  static void
     630  initializeLibxml2(void) {
     631      xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
     632      xmlInitParser();
     633      xmlSetExternalEntityLoader(testExternalEntityLoader);
     634      xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
     635      /*
     636       * register the new I/O handlers
     637       */
     638      if (xmlRegisterInputCallbacks(hugeMatch, hugeOpen,
     639                                    hugeRead, hugeClose) < 0) {
     640          fprintf(stderr, "failed to register Huge handler\n");
     641  	exit(1);
     642      }
     643  }
     644  
     645  static void
     646  initSAX(xmlParserCtxtPtr ctxt) {
     647      ctxt->sax->startElementNs = NULL;
     648      ctxt->sax->endElementNs = NULL;
     649      ctxt->sax->startElement = NULL;
     650      ctxt->sax->endElement = NULL;
     651      ctxt->sax->characters = NULL;
     652      ctxt->sax->cdataBlock = NULL;
     653      ctxt->sax->ignorableWhitespace = NULL;
     654      ctxt->sax->processingInstruction = NULL;
     655      ctxt->sax->comment = NULL;
     656  }
     657  
     658  /************************************************************************
     659   *									*
     660   *		File name and path utilities				*
     661   *									*
     662   ************************************************************************/
     663  
     664  static const char *baseFilename(const char *filename) {
     665      const char *cur;
     666      if (filename == NULL)
     667          return(NULL);
     668      cur = &filename[strlen(filename)];
     669      while ((cur > filename) && (*cur != '/'))
     670          cur--;
     671      if (*cur == '/')
     672          return(cur + 1);
     673      return(cur);
     674  }
     675  
     676  static char *resultFilename(const char *filename, const char *out,
     677                              const char *suffix) {
     678      const char *base;
     679      char res[500];
     680      char suffixbuff[500];
     681  
     682  /*************
     683      if ((filename[0] == 't') && (filename[1] == 'e') &&
     684          (filename[2] == 's') && (filename[3] == 't') &&
     685  	(filename[4] == '/'))
     686  	filename = &filename[5];
     687   *************/
     688  
     689      base = baseFilename(filename);
     690      if (suffix == NULL)
     691          suffix = ".tmp";
     692      if (out == NULL)
     693          out = "";
     694  
     695      strncpy(suffixbuff,suffix,499);
     696  #ifdef VMS
     697      if(strstr(base,".") && suffixbuff[0]=='.')
     698        suffixbuff[0]='_';
     699  #endif
     700  
     701      if (snprintf(res, 499, "%s%s%s", out, base, suffixbuff) >= 499)
     702          res[499] = 0;
     703      return(strdup(res));
     704  }
     705  
     706  static int checkTestFile(const char *filename) {
     707      struct stat buf;
     708  
     709      if (stat(filename, &buf) == -1)
     710          return(0);
     711  
     712  #if defined(_WIN32)
     713      if (!(buf.st_mode & _S_IFREG))
     714          return(0);
     715  #else
     716      if (!S_ISREG(buf.st_mode))
     717          return(0);
     718  #endif
     719  
     720      return(1);
     721  }
     722  
     723  
     724  
     725  /************************************************************************
     726   *									*
     727   *		Test to detect or not recursive entities		*
     728   *									*
     729   ************************************************************************/
     730  /**
     731   * recursiveDetectTest:
     732   * @filename: the file to parse
     733   * @result: the file with expected result
     734   * @err: the file with error messages: unused
     735   *
     736   * Parse a file loading DTD and replacing entities check it fails for
     737   * lol cases
     738   *
     739   * Returns 0 in case of success, an error code otherwise
     740   */
     741  static int
     742  recursiveDetectTest(const char *filename,
     743               const char *result ATTRIBUTE_UNUSED,
     744               const char *err ATTRIBUTE_UNUSED,
     745  	     int options) {
     746      xmlDocPtr doc;
     747      xmlParserCtxtPtr ctxt;
     748      int res = 0;
     749      /*
     750       * XML_PARSE_DTDVALID is the only way to load external entities
     751       * without XML_PARSE_NOENT. The validation result doesn't matter
     752       * anyway.
     753       */
     754      int parserOptions = XML_PARSE_DTDVALID;
     755  
     756      nb_tests++;
     757  
     758      ctxt = xmlNewParserCtxt();
     759      if (options & OPT_SAX)
     760          initSAX(ctxt);
     761      if ((options & OPT_NO_SUBST) == 0)
     762          parserOptions |= XML_PARSE_NOENT;
     763      /*
     764       * base of the test, parse with the old API
     765       */
     766      doc = xmlCtxtReadFile(ctxt, filename, NULL, parserOptions);
     767      if ((doc != NULL) || (ctxt->lastError.code != XML_ERR_ENTITY_LOOP)) {
     768          fprintf(stderr, "Failed to detect recursion in %s\n", filename);
     769  	xmlFreeParserCtxt(ctxt);
     770  	xmlFreeDoc(doc);
     771          return(1);
     772      }
     773      xmlFreeParserCtxt(ctxt);
     774  
     775      return(res);
     776  }
     777  
     778  /**
     779   * notRecursiveDetectTest:
     780   * @filename: the file to parse
     781   * @result: the file with expected result
     782   * @err: the file with error messages: unused
     783   *
     784   * Parse a file loading DTD and replacing entities check it works for
     785   * good cases
     786   *
     787   * Returns 0 in case of success, an error code otherwise
     788   */
     789  static int
     790  notRecursiveDetectTest(const char *filename,
     791               const char *result ATTRIBUTE_UNUSED,
     792               const char *err ATTRIBUTE_UNUSED,
     793  	     int options) {
     794      xmlDocPtr doc;
     795      xmlParserCtxtPtr ctxt;
     796      int res = 0;
     797      int parserOptions = XML_PARSE_DTDLOAD;
     798  
     799      nb_tests++;
     800  
     801      ctxt = xmlNewParserCtxt();
     802      if (options & OPT_SAX)
     803          initSAX(ctxt);
     804      if ((options & OPT_NO_SUBST) == 0)
     805          parserOptions |= XML_PARSE_NOENT;
     806      /*
     807       * base of the test, parse with the old API
     808       */
     809      doc = xmlCtxtReadFile(ctxt, filename, NULL, parserOptions);
     810      if (doc == NULL) {
     811          fprintf(stderr, "Failed to parse correct file %s\n", filename);
     812  	xmlFreeParserCtxt(ctxt);
     813          return(1);
     814      }
     815      xmlFreeDoc(doc);
     816      xmlFreeParserCtxt(ctxt);
     817  
     818      return(res);
     819  }
     820  
     821  /**
     822   * notRecursiveHugeTest:
     823   * @filename: the file to parse
     824   * @result: the file with expected result
     825   * @err: the file with error messages: unused
     826   *
     827   * Parse a memory generated file
     828   * good cases
     829   *
     830   * Returns 0 in case of success, an error code otherwise
     831   */
     832  static int
     833  notRecursiveHugeTest(const char *filename ATTRIBUTE_UNUSED,
     834               const char *result ATTRIBUTE_UNUSED,
     835               const char *err ATTRIBUTE_UNUSED,
     836  	     int options) {
     837      xmlParserCtxtPtr ctxt;
     838      xmlDocPtr doc;
     839      int res = 0;
     840      int parserOptions = XML_PARSE_DTDVALID;
     841  
     842      nb_tests++;
     843  
     844      ctxt = xmlNewParserCtxt();
     845      if (options & OPT_SAX)
     846          initSAX(ctxt);
     847      if ((options & OPT_NO_SUBST) == 0)
     848          parserOptions |= XML_PARSE_NOENT;
     849      doc = xmlCtxtReadFile(ctxt, "test/recurse/huge.xml", NULL, parserOptions);
     850      if (doc == NULL) {
     851          fprintf(stderr, "Failed to parse huge.xml\n");
     852  	res = 1;
     853      } else {
     854          xmlEntityPtr ent;
     855          unsigned long fixed_cost = 20;
     856          unsigned long allowed_expansion = 1000000;
     857          unsigned long f_size = xmlStrlen(BAD_CAST "some internal data");
     858          unsigned long e_size;
     859          unsigned long d_size;
     860          unsigned long total_size;
     861  
     862          ent = xmlGetDocEntity(doc, BAD_CAST "e");
     863          e_size = f_size * 2 +
     864                   xmlStrlen(BAD_CAST "&f;") * 2 +
     865                   fixed_cost * 2;
     866          if (ent->expandedSize != e_size) {
     867              fprintf(stderr, "Wrong size for entity e: %lu (expected %lu)\n",
     868                      ent->expandedSize, e_size);
     869              res = 1;
     870          }
     871  
     872          ent = xmlGetDocEntity(doc, BAD_CAST "b");
     873          if (ent->expandedSize != e_size) {
     874              fprintf(stderr, "Wrong size for entity b: %lu (expected %lu)\n",
     875                      ent->expandedSize, e_size);
     876              res = 1;
     877          }
     878  
     879          ent = xmlGetDocEntity(doc, BAD_CAST "d");
     880          d_size = e_size * 2 +
     881                   xmlStrlen(BAD_CAST "&e;") * 2 +
     882                   fixed_cost * 2;
     883          if (ent->expandedSize != d_size) {
     884              fprintf(stderr, "Wrong size for entity d: %lu (expected %lu)\n",
     885                      ent->expandedSize, d_size);
     886              res = 1;
     887          }
     888  
     889          ent = xmlGetDocEntity(doc, BAD_CAST "c");
     890          if (ent->expandedSize != d_size) {
     891              fprintf(stderr, "Wrong size for entity c: %lu (expected %lu)\n",
     892                      ent->expandedSize, d_size);
     893              res = 1;
     894          }
     895  
     896          if (ctxt->sizeentcopy < allowed_expansion) {
     897              fprintf(stderr, "Total entity size too small: %lu\n",
     898                      ctxt->sizeentcopy);
     899              res = 1;
     900          }
     901  
     902          total_size = (f_size + e_size + d_size + 3 * fixed_cost) *
     903                       (MAX_NODES - 1) * 3;
     904          if (ctxt->sizeentcopy != total_size) {
     905              fprintf(stderr, "Wrong total entity size: %lu (expected %lu)\n",
     906                      ctxt->sizeentcopy, total_size);
     907              res = 1;
     908          }
     909  
     910          if (ctxt->sizeentities != 30) {
     911              fprintf(stderr, "Wrong parsed entity size: %lu (expected %lu)\n",
     912                      ctxt->sizeentities, 30lu);
     913              res = 1;
     914          }
     915      }
     916  
     917      xmlFreeDoc(doc);
     918      xmlFreeParserCtxt(ctxt);
     919  
     920      return(res);
     921  }
     922  
     923  /**
     924   * notRecursiveHugeTest:
     925   * @filename: the file to parse
     926   * @result: the file with expected result
     927   * @err: the file with error messages: unused
     928   *
     929   * Parse a memory generated file
     930   * good cases
     931   *
     932   * Returns 0 in case of success, an error code otherwise
     933   */
     934  static int
     935  hugeDtdTest(const char *filename ATTRIBUTE_UNUSED,
     936              const char *result ATTRIBUTE_UNUSED,
     937              const char *err ATTRIBUTE_UNUSED,
     938              int options) {
     939      xmlParserCtxtPtr ctxt;
     940      xmlDocPtr doc;
     941      int res = 0;
     942      int parserOptions = XML_PARSE_DTDVALID;
     943  
     944      nb_tests++;
     945  
     946      ctxt = xmlNewParserCtxt();
     947      if (options & OPT_SAX)
     948          initSAX(ctxt);
     949      if ((options & OPT_NO_SUBST) == 0)
     950          parserOptions |= XML_PARSE_NOENT;
     951      doc = xmlCtxtReadFile(ctxt, "test/recurse/huge_dtd.xml", NULL,
     952                            parserOptions);
     953      if (doc == NULL) {
     954          fprintf(stderr, "Failed to parse huge_dtd.xml\n");
     955  	res = 1;
     956      } else {
     957          unsigned long fixed_cost = 20;
     958          unsigned long allowed_expansion = 1000000;
     959          unsigned long a_size = xmlStrlen(BAD_CAST "<!-- comment -->");
     960          unsigned long b_size;
     961          unsigned long c_size;
     962          unsigned long e_size;
     963          unsigned long f_size;
     964          unsigned long total_size;
     965  
     966          if (ctxt->sizeentcopy < allowed_expansion) {
     967              fprintf(stderr, "Total entity size too small: %lu\n",
     968                      ctxt->sizeentcopy);
     969              res = 1;
     970          }
     971  
     972          b_size = (a_size + strlen("&a;") + fixed_cost) * 2;
     973          c_size = (b_size + strlen("&b;") + fixed_cost) * 2;
     974          /*
     975           * Internal parameter entites are substitued eagerly and
     976           * need different accounting.
     977           */
     978          e_size = a_size * 2;
     979          f_size = e_size * 2;
     980          total_size = /* internal */
     981                       e_size + f_size + fixed_cost * 4 +
     982                       (a_size + e_size + f_size + fixed_cost * 3) *
     983                       (MAX_NODES - 1) * 2 +
     984                       /* external */
     985                       (a_size + b_size + c_size + fixed_cost * 3) *
     986                       (MAX_NODES - 1) * 2 +
     987                       /* final reference in main doc */
     988                       strlen("success") + fixed_cost;
     989          if (ctxt->sizeentcopy != total_size) {
     990              fprintf(stderr, "Wrong total entity size: %lu (expected %lu)\n",
     991                      ctxt->sizeentcopy, total_size);
     992              res = 1;
     993          }
     994  
     995          total_size = strlen(hugeDocParts->start) +
     996                       strlen(hugeDocParts->segment) * (MAX_NODES - 1) +
     997                       strlen(hugeDocParts->finish) +
     998                       /*
     999                        * Other external entities pa.ent, pb.ent, pc.ent.
    1000                        * These are currently counted twice because they're
    1001                        * used both in DTD and EntityValue.
    1002                        */
    1003                       (16 + 6 + 6) * 2;
    1004          if (ctxt->sizeentities != total_size) {
    1005              fprintf(stderr, "Wrong parsed entity size: %lu (expected %lu)\n",
    1006                      ctxt->sizeentities, total_size);
    1007              res = 1;
    1008          }
    1009      }
    1010  
    1011      xmlFreeDoc(doc);
    1012      xmlFreeParserCtxt(ctxt);
    1013  
    1014      return(res);
    1015  }
    1016  
    1017  /************************************************************************
    1018   *									*
    1019   *			Tests Descriptions				*
    1020   *									*
    1021   ************************************************************************/
    1022  
    1023  static
    1024  testDesc testDescriptions[] = {
    1025      { "Parsing recursive test cases" ,
    1026        recursiveDetectTest, "./test/recurse/lol*.xml", NULL, NULL, NULL,
    1027        0 },
    1028      { "Parsing recursive test cases (no substitution)" ,
    1029        recursiveDetectTest, "./test/recurse/lol*.xml", NULL, NULL, NULL,
    1030        OPT_NO_SUBST },
    1031      { "Parsing recursive test cases (SAX)" ,
    1032        recursiveDetectTest, "./test/recurse/lol*.xml", NULL, NULL, NULL,
    1033        OPT_SAX },
    1034      { "Parsing recursive test cases (SAX, no substitution)" ,
    1035        recursiveDetectTest, "./test/recurse/lol*.xml", NULL, NULL, NULL,
    1036        OPT_SAX | OPT_NO_SUBST },
    1037      { "Parsing non-recursive test cases" ,
    1038        notRecursiveDetectTest, "./test/recurse/good*.xml", NULL, NULL, NULL,
    1039        0 },
    1040      { "Parsing non-recursive test cases (SAX)" ,
    1041        notRecursiveDetectTest, "./test/recurse/good*.xml", NULL, NULL, NULL,
    1042        OPT_SAX },
    1043      { "Parsing non-recursive huge case" ,
    1044        notRecursiveHugeTest, NULL, NULL, NULL, NULL,
    1045        0 },
    1046      { "Parsing non-recursive huge case (no substitution)" ,
    1047        notRecursiveHugeTest, NULL, NULL, NULL, NULL,
    1048        OPT_NO_SUBST },
    1049      { "Parsing non-recursive huge case (SAX)" ,
    1050        notRecursiveHugeTest, NULL, NULL, NULL, NULL,
    1051        OPT_SAX },
    1052      { "Parsing non-recursive huge case (SAX, no substitution)" ,
    1053        notRecursiveHugeTest, NULL, NULL, NULL, NULL,
    1054        OPT_SAX | OPT_NO_SUBST },
    1055      { "Parsing non-recursive huge DTD case" ,
    1056        hugeDtdTest, NULL, NULL, NULL, NULL,
    1057        0 },
    1058      {NULL, NULL, NULL, NULL, NULL, NULL, 0}
    1059  };
    1060  
    1061  /************************************************************************
    1062   *									*
    1063   *		The main code driving the tests				*
    1064   *									*
    1065   ************************************************************************/
    1066  
    1067  static int
    1068  launchTests(testDescPtr tst) {
    1069      int res = 0, err = 0;
    1070      size_t i;
    1071      char *result;
    1072      char *error;
    1073      int mem;
    1074  
    1075      if (tst == NULL) return(-1);
    1076      if (tst->in != NULL) {
    1077  	glob_t globbuf;
    1078  
    1079  	globbuf.gl_offs = 0;
    1080  	glob(tst->in, GLOB_DOOFFS, NULL, &globbuf);
    1081  	for (i = 0;i < globbuf.gl_pathc;i++) {
    1082  	    if (!checkTestFile(globbuf.gl_pathv[i]))
    1083  	        continue;
    1084  	    if (tst->suffix != NULL) {
    1085  		result = resultFilename(globbuf.gl_pathv[i], tst->out,
    1086  					tst->suffix);
    1087  		if (result == NULL) {
    1088  		    fprintf(stderr, "Out of memory !\n");
    1089  		    fatalError();
    1090  		}
    1091  	    } else {
    1092  	        result = NULL;
    1093  	    }
    1094  	    if (tst->err != NULL) {
    1095  		error = resultFilename(globbuf.gl_pathv[i], tst->out,
    1096  		                        tst->err);
    1097  		if (error == NULL) {
    1098  		    fprintf(stderr, "Out of memory !\n");
    1099  		    fatalError();
    1100  		}
    1101  	    } else {
    1102  	        error = NULL;
    1103  	    }
    1104  	    if ((result) &&(!checkTestFile(result))) {
    1105  	        fprintf(stderr, "Missing result file %s\n", result);
    1106  	    } else if ((error) &&(!checkTestFile(error))) {
    1107  	        fprintf(stderr, "Missing error file %s\n", error);
    1108  	    } else {
    1109  		mem = xmlMemUsed();
    1110  		extraMemoryFromResolver = 0;
    1111  		testErrorsSize = 0;
    1112  		testErrors[0] = 0;
    1113  		res = tst->func(globbuf.gl_pathv[i], result, error,
    1114  		                tst->options | XML_PARSE_COMPACT);
    1115  		xmlResetLastError();
    1116  		if (res != 0) {
    1117  		    fprintf(stderr, "File %s generated an error\n",
    1118  		            globbuf.gl_pathv[i]);
    1119  		    nb_errors++;
    1120  		    err++;
    1121  		}
    1122  		else if (xmlMemUsed() != mem) {
    1123  		    if ((xmlMemUsed() != mem) &&
    1124  		        (extraMemoryFromResolver == 0)) {
    1125  			fprintf(stderr, "File %s leaked %d bytes\n",
    1126  				globbuf.gl_pathv[i], xmlMemUsed() - mem);
    1127  			nb_leaks++;
    1128  			err++;
    1129  		    }
    1130  		}
    1131  		testErrorsSize = 0;
    1132  	    }
    1133  	    if (result)
    1134  		free(result);
    1135  	    if (error)
    1136  		free(error);
    1137  	}
    1138  	globfree(&globbuf);
    1139      } else {
    1140          testErrorsSize = 0;
    1141  	testErrors[0] = 0;
    1142  	extraMemoryFromResolver = 0;
    1143          res = tst->func(NULL, NULL, NULL, tst->options);
    1144  	if (res != 0) {
    1145  	    nb_errors++;
    1146  	    err++;
    1147  	}
    1148      }
    1149      return(err);
    1150  }
    1151  
    1152  static int verbose = 0;
    1153  static int tests_quiet = 0;
    1154  
    1155  static int
    1156  runtest(int i) {
    1157      int ret = 0, res;
    1158      int old_errors, old_tests, old_leaks;
    1159  
    1160      old_errors = nb_errors;
    1161      old_tests = nb_tests;
    1162      old_leaks = nb_leaks;
    1163      if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL))
    1164  	printf("## %s\n", testDescriptions[i].desc);
    1165      res = launchTests(&testDescriptions[i]);
    1166      if (res != 0)
    1167  	ret++;
    1168      if (verbose) {
    1169  	if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
    1170  	    printf("Ran %d tests, no errors\n", nb_tests - old_tests);
    1171  	else
    1172  	    printf("Ran %d tests, %d errors, %d leaks\n",
    1173  		   nb_tests - old_tests,
    1174  		   nb_errors - old_errors,
    1175  		   nb_leaks - old_leaks);
    1176      }
    1177      return(ret);
    1178  }
    1179  
    1180  int
    1181  main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
    1182      int i, a, ret = 0;
    1183      int subset = 0;
    1184  
    1185      initializeLibxml2();
    1186  
    1187      for (a = 1; a < argc;a++) {
    1188          if (!strcmp(argv[a], "-v"))
    1189  	    verbose = 1;
    1190          else if (!strcmp(argv[a], "-quiet"))
    1191  	    tests_quiet = 1;
    1192  	else {
    1193  	    for (i = 0; testDescriptions[i].func != NULL; i++) {
    1194  	        if (strstr(testDescriptions[i].desc, argv[a])) {
    1195  		    ret += runtest(i);
    1196  		    subset++;
    1197  		}
    1198  	    }
    1199  	}
    1200      }
    1201      if (subset == 0) {
    1202  	for (i = 0; testDescriptions[i].func != NULL; i++) {
    1203  	    ret += runtest(i);
    1204  	}
    1205      }
    1206      if ((nb_errors == 0) && (nb_leaks == 0)) {
    1207          ret = 0;
    1208  	printf("Total %d tests, no errors\n",
    1209  	       nb_tests);
    1210      } else {
    1211          ret = 1;
    1212  	printf("Total %d tests, %d errors, %d leaks\n",
    1213  	       nb_tests, nb_errors, nb_leaks);
    1214      }
    1215      xmlCleanupParser();
    1216  
    1217      return(ret);
    1218  }