/ libxml2 / testrecurse.c
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 "libxml.h"
 14  #include <stdio.h>
 15  
 16  #if !defined(_WIN32) || defined(__CYGWIN__)
 17  #include <unistd.h>
 18  #endif
 19  #include <string.h>
 20  #include <sys/types.h>
 21  #include <sys/stat.h>
 22  #include <fcntl.h>
 23  
 24  #include <libxml/parser.h>
 25  #include <libxml/tree.h>
 26  #include <libxml/uri.h>
 27  #ifdef LIBXML_READER_ENABLED
 28  #include <libxml/xmlreader.h>
 29  #endif
 30  
 31  /*
 32   * O_BINARY is just for Windows compatibility - if it isn't defined
 33   * on this system, avoid any compilation error
 34   */
 35  #ifdef	O_BINARY
 36  #define RD_FLAGS	O_RDONLY | O_BINARY
 37  #else
 38  #define	RD_FLAGS	O_RDONLY
 39  #endif
 40  
 41  typedef int (*functest) (const char *filename, const char *result,
 42                           const char *error, int options);
 43  
 44  typedef struct testDesc testDesc;
 45  typedef testDesc *testDescPtr;
 46  struct testDesc {
 47      const char *desc; /* descripton of the test */
 48      functest    func; /* function implementing the test */
 49      const char *in;   /* glob to path for input files */
 50      const char *out;  /* output directory */
 51      const char *suffix;/* suffix for output files */
 52      const char *err;  /* suffix for error output files */
 53      int     options;  /* parser options for the test */
 54  };
 55  
 56  static int checkTestFile(const char *filename);
 57  
 58  
 59  #if defined(_WIN32) && !defined(__CYGWIN__)
 60  
 61  #include <windows.h>
 62  #include <io.h>
 63  
 64  typedef struct
 65  {
 66        size_t gl_pathc;    /* Count of paths matched so far  */
 67        char **gl_pathv;    /* List of matched pathnames.  */
 68        size_t gl_offs;     /* Slots to reserve in 'gl_pathv'.  */
 69  } glob_t;
 70  
 71  #define GLOB_DOOFFS 0
 72  static int glob(const char *pattern, int flags,
 73                  int errfunc(const char *epath, int eerrno),
 74                  glob_t *pglob) {
 75      glob_t *ret;
 76      WIN32_FIND_DATA FindFileData;
 77      HANDLE hFind;
 78      unsigned int nb_paths = 0;
 79      char directory[500];
 80      int len;
 81  
 82      if ((pattern == NULL) || (pglob == NULL)) return(-1);
 83  
 84      strncpy(directory, pattern, 499);
 85      for (len = strlen(directory);len >= 0;len--) {
 86          if (directory[len] == '/') {
 87  	    len++;
 88  	    directory[len] = 0;
 89  	    break;
 90  	}
 91      }
 92      if (len <= 0)
 93          len = 0;
 94  
 95  
 96      ret = pglob;
 97      memset(ret, 0, sizeof(glob_t));
 98  
 99      hFind = FindFirstFileA(pattern, &FindFileData);
100      if (hFind == INVALID_HANDLE_VALUE)
101          return(0);
102      nb_paths = 20;
103      ret->gl_pathv = (char **) malloc(nb_paths * sizeof(char *));
104      if (ret->gl_pathv == NULL) {
105  	FindClose(hFind);
106          return(-1);
107      }
108      strncpy(directory + len, FindFileData.cFileName, 499 - len);
109      ret->gl_pathv[ret->gl_pathc] = strdup(directory);
110      if (ret->gl_pathv[ret->gl_pathc] == NULL)
111          goto done;
112      ret->gl_pathc++;
113      while(FindNextFileA(hFind, &FindFileData)) {
114          if (FindFileData.cFileName[0] == '.')
115  	    continue;
116          if (ret->gl_pathc + 2 > nb_paths) {
117              char **tmp = realloc(ret->gl_pathv, nb_paths * 2 * sizeof(char *));
118              if (tmp == NULL)
119                  break;
120              ret->gl_pathv = tmp;
121              nb_paths *= 2;
122  	}
123  	strncpy(directory + len, FindFileData.cFileName, 499 - len);
124  	ret->gl_pathv[ret->gl_pathc] = strdup(directory);
125          if (ret->gl_pathv[ret->gl_pathc] == NULL)
126              break;
127          ret->gl_pathc++;
128      }
129      ret->gl_pathv[ret->gl_pathc] = NULL;
130  
131  done:
132      FindClose(hFind);
133      return(0);
134  }
135  
136  
137  
138  static void globfree(glob_t *pglob) {
139      unsigned int i;
140      if (pglob == NULL)
141          return;
142  
143      for (i = 0;i < pglob->gl_pathc;i++) {
144           if (pglob->gl_pathv[i] != NULL)
145               free(pglob->gl_pathv[i]);
146      }
147  }
148  
149  #else
150  #include <glob.h>
151  #endif
152  
153  /************************************************************************
154   *									*
155   *		Huge document generator					*
156   *									*
157   ************************************************************************/
158  
159  #include <libxml/xmlIO.h>
160  
161  
162  static const char *start = "<!DOCTYPE foo [\
163  <!ENTITY f 'some internal data'> \
164  <!ENTITY e '&f;&f;'> \
165  <!ENTITY d '&e;&e;'> \
166  ]> \
167  <foo>";
168  
169  static const char *segment = "  <bar>&e; &f; &d;</bar>\n";
170  static const char *finish = "</foo>";
171  
172  static int curseg = 0;
173  static const char *current;
174  static int rlen;
175  
176  /**
177   * hugeMatch:
178   * @URI: an URI to test
179   *
180   * Check for an huge: query
181   *
182   * Returns 1 if yes and 0 if another Input module should be used
183   */
184  static int
185  hugeMatch(const char * URI) {
186      if ((URI != NULL) && (!strncmp(URI, "huge:", 4)))
187          return(1);
188      return(0);
189  }
190  
191  /**
192   * hugeOpen:
193   * @URI: an URI to test
194   *
195   * Return a pointer to the huge: query handler, in this example simply
196   * the current pointer...
197   *
198   * Returns an Input context or NULL in case or error
199   */
200  static void *
201  hugeOpen(const char * URI) {
202      if ((URI == NULL) || (strncmp(URI, "huge:", 4)))
203          return(NULL);
204      rlen = strlen(start);
205      current = start;
206      return((void *) current);
207  }
208  
209  /**
210   * hugeClose:
211   * @context: the read context
212   *
213   * Close the huge: query handler
214   *
215   * Returns 0 or -1 in case of error
216   */
217  static int
218  hugeClose(void * context) {
219      if (context == NULL) return(-1);
220      return(0);
221  }
222  
223  #define MAX_NODES 1000000
224  
225  /**
226   * hugeRead:
227   * @context: the read context
228   * @buffer: where to store data
229   * @len: number of bytes to read
230   *
231   * Implement an huge: query read.
232   *
233   * Returns the number of bytes read or -1 in case of error
234   */
235  static int
236  hugeRead(void *context, char *buffer, int len)
237  {
238      if ((context == NULL) || (buffer == NULL) || (len < 0))
239          return (-1);
240  
241      if (len >= rlen) {
242          if (curseg >= MAX_NODES + 1) {
243              rlen = 0;
244              return(0);
245          }
246          len = rlen;
247          rlen = 0;
248  	memcpy(buffer, current, len);
249          curseg ++;
250          if (curseg == MAX_NODES) {
251  	    fprintf(stderr, "\n");
252              rlen = strlen(finish);
253              current = finish;
254  	} else {
255  	    if (curseg % (MAX_NODES / 10) == 0)
256  	        fprintf(stderr, ".");
257              rlen = strlen(segment);
258              current = segment;
259  	}
260      } else {
261  	memcpy(buffer, current, len);
262  	rlen -= len;
263          current += len;
264      }
265      return (len);
266  }
267  
268  /************************************************************************
269   *									*
270   *		Libxml2 specific routines				*
271   *									*
272   ************************************************************************/
273  
274  static int nb_tests = 0;
275  static int nb_errors = 0;
276  static int nb_leaks = 0;
277  static int extraMemoryFromResolver = 0;
278  
279  static int
280  fatalError(void) {
281      fprintf(stderr, "Exitting tests on fatal error\n");
282      exit(1);
283  }
284  
285  /*
286   * We need to trap calls to the resolver to not account memory for the catalog
287   * which is shared to the current running test. We also don't want to have
288   * network downloads modifying tests.
289   */
290  static xmlParserInputPtr
291  testExternalEntityLoader(const char *URL, const char *ID,
292  			 xmlParserCtxtPtr ctxt) {
293      xmlParserInputPtr ret;
294  
295      if (checkTestFile(URL)) {
296  	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
297      } else {
298  	int memused = xmlMemUsed();
299  	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
300  	extraMemoryFromResolver += xmlMemUsed() - memused;
301      }
302  
303      return(ret);
304  }
305  
306  /*
307   * Trapping the error messages at the generic level to grab the equivalent of
308   * stderr messages on CLI tools.
309   */
310  static char testErrors[32769];
311  static int testErrorsSize = 0;
312  
313  static void XMLCDECL LIBXML_ATTR_FORMAT(2,3)
314  channel(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
315      va_list args;
316      int res;
317  
318      if (testErrorsSize >= 32768)
319          return;
320      va_start(args, msg);
321      res = vsnprintf(&testErrors[testErrorsSize],
322                      32768 - testErrorsSize,
323  		    msg, args);
324      va_end(args);
325      if (testErrorsSize + res >= 32768) {
326          /* buffer is full */
327  	testErrorsSize = 32768;
328  	testErrors[testErrorsSize] = 0;
329      } else {
330          testErrorsSize += res;
331      }
332      testErrors[testErrorsSize] = 0;
333  }
334  
335  /**
336   * xmlParserPrintFileContext:
337   * @input:  an xmlParserInputPtr input
338   *
339   * Displays current context within the input content for error tracking
340   */
341  
342  static void
343  xmlParserPrintFileContextInternal(xmlParserInputPtr input ,
344  		xmlGenericErrorFunc chanl, void *data ) {
345      const xmlChar *cur, *base;
346      unsigned int n, col;	/* GCC warns if signed, because compared with sizeof() */
347      xmlChar  content[81]; /* space for 80 chars + line terminator */
348      xmlChar *ctnt;
349  
350      if (input == NULL) return;
351      cur = input->cur;
352      base = input->base;
353      /* skip backwards over any end-of-lines */
354      while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) {
355  	cur--;
356      }
357      n = 0;
358      /* search backwards for beginning-of-line (to max buff size) */
359      while ((n++ < (sizeof(content)-1)) && (cur > base) &&
360     (*(cur) != '\n') && (*(cur) != '\r'))
361          cur--;
362      if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;
363      /* calculate the error position in terms of the current position */
364      col = input->cur - cur;
365      /* search forward for end-of-line (to max buff size) */
366      n = 0;
367      ctnt = content;
368      /* copy selected text to our buffer */
369      while ((*cur != 0) && (*(cur) != '\n') &&
370     (*(cur) != '\r') && (n < sizeof(content)-1)) {
371  		*ctnt++ = *cur++;
372  	n++;
373      }
374      *ctnt = 0;
375      /* print out the selected text */
376      chanl(data ,"%s\n", content);
377      /* create blank line with problem pointer */
378      n = 0;
379      ctnt = content;
380      /* (leave buffer space for pointer + line terminator) */
381      while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) {
382  	if (*(ctnt) != '\t')
383  	    *(ctnt) = ' ';
384  	ctnt++;
385      }
386      *ctnt++ = '^';
387      *ctnt = 0;
388      chanl(data ,"%s\n", content);
389  }
390  
391  static void
392  testStructuredErrorHandler(void *ctx  ATTRIBUTE_UNUSED, xmlErrorPtr err) {
393      char *file = NULL;
394      int line = 0;
395      int code = -1;
396      int domain;
397      void *data = NULL;
398      const char *str;
399      const xmlChar *name = NULL;
400      xmlNodePtr node;
401      xmlErrorLevel level;
402      xmlParserInputPtr input = NULL;
403      xmlParserInputPtr cur = NULL;
404      xmlParserCtxtPtr ctxt = NULL;
405  
406      if (err == NULL)
407          return;
408  
409      file = err->file;
410      line = err->line;
411      code = err->code;
412      domain = err->domain;
413      level = err->level;
414      node = err->node;
415      if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) ||
416          (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) ||
417  	(domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) {
418  	ctxt = err->ctxt;
419      }
420      str = err->message;
421  
422      if (code == XML_ERR_OK)
423          return;
424  
425      if ((node != NULL) && (node->type == XML_ELEMENT_NODE))
426          name = node->name;
427  
428      /*
429       * Maintain the compatibility with the legacy error handling
430       */
431      if (ctxt != NULL) {
432          input = ctxt->input;
433          if ((input != NULL) && (input->filename == NULL) &&
434              (ctxt->inputNr > 1)) {
435              cur = input;
436              input = ctxt->inputTab[ctxt->inputNr - 2];
437          }
438          if (input != NULL) {
439              if (input->filename)
440                  channel(data, "%s:%d: ", input->filename, input->line);
441              else if ((line != 0) && (domain == XML_FROM_PARSER))
442                  channel(data, "Entity: line %d: ", input->line);
443          }
444      } else {
445          if (file != NULL)
446              channel(data, "%s:%d: ", file, line);
447          else if ((line != 0) && (domain == XML_FROM_PARSER))
448              channel(data, "Entity: line %d: ", line);
449      }
450      if (name != NULL) {
451          channel(data, "element %s: ", name);
452      }
453      if (code == XML_ERR_OK)
454          return;
455      switch (domain) {
456          case XML_FROM_PARSER:
457              channel(data, "parser ");
458              break;
459          case XML_FROM_NAMESPACE:
460              channel(data, "namespace ");
461              break;
462          case XML_FROM_DTD:
463          case XML_FROM_VALID:
464              channel(data, "validity ");
465              break;
466          case XML_FROM_HTML:
467              channel(data, "HTML parser ");
468              break;
469          case XML_FROM_MEMORY:
470              channel(data, "memory ");
471              break;
472          case XML_FROM_OUTPUT:
473              channel(data, "output ");
474              break;
475          case XML_FROM_IO:
476              channel(data, "I/O ");
477              break;
478          case XML_FROM_XINCLUDE:
479              channel(data, "XInclude ");
480              break;
481          case XML_FROM_XPATH:
482              channel(data, "XPath ");
483              break;
484          case XML_FROM_XPOINTER:
485              channel(data, "parser ");
486              break;
487          case XML_FROM_REGEXP:
488              channel(data, "regexp ");
489              break;
490          case XML_FROM_MODULE:
491              channel(data, "module ");
492              break;
493          case XML_FROM_SCHEMASV:
494              channel(data, "Schemas validity ");
495              break;
496          case XML_FROM_SCHEMASP:
497              channel(data, "Schemas parser ");
498              break;
499          case XML_FROM_RELAXNGP:
500              channel(data, "Relax-NG parser ");
501              break;
502          case XML_FROM_RELAXNGV:
503              channel(data, "Relax-NG validity ");
504              break;
505          case XML_FROM_CATALOG:
506              channel(data, "Catalog ");
507              break;
508          case XML_FROM_C14N:
509              channel(data, "C14N ");
510              break;
511          case XML_FROM_XSLT:
512              channel(data, "XSLT ");
513              break;
514          default:
515              break;
516      }
517      if (code == XML_ERR_OK)
518          return;
519      switch (level) {
520          case XML_ERR_NONE:
521              channel(data, ": ");
522              break;
523          case XML_ERR_WARNING:
524              channel(data, "warning : ");
525              break;
526          case XML_ERR_ERROR:
527              channel(data, "error : ");
528              break;
529          case XML_ERR_FATAL:
530              channel(data, "error : ");
531              break;
532      }
533      if (code == XML_ERR_OK)
534          return;
535      if (str != NULL) {
536          int len;
537  	len = xmlStrlen((const xmlChar *)str);
538  	if ((len > 0) && (str[len - 1] != '\n'))
539  	    channel(data, "%s\n", str);
540  	else
541  	    channel(data, "%s", str);
542      } else {
543          channel(data, "%s\n", "out of memory error");
544      }
545      if (code == XML_ERR_OK)
546          return;
547  
548      if (ctxt != NULL) {
549          xmlParserPrintFileContextInternal(input, channel, data);
550          if (cur != NULL) {
551              if (cur->filename)
552                  channel(data, "%s:%d: \n", cur->filename, cur->line);
553              else if ((line != 0) && (domain == XML_FROM_PARSER))
554                  channel(data, "Entity: line %d: \n", cur->line);
555              xmlParserPrintFileContextInternal(cur, channel, data);
556          }
557      }
558      if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) &&
559          (err->int1 < 100) &&
560  	(err->int1 < xmlStrlen((const xmlChar *)err->str1))) {
561  	xmlChar buf[150];
562  	int i;
563  
564  	channel(data, "%s\n", err->str1);
565  	for (i=0;i < err->int1;i++)
566  	     buf[i] = ' ';
567  	buf[i++] = '^';
568  	buf[i] = 0;
569  	channel(data, "%s\n", buf);
570      }
571  }
572  
573  static void
574  initializeLibxml2(void) {
575      xmlGetWarningsDefaultValue = 0;
576      xmlPedanticParserDefault(0);
577  
578      xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
579      xmlInitParser();
580      xmlSetExternalEntityLoader(testExternalEntityLoader);
581      xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
582      /*
583       * register the new I/O handlers
584       */
585      if (xmlRegisterInputCallbacks(hugeMatch, hugeOpen,
586                                    hugeRead, hugeClose) < 0) {
587          fprintf(stderr, "failed to register Huge handler\n");
588  	exit(1);
589      }
590  }
591  
592  /************************************************************************
593   *									*
594   *		File name and path utilities				*
595   *									*
596   ************************************************************************/
597  
598  static const char *baseFilename(const char *filename) {
599      const char *cur;
600      if (filename == NULL)
601          return(NULL);
602      cur = &filename[strlen(filename)];
603      while ((cur > filename) && (*cur != '/'))
604          cur--;
605      if (*cur == '/')
606          return(cur + 1);
607      return(cur);
608  }
609  
610  static char *resultFilename(const char *filename, const char *out,
611                              const char *suffix) {
612      const char *base;
613      char res[500];
614      char suffixbuff[500];
615  
616  /*************
617      if ((filename[0] == 't') && (filename[1] == 'e') &&
618          (filename[2] == 's') && (filename[3] == 't') &&
619  	(filename[4] == '/'))
620  	filename = &filename[5];
621   *************/
622  
623      base = baseFilename(filename);
624      if (suffix == NULL)
625          suffix = ".tmp";
626      if (out == NULL)
627          out = "";
628  
629      strncpy(suffixbuff,suffix,499);
630  #ifdef VMS
631      if(strstr(base,".") && suffixbuff[0]=='.')
632        suffixbuff[0]='_';
633  #endif
634  
635      snprintf(res, 499, "%s%s%s", out, base, suffixbuff);
636      res[499] = 0;
637      return(strdup(res));
638  }
639  
640  static int checkTestFile(const char *filename) {
641      struct stat buf;
642  
643      if (stat(filename, &buf) == -1)
644          return(0);
645  
646  #if defined(_WIN32) && !defined(__CYGWIN__)
647      if (!(buf.st_mode & _S_IFREG))
648          return(0);
649  #else
650      if (!S_ISREG(buf.st_mode))
651          return(0);
652  #endif
653  
654      return(1);
655  }
656  
657  
658  
659  /************************************************************************
660   *									*
661   *		Test to detect or not recursive entities		*
662   *									*
663   ************************************************************************/
664  /**
665   * recursiveDetectTest:
666   * @filename: the file to parse
667   * @result: the file with expected result
668   * @err: the file with error messages: unused
669   *
670   * Parse a file loading DTD and replacing entities check it fails for
671   * lol cases
672   *
673   * Returns 0 in case of success, an error code otherwise
674   */
675  static int
676  recursiveDetectTest(const char *filename,
677               const char *result ATTRIBUTE_UNUSED,
678               const char *err ATTRIBUTE_UNUSED,
679  	     int options ATTRIBUTE_UNUSED) {
680      xmlDocPtr doc;
681      xmlParserCtxtPtr ctxt;
682      int res = 0;
683  
684      nb_tests++;
685  
686      ctxt = xmlNewParserCtxt();
687      /*
688       * base of the test, parse with the old API
689       */
690      doc = xmlCtxtReadFile(ctxt, filename, NULL,
691                            XML_PARSE_NOENT | XML_PARSE_DTDLOAD);
692      if ((doc != NULL) || (ctxt->lastError.code != XML_ERR_ENTITY_LOOP)) {
693          fprintf(stderr, "Failed to detect recursion in %s\n", filename);
694  	xmlFreeParserCtxt(ctxt);
695  	xmlFreeDoc(doc);
696          return(1);
697      }
698      xmlFreeParserCtxt(ctxt);
699  
700      return(res);
701  }
702  
703  /**
704   * notRecursiveDetectTest:
705   * @filename: the file to parse
706   * @result: the file with expected result
707   * @err: the file with error messages: unused
708   *
709   * Parse a file loading DTD and replacing entities check it works for
710   * good cases
711   *
712   * Returns 0 in case of success, an error code otherwise
713   */
714  static int
715  notRecursiveDetectTest(const char *filename,
716               const char *result ATTRIBUTE_UNUSED,
717               const char *err ATTRIBUTE_UNUSED,
718  	     int options ATTRIBUTE_UNUSED) {
719      xmlDocPtr doc;
720      xmlParserCtxtPtr ctxt;
721      int res = 0;
722  
723      nb_tests++;
724  
725      ctxt = xmlNewParserCtxt();
726      /*
727       * base of the test, parse with the old API
728       */
729      doc = xmlCtxtReadFile(ctxt, filename, NULL,
730                            XML_PARSE_NOENT | XML_PARSE_DTDLOAD);
731      if (doc == NULL) {
732          fprintf(stderr, "Failed to parse correct file %s\n", filename);
733  	xmlFreeParserCtxt(ctxt);
734          return(1);
735      }
736      xmlFreeDoc(doc);
737      xmlFreeParserCtxt(ctxt);
738  
739      return(res);
740  }
741  
742  #ifdef LIBXML_READER_ENABLED
743  /**
744   * notRecursiveHugeTest:
745   * @filename: the file to parse
746   * @result: the file with expected result
747   * @err: the file with error messages: unused
748   *
749   * Parse a memory generated file
750   * good cases
751   *
752   * Returns 0 in case of success, an error code otherwise
753   */
754  static int
755  notRecursiveHugeTest(const char *filename ATTRIBUTE_UNUSED,
756               const char *result ATTRIBUTE_UNUSED,
757               const char *err ATTRIBUTE_UNUSED,
758  	     int options ATTRIBUTE_UNUSED) {
759      xmlTextReaderPtr reader;
760      int res = 0;
761      int ret;
762  
763      nb_tests++;
764  
765      reader = xmlReaderForFile("huge:test" , NULL,
766                                XML_PARSE_NOENT | XML_PARSE_DTDLOAD);
767      if (reader == NULL) {
768          fprintf(stderr, "Failed to open huge:test\n");
769  	return(1);
770      }
771      ret = xmlTextReaderRead(reader);
772      while (ret == 1) {
773          ret = xmlTextReaderRead(reader);
774      }
775      if (ret != 0) {
776          fprintf(stderr, "Failed to parser huge:test with entities\n");
777  	res = 1;
778      }
779      xmlFreeTextReader(reader);
780  
781      return(res);
782  }
783  #endif
784  
785  /************************************************************************
786   *									*
787   *			Tests Descriptions				*
788   *									*
789   ************************************************************************/
790  
791  static
792  testDesc testDescriptions[] = {
793      { "Parsing recursive test cases" ,
794        recursiveDetectTest, "./test/recurse/lol*.xml", NULL, NULL, NULL,
795        0 },
796      { "Parsing non-recursive test cases" ,
797        notRecursiveDetectTest, "./test/recurse/good*.xml", NULL, NULL, NULL,
798        0 },
799  #ifdef LIBXML_READER_ENABLED
800      { "Parsing non-recursive huge case" ,
801        notRecursiveHugeTest, NULL, NULL, NULL, NULL,
802        0 },
803  #endif
804      {NULL, NULL, NULL, NULL, NULL, NULL, 0}
805  };
806  
807  /************************************************************************
808   *									*
809   *		The main code driving the tests				*
810   *									*
811   ************************************************************************/
812  
813  static int
814  launchTests(testDescPtr tst) {
815      int res = 0, err = 0;
816      size_t i;
817      char *result;
818      char *error;
819      int mem;
820  
821      if (tst == NULL) return(-1);
822      if (tst->in != NULL) {
823  	glob_t globbuf;
824  
825  	globbuf.gl_offs = 0;
826  	glob(tst->in, GLOB_DOOFFS, NULL, &globbuf);
827  	for (i = 0;i < globbuf.gl_pathc;i++) {
828  	    if (!checkTestFile(globbuf.gl_pathv[i]))
829  	        continue;
830  	    if (tst->suffix != NULL) {
831  		result = resultFilename(globbuf.gl_pathv[i], tst->out,
832  					tst->suffix);
833  		if (result == NULL) {
834  		    fprintf(stderr, "Out of memory !\n");
835  		    fatalError();
836  		}
837  	    } else {
838  	        result = NULL;
839  	    }
840  	    if (tst->err != NULL) {
841  		error = resultFilename(globbuf.gl_pathv[i], tst->out,
842  		                        tst->err);
843  		if (error == NULL) {
844  		    fprintf(stderr, "Out of memory !\n");
845  		    fatalError();
846  		}
847  	    } else {
848  	        error = NULL;
849  	    }
850  	    if ((result) &&(!checkTestFile(result))) {
851  	        fprintf(stderr, "Missing result file %s\n", result);
852  	    } else if ((error) &&(!checkTestFile(error))) {
853  	        fprintf(stderr, "Missing error file %s\n", error);
854  	    } else {
855  		mem = xmlMemUsed();
856  		extraMemoryFromResolver = 0;
857  		testErrorsSize = 0;
858  		testErrors[0] = 0;
859  		res = tst->func(globbuf.gl_pathv[i], result, error,
860  		                tst->options | XML_PARSE_COMPACT);
861  		xmlResetLastError();
862  		if (res != 0) {
863  		    fprintf(stderr, "File %s generated an error\n",
864  		            globbuf.gl_pathv[i]);
865  		    nb_errors++;
866  		    err++;
867  		}
868  		else if (xmlMemUsed() != mem) {
869  		    if ((xmlMemUsed() != mem) &&
870  		        (extraMemoryFromResolver == 0)) {
871  			fprintf(stderr, "File %s leaked %d bytes\n",
872  				globbuf.gl_pathv[i], xmlMemUsed() - mem);
873  			nb_leaks++;
874  			err++;
875  		    }
876  		}
877  		testErrorsSize = 0;
878  	    }
879  	    if (result)
880  		free(result);
881  	    if (error)
882  		free(error);
883  	}
884  	globfree(&globbuf);
885      } else {
886          testErrorsSize = 0;
887  	testErrors[0] = 0;
888  	extraMemoryFromResolver = 0;
889          res = tst->func(NULL, NULL, NULL, tst->options);
890  	if (res != 0) {
891  	    nb_errors++;
892  	    err++;
893  	}
894      }
895      return(err);
896  }
897  
898  static int verbose = 0;
899  static int tests_quiet = 0;
900  
901  static int
902  runtest(int i) {
903      int ret = 0, res;
904      int old_errors, old_tests, old_leaks;
905  
906      old_errors = nb_errors;
907      old_tests = nb_tests;
908      old_leaks = nb_leaks;
909      if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL))
910  	printf("## %s\n", testDescriptions[i].desc);
911      res = launchTests(&testDescriptions[i]);
912      if (res != 0)
913  	ret++;
914      if (verbose) {
915  	if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
916  	    printf("Ran %d tests, no errors\n", nb_tests - old_tests);
917  	else
918  	    printf("Ran %d tests, %d errors, %d leaks\n",
919  		   nb_tests - old_tests,
920  		   nb_errors - old_errors,
921  		   nb_leaks - old_leaks);
922      }
923      return(ret);
924  }
925  
926  int
927  main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
928      int i, a, ret = 0;
929      int subset = 0;
930  
931      initializeLibxml2();
932  
933      for (a = 1; a < argc;a++) {
934          if (!strcmp(argv[a], "-v"))
935  	    verbose = 1;
936          else if (!strcmp(argv[a], "-quiet"))
937  	    tests_quiet = 1;
938  	else {
939  	    for (i = 0; testDescriptions[i].func != NULL; i++) {
940  	        if (strstr(testDescriptions[i].desc, argv[a])) {
941  		    ret += runtest(i);
942  		    subset++;
943  		}
944  	    }
945  	}
946      }
947      if (subset == 0) {
948  	for (i = 0; testDescriptions[i].func != NULL; i++) {
949  	    ret += runtest(i);
950  	}
951      }
952      if ((nb_errors == 0) && (nb_leaks == 0)) {
953          ret = 0;
954  	printf("Total %d tests, no errors\n",
955  	       nb_tests);
956      } else {
957          ret = 1;
958  	printf("Total %d tests, %d errors, %d leaks\n",
959  	       nb_tests, nb_errors, nb_leaks);
960      }
961      xmlCleanupParser();
962      xmlMemoryDump();
963  
964      return(ret);
965  }