text: /*
   * runtest.c: C program to run libxml2 regression tests without
   *            requiring make or Python, and reducing platform dependencies
   *            to a strict minimum.
   *
   * To compile on Unixes:
   * cc -o runtest `xml2-config --cflags` runtest.c `xml2-config --libs` -lpthread
   *
   * See Copyright for the status of this software.
   *
   * daniel@veillard.com
   */
  
  #include "libxml.h"
  #include <stdio.h>
  
  #if !defined(_WIN32) || defined(__CYGWIN__)
  #include <unistd.h>
  #endif
  #include <string.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <fcntl.h>
  
  #include <libxml/parser.h>
  #include <libxml/parserInternals.h>
  #include <libxml/tree.h>
  #include <libxml/uri.h>
  #include <libxml/encoding.h>
  
  #ifdef LIBXML_OUTPUT_ENABLED
  #ifdef LIBXML_READER_ENABLED
  #include <libxml/xmlreader.h>
  #endif
  
  #ifdef LIBXML_XINCLUDE_ENABLED
  #include <libxml/xinclude.h>
  #endif
  
  #ifdef LIBXML_XPATH_ENABLED
  #include <libxml/xpath.h>
  #include <libxml/xpathInternals.h>
  #ifdef LIBXML_XPTR_ENABLED
  #include <libxml/xpointer.h>
  #endif
  #endif
  
  #ifdef LIBXML_SCHEMAS_ENABLED
  #include <libxml/relaxng.h>
  #include <libxml/xmlschemas.h>
  #include <libxml/xmlschemastypes.h>
  #endif
  
  #ifdef LIBXML_PATTERN_ENABLED
  #include <libxml/pattern.h>
  #endif
  
  #ifdef LIBXML_C14N_ENABLED
  #include <libxml/c14n.h>
  #endif
  
  #ifdef LIBXML_HTML_ENABLED
  #include <libxml/HTMLparser.h>
  #include <libxml/HTMLtree.h>
  
  /*
   * pseudo flag for the unification of HTML and XML tests
   */
  #define XML_PARSE_HTML 1 << 24
  #endif
  
  #if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED)
  #include <libxml/globals.h>
  #include <libxml/threads.h>
  #include <libxml/parser.h>
  #include <libxml/catalog.h>
  #include <string.h>
  #endif
  
  /*
   * O_BINARY is just for Windows compatibility - if it isn't defined
   * on this system, avoid any compilation error
   */
  #ifdef	O_BINARY
  #define RD_FLAGS	O_RDONLY | O_BINARY
  #define WR_FLAGS	O_WRONLY | O_CREAT | O_TRUNC | O_BINARY
  #else
  #define RD_FLAGS	O_RDONLY
  #define WR_FLAGS	O_WRONLY | O_CREAT | O_TRUNC
  #endif
  
  typedef int (*functest) (const char *filename, const char *result,
                           const char *error, int options);
  
  typedef struct testDesc testDesc;
  typedef testDesc *testDescPtr;
  struct testDesc {
      const char *desc; /* description of the test */
      functest    func; /* function implementing the test */
      const char *in;   /* glob to path for input files */
      const char *out;  /* output directory */
      const char *suffix;/* suffix for output files */
      const char *err;  /* suffix for error output files */
      int     options;  /* parser options for the test */
  };
  
  static int update_results = 0;
  static char* temp_directory = NULL;
  static int checkTestFile(const char *filename);
  
  #if defined(_WIN32) && !defined(__CYGWIN__)
  
  #include <windows.h>
  #include <io.h>
  
  typedef struct
  {
        size_t gl_pathc;    /* Count of paths matched so far  */
        char **gl_pathv;    /* List of matched pathnames.  */
        size_t gl_offs;     /* Slots to reserve in 'gl_pathv'.  */
  } glob_t;
  
  #define GLOB_DOOFFS 0
  static int glob(const char *pattern, ATTRIBUTE_UNUSED int flags,
                  ATTRIBUTE_UNUSED int errfunc(const char *epath, int eerrno),
                  glob_t *pglob) {
      glob_t *ret;
      WIN32_FIND_DATA FindFileData;
      HANDLE hFind;
      unsigned int nb_paths = 0;
      char directory[500];
      int len;
  
      if ((pattern == NULL) || (pglob == NULL)) return(-1);
  
      strncpy(directory, pattern, 499);
      for (len = strlen(directory);len >= 0;len--) {
          if (directory[len] == '/') {
  	    len++;
  	    directory[len] = 0;
  	    break;
  	}
      }
      if (len <= 0)
          len = 0;
  
  
      ret = pglob;
      memset(ret, 0, sizeof(glob_t));
  
      hFind = FindFirstFileA(pattern, &FindFileData);
      if (hFind == INVALID_HANDLE_VALUE)
          return(0);
      nb_paths = 20;
      ret->gl_pathv = (char **) malloc(nb_paths * sizeof(char *));
      if (ret->gl_pathv == NULL) {
  	FindClose(hFind);
          return(-1);
      }
      strncpy(directory + len, FindFileData.cFileName, 499 - len);
      ret->gl_pathv[ret->gl_pathc] = strdup(directory);
      if (ret->gl_pathv[ret->gl_pathc] == NULL)
          goto done;
      ret->gl_pathc++;
      while(FindNextFileA(hFind, &FindFileData)) {
          if (FindFileData.cFileName[0] == '.')
  	    continue;
          if (ret->gl_pathc + 2 > nb_paths) {
              char **tmp = realloc(ret->gl_pathv, nb_paths * 2 * sizeof(char *));
              if (tmp == NULL)
                  break;
              ret->gl_pathv = tmp;
              nb_paths *= 2;
  	}
  	strncpy(directory + len, FindFileData.cFileName, 499 - len);
  	ret->gl_pathv[ret->gl_pathc] = strdup(directory);
          if (ret->gl_pathv[ret->gl_pathc] == NULL)
              break;
          ret->gl_pathc++;
      }
      ret->gl_pathv[ret->gl_pathc] = NULL;
  
  done:
      FindClose(hFind);
      return(0);
  }
  
  
  
  static void globfree(glob_t *pglob) {
      unsigned int i;
      if (pglob == NULL)
          return;
  
      for (i = 0;i < pglob->gl_pathc;i++) {
           if (pglob->gl_pathv[i] != NULL)
               free(pglob->gl_pathv[i]);
      }
  }
  
  #else
  #include <glob.h>
  #endif
  
  /************************************************************************
   *									*
   *		Libxml2 specific routines				*
   *									*
   ************************************************************************/
  
  static int nb_tests = 0;
  static int nb_errors = 0;
  static int nb_leaks = 0;
  static int extraMemoryFromResolver = 0;
  
  static int
  fatalError(void) {
      fprintf(stderr, "Exitting tests on fatal error\n");
      exit(1);
  }
  
  /*
   * We need to trap calls to the resolver to not account memory for the catalog
   * which is shared to the current running test. We also don't want to have
   * network downloads modifying tests.
   */
  static xmlParserInputPtr
  testExternalEntityLoader(const char *URL, const char *ID,
  			 xmlParserCtxtPtr ctxt) {
      xmlParserInputPtr ret;
  
      if (checkTestFile(URL)) {
  	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
      } else {
  	int memused = xmlMemUsed();
  	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
  	extraMemoryFromResolver += xmlMemUsed() - memused;
      }
  
      return(ret);
  }
  
  /*
   * Trapping the error messages at the generic level to grab the equivalent of
   * stderr messages on CLI tools.
   */
  static char testErrors[32769];
  static int testErrorsSize = 0;
  
  static void XMLCDECL
  testErrorHandler(void *ctx  ATTRIBUTE_UNUSED, const char *msg, ...) {
      va_list args;
      int res;
  
      if (testErrorsSize >= 32768)
          return;
      va_start(args, msg);
      res = vsnprintf(&testErrors[testErrorsSize],
                      32768 - testErrorsSize,
  		    msg, args);
      va_end(args);
      if (testErrorsSize + res >= 32768) {
          /* buffer is full */
  	testErrorsSize = 32768;
  	testErrors[testErrorsSize] = 0;
      } else {
          testErrorsSize += res;
      }
      testErrors[testErrorsSize] = 0;
  }
  
  static void XMLCDECL
  channel(void *ctx  ATTRIBUTE_UNUSED, const char *msg, ...) {
      va_list args;
      int res;
  
      if (testErrorsSize >= 32768)
          return;
      va_start(args, msg);
      res = vsnprintf(&testErrors[testErrorsSize],
                      32768 - testErrorsSize,
  		    msg, args);
      va_end(args);
      if (testErrorsSize + res >= 32768) {
          /* buffer is full */
  	testErrorsSize = 32768;
  	testErrors[testErrorsSize] = 0;
      } else {
          testErrorsSize += res;
      }
      testErrors[testErrorsSize] = 0;
  }
  
  /**
   * xmlParserPrintFileContext:
   * @input:  an xmlParserInputPtr input
   *
   * Displays current context within the input content for error tracking
   */
  
  static void
  xmlParserPrintFileContextInternal(xmlParserInputPtr input ,
  		xmlGenericErrorFunc chanl, void *data ) {
      const xmlChar *cur, *base;
      unsigned int n, col;	/* GCC warns if signed, because compared with sizeof() */
      xmlChar  content[81]; /* space for 80 chars + line terminator */
      xmlChar *ctnt;
  
      if (input == NULL) return;
      cur = input->cur;
      base = input->base;
      /* skip backwards over any end-of-lines */
      while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) {
  	cur--;
      }
      n = 0;
      /* search backwards for beginning-of-line (to max buff size) */
      while ((n++ < (sizeof(content)-1)) && (cur > base) &&
     (*(cur) != '\n') && (*(cur) != '\r'))
          cur--;
      if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;
      /* calculate the error position in terms of the current position */
      col = input->cur - cur;
      /* search forward for end-of-line (to max buff size) */
      n = 0;
      ctnt = content;
      /* copy selected text to our buffer */
      while ((*cur != 0) && (*(cur) != '\n') &&
     (*(cur) != '\r') && (n < sizeof(content)-1)) {
  		*ctnt++ = *cur++;
  	n++;
      }
      *ctnt = 0;
      /* print out the selected text */
      chanl(data ,"%s\n", content);
      /* create blank line with problem pointer */
      n = 0;
      ctnt = content;
      /* (leave buffer space for pointer + line terminator) */
      while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) {
  	if (*(ctnt) != '\t')
  	    *(ctnt) = ' ';
  	ctnt++;
      }
      *ctnt++ = '^';
      *ctnt = 0;
      chanl(data ,"%s\n", content);
  }
  
  static void
  testStructuredErrorHandler(void *ctx  ATTRIBUTE_UNUSED, xmlErrorPtr err) {
      char *file = NULL;
      int line = 0;
      int code = -1;
      int domain;
      void *data = NULL;
      const char *str;
      const xmlChar *name = NULL;
      xmlNodePtr node;
      xmlErrorLevel level;
      xmlParserInputPtr input = NULL;
      xmlParserInputPtr cur = NULL;
      xmlParserCtxtPtr ctxt = NULL;
  
      if (err == NULL)
          return;
  
      file = err->file;
      line = err->line;
      code = err->code;
      domain = err->domain;
      level = err->level;
      node = err->node;
      if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) ||
          (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) ||
  	(domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) {
  	ctxt = err->ctxt;
      }
      str = err->message;
  
      if (code == XML_ERR_OK)
          return;
  
      if ((node != NULL) && (node->type == XML_ELEMENT_NODE))
          name = node->name;
  
      /*
       * Maintain the compatibility with the legacy error handling
       */
      if (ctxt != NULL) {
          input = ctxt->input;
          if ((input != NULL) && (input->filename == NULL) &&
              (ctxt->inputNr > 1)) {
              cur = input;
              input = ctxt->inputTab[ctxt->inputNr - 2];
          }
          if (input != NULL) {
              if (input->filename)
                  channel(data, "%s:%d: ", input->filename, input->line);
              else if ((line != 0) && (domain == XML_FROM_PARSER))
                  channel(data, "Entity: line %d: ", input->line);
          }
      } else {
          if (file != NULL)
              channel(data, "%s:%d: ", file, line);
          else if ((line != 0) && (domain == XML_FROM_PARSER))
              channel(data, "Entity: line %d: ", line);
      }
      if (name != NULL) {
          channel(data, "element %s: ", name);
      }
      if (code == XML_ERR_OK)
          return;
      switch (domain) {
          case XML_FROM_PARSER:
              channel(data, "parser ");
              break;
          case XML_FROM_NAMESPACE:
              channel(data, "namespace ");
              break;
          case XML_FROM_DTD:
          case XML_FROM_VALID:
              channel(data, "validity ");
              break;
          case XML_FROM_HTML:
              channel(data, "HTML parser ");
              break;
          case XML_FROM_MEMORY:
              channel(data, "memory ");
              break;
          case XML_FROM_OUTPUT:
              channel(data, "output ");
              break;
          case XML_FROM_IO:
              channel(data, "I/O ");
              break;
          case XML_FROM_XINCLUDE:
              channel(data, "XInclude ");
              break;
          case XML_FROM_XPATH:
              channel(data, "XPath ");
              break;
          case XML_FROM_XPOINTER:
              channel(data, "parser ");
              break;
          case XML_FROM_REGEXP:
              channel(data, "regexp ");
              break;
          case XML_FROM_MODULE:
              channel(data, "module ");
              break;
          case XML_FROM_SCHEMASV:
              channel(data, "Schemas validity ");
              break;
          case XML_FROM_SCHEMASP:
              channel(data, "Schemas parser ");
              break;
          case XML_FROM_RELAXNGP:
              channel(data, "Relax-NG parser ");
              break;
          case XML_FROM_RELAXNGV:
              channel(data, "Relax-NG validity ");
              break;
          case XML_FROM_CATALOG:
              channel(data, "Catalog ");
              break;
          case XML_FROM_C14N:
              channel(data, "C14N ");
              break;
          case XML_FROM_XSLT:
              channel(data, "XSLT ");
              break;
          default:
              break;
      }
      if (code == XML_ERR_OK)
          return;
      switch (level) {
          case XML_ERR_NONE:
              channel(data, ": ");
              break;
          case XML_ERR_WARNING:
              channel(data, "warning : ");
              break;
          case XML_ERR_ERROR:
              channel(data, "error : ");
              break;
          case XML_ERR_FATAL:
              channel(data, "error : ");
              break;
      }
      if (code == XML_ERR_OK)
          return;
      if (str != NULL) {
          int len;
  	len = xmlStrlen((const xmlChar *)str);
  	if ((len > 0) && (str[len - 1] != '\n'))
  	    channel(data, "%s\n", str);
  	else
  	    channel(data, "%s", str);
      } else {
          channel(data, "%s\n", "out of memory error");
      }
      if (code == XML_ERR_OK)
          return;
  
      if (ctxt != NULL) {
          xmlParserPrintFileContextInternal(input, channel, data);
          if (cur != NULL) {
              if (cur->filename)
                  channel(data, "%s:%d: \n", cur->filename, cur->line);
              else if ((line != 0) && (domain == XML_FROM_PARSER))
                  channel(data, "Entity: line %d: \n", cur->line);
              xmlParserPrintFileContextInternal(cur, channel, data);
          }
      }
      if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) &&
          (err->int1 < 100) &&
  	(err->int1 < xmlStrlen((const xmlChar *)err->str1))) {
  	xmlChar buf[150];
  	int i;
  
  	channel(data, "%s\n", err->str1);
  	for (i=0;i < err->int1;i++)
  	     buf[i] = ' ';
  	buf[i++] = '^';
  	buf[i] = 0;
  	channel(data, "%s\n", buf);
      }
  }
  
  static void
  initializeLibxml2(void) {
      xmlGetWarningsDefaultValue = 0;
      xmlPedanticParserDefault(0);
  
      xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
      xmlInitParser();
      xmlSetExternalEntityLoader(testExternalEntityLoader);
      xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
  #ifdef LIBXML_SCHEMAS_ENABLED
      xmlSchemaInitTypes();
      xmlRelaxNGInitTypes();
  #endif
  }
  
  
  /************************************************************************
   *									*
   *		File name and path utilities				*
   *									*
   ************************************************************************/
  
  static const char *baseFilename(const char *filename) {
      const char *cur;
      if (filename == NULL)
          return(NULL);
      cur = &filename[strlen(filename)];
      while ((cur > filename) && (*cur != '/'))
          cur--;
      if (*cur == '/')
          return(cur + 1);
      return(cur);
  }
  
  static char *resultFilename(const char *filename, const char *out,
                              const char *suffix) {
      const char *base;
      char res[500];
      char suffixbuff[500];
  
  /*************
      if ((filename[0] == 't') && (filename[1] == 'e') &&
          (filename[2] == 's') && (filename[3] == 't') &&
  	(filename[4] == '/'))
  	filename = &filename[5];
   *************/
  
      base = baseFilename(filename);
      if (suffix == NULL)
          suffix = ".tmp";
      if (out == NULL)
          out = "";
  
      strncpy(suffixbuff,suffix,499);
  #ifdef VMS
      if(strstr(base,".") && suffixbuff[0]=='.')
        suffixbuff[0]='_';
  #endif
  
      if (snprintf(res, 499, "%s%s%s", out, base, suffixbuff) >= 499)
          res[499] = 0;
      return(strdup(res));
  }
  
  static int checkTestFile(const char *filename) {
      struct stat buf;
  
      if (stat(filename, &buf) == -1)
          return(0);
  
  #if defined(_WIN32) && !defined(__CYGWIN__)
      if (!(buf.st_mode & _S_IFREG))
          return(0);
  #else
      if (!S_ISREG(buf.st_mode))
          return(0);
  #endif
  
      return(1);
  }
  
  static int compareFiles(const char *r1 /* temp */, const char *r2 /* result */) {
      int res1, res2;
      int fd1, fd2;
      char bytes1[4096];
      char bytes2[4096];
  
      if (update_results) {
          fd1 = open(r1, RD_FLAGS);
          if (fd1 < 0)
              return(-1);
          fd2 = open(r2, WR_FLAGS, 0644);
          if (fd2 < 0) {
              close(fd1);
              return(-1);
          }
          do {
              res1 = read(fd1, bytes1, 4096);
              if (res1 <= 0)
                  break;
              res2 = write(fd2, bytes1, res1);
              if (res2 <= 0 || res2 != res1)
                  break;
          } while (1);
          close(fd2);
          close(fd1);
          return(res1 != 0);
      }
  
      fd1 = open(r1, RD_FLAGS);
      if (fd1 < 0)
          return(-1);
      fd2 = open(r2, RD_FLAGS);
      if (fd2 < 0) {
          close(fd1);
          return(-1);
      }
      while (1) {
          res1 = read(fd1, bytes1, 4096);
          res2 = read(fd2, bytes2, 4096);
  	if ((res1 != res2) || (res1 < 0)) {
  	    close(fd1);
  	    close(fd2);
  	    return(1);
  	}
  	if (res1 == 0)
  	    break;
  	if (memcmp(bytes1, bytes2, res1) != 0) {
  	    close(fd1);
  	    close(fd2);
  	    return(1);
  	}
      }
      close(fd1);
      close(fd2);
      return(0);
  }
  
  static int compareFileMem(const char *filename, const char *mem, int size) {
      int res;
      int fd;
      char bytes[4096];
      int idx = 0;
      struct stat info;
  
      if (update_results) {
          fd = open(filename, WR_FLAGS, 0644);
          if (fd < 0) {
  	    fprintf(stderr, "failed to open %s for writing", filename);
              return(-1);
  	}
          res = write(fd, mem, size);
          close(fd);
          return(res != size);
      }
  
      if (stat(filename, &info) < 0) {
          fprintf(stderr, "failed to stat %s\n", filename);
  	return(-1);
      }
      if (info.st_size != size) {
          fprintf(stderr, "file %s is %ld bytes, result is %d bytes\n",
  	        filename, (long) info.st_size, size);
          return(-1);
      }
      fd = open(filename, RD_FLAGS);
      if (fd < 0) {
  	fprintf(stderr, "failed to open %s for reading", filename);
          return(-1);
      }
      while (idx < size) {
          res = read(fd, bytes, 4096);
  	if (res <= 0)
  	    break;
  	if (res + idx > size)
  	    break;
  	if (memcmp(bytes, &mem[idx], res) != 0) {
  	    int ix;
  	    for (ix=0; ix<res; ix++)
  		if (bytes[ix] != mem[idx+ix])
  			break;
  	    fprintf(stderr,"Compare error at position %d\n", idx+ix);
  	    close(fd);
  	    return(1);
  	}
  	idx += res;
      }
      close(fd);
      if (idx != size) {
  	fprintf(stderr,"Compare error index %d, size %d\n", idx, size);
      }
      return(idx != size);
  }
  
  static int loadMem(const char *filename, const char **mem, int *size) {
      int fd, res;
      struct stat info;
      char *base;
      int siz = 0;
      if (stat(filename, &info) < 0)
  	return(-1);
      base = malloc(info.st_size + 1);
      if (base == NULL)
  	return(-1);
      if ((fd = open(filename, RD_FLAGS)) < 0) {
          free(base);
  	return(-1);
      }
      while ((res = read(fd, &base[siz], info.st_size - siz)) > 0) {
          siz += res;
      }
      close(fd);
  #if !defined(_WIN32)
      if (siz != info.st_size) {
          free(base);
  	return(-1);
      }
  #endif
      base[siz] = 0;
      *mem = base;
      *size = siz;
      return(0);
  }
  
  static int unloadMem(const char *mem) {
      free((char *)mem);
      return(0);
  }
  
  /************************************************************************
   *									*
   *		Tests implementations					*
   *									*
   ************************************************************************/
  
  /************************************************************************
   *									*
   *		Parse to SAX based tests				*
   *									*
   ************************************************************************/
  
  static FILE *SAXdebug = NULL;
  
  /*
   * empty SAX block
   */
  static xmlSAXHandler emptySAXHandlerStruct = {
      NULL, /* internalSubset */
      NULL, /* isStandalone */
      NULL, /* hasInternalSubset */
      NULL, /* hasExternalSubset */
      NULL, /* resolveEntity */
      NULL, /* getEntity */
      NULL, /* entityDecl */
      NULL, /* notationDecl */
      NULL, /* attributeDecl */
      NULL, /* elementDecl */
      NULL, /* unparsedEntityDecl */
      NULL, /* setDocumentLocator */
      NULL, /* startDocument */
      NULL, /* endDocument */
      NULL, /* startElement */
      NULL, /* endElement */
      NULL, /* reference */
      NULL, /* characters */
      NULL, /* ignorableWhitespace */
      NULL, /* processingInstruction */
      NULL, /* comment */
      NULL, /* xmlParserWarning */
      NULL, /* xmlParserError */
      NULL, /* xmlParserError */
      NULL, /* getParameterEntity */
      NULL, /* cdataBlock; */
      NULL, /* externalSubset; */
      1,
      NULL,
      NULL, /* startElementNs */
      NULL, /* endElementNs */
      NULL  /* xmlStructuredErrorFunc */
  };
  
  static xmlSAXHandlerPtr emptySAXHandler = &emptySAXHandlerStruct;
  static int callbacks = 0;
  static int quiet = 0;
  
  /**
   * isStandaloneDebug:
   * @ctxt:  An XML parser context
   *
   * Is this document tagged standalone ?
   *
   * Returns 1 if true
   */
  static int
  isStandaloneDebug(void *ctx ATTRIBUTE_UNUSED)
  {
      callbacks++;
      if (quiet)
  	return(0);
      fprintf(SAXdebug, "SAX.isStandalone()\n");
      return(0);
  }
  
  /**
   * hasInternalSubsetDebug:
   * @ctxt:  An XML parser context
   *
   * Does this document has an internal subset
   *
   * Returns 1 if true
   */
  static int
  hasInternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED)
  {
      callbacks++;
      if (quiet)
  	return(0);
      fprintf(SAXdebug, "SAX.hasInternalSubset()\n");
      return(0);
  }
  
  /**
   * hasExternalSubsetDebug:
   * @ctxt:  An XML parser context
   *
   * Does this document has an external subset
   *
   * Returns 1 if true
   */
  static int
  hasExternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED)
  {
      callbacks++;
      if (quiet)
  	return(0);
      fprintf(SAXdebug, "SAX.hasExternalSubset()\n");
      return(0);
  }
  
  /**
   * internalSubsetDebug:
   * @ctxt:  An XML parser context
   *
   * Does this document has an internal subset
   */
  static void
  internalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
  	       const xmlChar *ExternalID, const xmlChar *SystemID)
  {
      callbacks++;
      if (quiet)
  	return;
      if (name == NULL)
          name = BAD_CAST "(null)";
      fprintf(SAXdebug, "SAX.internalSubset(%s,", name);
      if (ExternalID == NULL)
  	fprintf(SAXdebug, " ,");
      else
  	fprintf(SAXdebug, " %s,", ExternalID);
      if (SystemID == NULL)
  	fprintf(SAXdebug, " )\n");
      else
  	fprintf(SAXdebug, " %s)\n", SystemID);
  }
  
  /**
   * externalSubsetDebug:
   * @ctxt:  An XML parser context
   *
   * Does this document has an external subset
   */
  static void
  externalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
  	       const xmlChar *ExternalID, const xmlChar *SystemID)
  {
      callbacks++;
      if (quiet)
  	return;
      fprintf(SAXdebug, "SAX.externalSubset(%s,", name);
      if (ExternalID == NULL)
  	fprintf(SAXdebug, " ,");
      else
  	fprintf(SAXdebug, " %s,", ExternalID);
      if (SystemID == NULL)
  	fprintf(SAXdebug, " )\n");
      else
  	fprintf(SAXdebug, " %s)\n", SystemID);
  }
  
  /**
   * resolveEntityDebug:
   * @ctxt:  An XML parser context
   * @publicId: The public ID of the entity
   * @systemId: The system ID of the entity
   *
   * Special entity resolver, better left to the parser, it has
   * more context than the application layer.
   * The default behaviour is to NOT resolve the entities, in that case
   * the ENTITY_REF nodes are built in the structure (and the parameter
   * values).
   *
   * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
   */
  static xmlParserInputPtr
  resolveEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *publicId, const xmlChar *systemId)
  {
      callbacks++;
      if (quiet)
  	return(NULL);
      /* xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; */
  
  
      fprintf(SAXdebug, "SAX.resolveEntity(");
      if (publicId != NULL)
  	fprintf(SAXdebug, "%s", (char *)publicId);
      else
  	fprintf(SAXdebug, " ");
      if (systemId != NULL)
  	fprintf(SAXdebug, ", %s)\n", (char *)systemId);
      else
  	fprintf(SAXdebug, ", )\n");
  /*********
      if (systemId != NULL) {
          return(xmlNewInputFromFile(ctxt, (char *) systemId));
      }
   *********/
      return(NULL);
  }
  
  /**
   * getEntityDebug:
   * @ctxt:  An XML parser context
   * @name: The entity name
   *
   * Get an entity by name
   *
   * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
   */
  static xmlEntityPtr
  getEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
  {
      callbacks++;
      if (quiet)
  	return(NULL);
      fprintf(SAXdebug, "SAX.getEntity(%s)\n", name);
      return(NULL);
  }
  
  /**
   * getParameterEntityDebug:
   * @ctxt:  An XML parser context
   * @name: The entity name
   *
   * Get a parameter entity by name
   *
   * Returns the xmlParserInputPtr
   */
  static xmlEntityPtr
  getParameterEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
  {
      callbacks++;
      if (quiet)
  	return(NULL);
      fprintf(SAXdebug, "SAX.getParameterEntity(%s)\n", name);
      return(NULL);
  }
  
  
  /**
   * entityDeclDebug:
   * @ctxt:  An XML parser context
   * @name:  the entity name
   * @type:  the entity type
   * @publicId: The public ID of the entity
   * @systemId: The system ID of the entity
   * @content: the entity value (without processing).
   *
   * An entity definition has been parsed
   */
  static void
  entityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type,
            const xmlChar *publicId, const xmlChar *systemId, xmlChar *content)
  {
  const xmlChar *nullstr = BAD_CAST "(null)";
      /* not all libraries handle printing null pointers nicely */
      if (publicId == NULL)
          publicId = nullstr;
      if (systemId == NULL)
          systemId = nullstr;
      if (content == NULL)
          content = (xmlChar *)nullstr;
      callbacks++;
      if (quiet)
  	return;
      fprintf(SAXdebug, "SAX.entityDecl(%s, %d, %s, %s, %s)\n",
              name, type, publicId, systemId, content);
  }
  
  /**
   * attributeDeclDebug:
   * @ctxt:  An XML parser context
   * @name:  the attribute name
   * @type:  the attribute type
   *
   * An attribute definition has been parsed
   */
  static void
  attributeDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar * elem,
                     const xmlChar * name, int type, int def,
                     const xmlChar * defaultValue, xmlEnumerationPtr tree)
  {
      callbacks++;
      if (quiet)
          return;
      if (defaultValue == NULL)
          fprintf(SAXdebug, "SAX.attributeDecl(%s, %s, %d, %d, NULL, ...)\n",
                  elem, name, type, def);
      else
          fprintf(SAXdebug, "SAX.attributeDecl(%s, %s, %d, %d, %s, ...)\n",
                  elem, name, type, def, defaultValue);
      xmlFreeEnumeration(tree);
  }
  
  /**
   * elementDeclDebug:
   * @ctxt:  An XML parser context
   * @name:  the element name
   * @type:  the element type
   * @content: the element value (without processing).
   *
   * An element definition has been parsed
   */
  static void
  elementDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type,
  	    xmlElementContentPtr content ATTRIBUTE_UNUSED)
  {
      callbacks++;
      if (quiet)
  	return;
      fprintf(SAXdebug, "SAX.elementDecl(%s, %d, ...)\n",
              name, type);
  }
  
  /**
   * notationDeclDebug:
   * @ctxt:  An XML parser context
   * @name: The name of the notation
   * @publicId: The public ID of the entity
   * @systemId: The system ID of the entity
   *
   * What to do when a notation declaration has been parsed.
   */
  static void
  notationDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
  	     const xmlChar *publicId, const xmlChar *systemId)
  {
      callbacks++;
      if (quiet)
  	return;
      fprintf(SAXdebug, "SAX.notationDecl(%s, %s, %s)\n",
              (char *) name, (char *) publicId, (char *) systemId);
  }
  
  /**
   * unparsedEntityDeclDebug:
   * @ctxt:  An XML parser context
   * @name: The name of the entity
   * @publicId: The public ID of the entity
   * @systemId: The system ID of the entity
   * @notationName: the name of the notation
   *
   * What to do when an unparsed entity declaration is parsed
   */
  static void
  unparsedEntityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
  		   const xmlChar *publicId, const xmlChar *systemId,
  		   const xmlChar *notationName)
  {
  const xmlChar *nullstr = BAD_CAST "(null)";
  
      if (publicId == NULL)
          publicId = nullstr;
      if (systemId == NULL)
          systemId = nullstr;
      if (notationName == NULL)
          notationName = nullstr;
      callbacks++;
      if (quiet)
  	return;
      fprintf(SAXdebug, "SAX.unparsedEntityDecl(%s, %s, %s, %s)\n",
              (char *) name, (char *) publicId, (char *) systemId,
  	    (char *) notationName);
  }
  
  /**
   * setDocumentLocatorDebug:
   * @ctxt:  An XML parser context
   * @loc: A SAX Locator
   *
   * Receive the document locator at startup, actually xmlDefaultSAXLocator
   * Everything is available on the context, so this is useless in our case.
   */
  static void
  setDocumentLocatorDebug(void *ctx ATTRIBUTE_UNUSED, xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED)
  {
      callbacks++;
      if (quiet)
  	return;
      fprintf(SAXdebug, "SAX.setDocumentLocator()\n");
  }
  
  /**
   * startDocumentDebug:
   * @ctxt:  An XML parser context
   *
   * called when the document start being processed.
   */
  static void
  startDocumentDebug(void *ctx ATTRIBUTE_UNUSED)
  {
      callbacks++;
      if (quiet)
  	return;
      fprintf(SAXdebug, "SAX.startDocument()\n");
  }
  
  /**
   * endDocumentDebug:
   * @ctxt:  An XML parser context
   *
   * called when the document end has been detected.
   */
  static void
  endDocumentDebug(void *ctx ATTRIBUTE_UNUSED)
  {
      callbacks++;
      if (quiet)
  	return;
      fprintf(SAXdebug, "SAX.endDocument()\n");
  }
  
  /**
   * startElementDebug:
   * @ctxt:  An XML parser context
   * @name:  The element name
   *
   * called when an opening tag has been processed.
   */
  static void
  startElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar **atts)
  {
      int i;
  
      callbacks++;
      if (quiet)
  	return;
      fprintf(SAXdebug, "SAX.startElement(%s", (char *) name);
      if (atts != NULL) {
          for (i = 0;(atts[i] != NULL);i++) {
  	    fprintf(SAXdebug, ", %s='", atts[i++]);
  	    if (atts[i] != NULL)
  	        fprintf(SAXdebug, "%s'", atts[i]);
  	}
      }
      fprintf(SAXdebug, ")\n");
  }
  
  /**
   * endElementDebug:
   * @ctxt:  An XML parser context
   * @name:  The element name
   *
   * called when the end of an element has been detected.
   */
  static void
  endElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
  {
      callbacks++;
      if (quiet)
  	return;
      fprintf(SAXdebug, "SAX.endElement(%s)\n", (char *) name);
  }
  
  /**
   * charactersDebug:
   * @ctxt:  An XML parser context
   * @ch:  a xmlChar string
   * @len: the number of xmlChar
   *
   * receiving some chars from the parser.
   * Question: how much at a time ???
   */
  static void
  charactersDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
  {
      char output[40];
      int i;
  
      callbacks++;
      if (quiet)
  	return;
      for (i = 0;(i<len) && (i < 30);i++)
  	output[i] = ch[i];
      output[i] = 0;
  
      fprintf(SAXdebug, "SAX.characters(%s, %d)\n", output, len);
  }
  
  /**
   * referenceDebug:
   * @ctxt:  An XML parser context
   * @name:  The entity name
   *
   * called when an entity reference is detected.
   */
  static void
  referenceDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
  {
      callbacks++;
      if (quiet)
  	return;
      fprintf(SAXdebug, "SAX.reference(%s)\n", name);
  }
  
  /**
   * ignorableWhitespaceDebug:
   * @ctxt:  An XML parser context
   * @ch:  a xmlChar string
   * @start: the first char in the string
   * @len: the number of xmlChar
   *
   * receiving some ignorable whitespaces from the parser.
   * Question: how much at a time ???
   */
  static void
  ignorableWhitespaceDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
  {
      char output[40];
      int i;
  
      callbacks++;
      if (quiet)
  	return;
      for (i = 0;(i<len) && (i < 30);i++)
  	output[i] = ch[i];
      output[i] = 0;
      fprintf(SAXdebug, "SAX.ignorableWhitespace(%s, %d)\n", output, len);
  }
  
  /**
   * processingInstructionDebug:
   * @ctxt:  An XML parser context
   * @target:  the target name
   * @data: the PI data's
   * @len: the number of xmlChar
   *
   * A processing instruction has been parsed.
   */
  static void
  processingInstructionDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *target,
                        const xmlChar *data)
  {
      callbacks++;
      if (quiet)
  	return;
      if (data != NULL)
  	fprintf(SAXdebug, "SAX.processingInstruction(%s, %s)\n",
  		(char *) target, (char *) data);
      else
  	fprintf(SAXdebug, "SAX.processingInstruction(%s, NULL)\n",
  		(char *) target);
  }
  
  /**
   * cdataBlockDebug:
   * @ctx: the user data (XML parser context)
   * @value:  The pcdata content
   * @len:  the block length
   *
   * called when a pcdata block has been parsed
   */
  static void
  cdataBlockDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *value, int len)
  {
      callbacks++;
      if (quiet)
  	return;
      fprintf(SAXdebug, "SAX.pcdata(%.20s, %d)\n",
  	    (char *) value, len);
  }
  
  /**
   * commentDebug:
   * @ctxt:  An XML parser context
   * @value:  the comment content
   *
   * A comment has been parsed.
   */
  static void
  commentDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *value)
  {
      callbacks++;
      if (quiet)
  	return;
      fprintf(SAXdebug, "SAX.comment(%s)\n", value);
  }
  
  /**
   * warningDebug:
   * @ctxt:  An XML parser context
   * @msg:  the message to display/transmit
   * @...:  extra parameters for the message display
   *
   * Display and format a warning messages, gives file, line, position and
   * extra parameters.
   */
  static void XMLCDECL
  warningDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...)
  {
      va_list args;
  
      callbacks++;
      if (quiet)
  	return;
      va_start(args, msg);
      fprintf(SAXdebug, "SAX.warning: ");
      vfprintf(SAXdebug, msg, args);
      va_end(args);
  }
  
  /**
   * errorDebug:
   * @ctxt:  An XML parser context
   * @msg:  the message to display/transmit
   * @...:  extra parameters for the message display
   *
   * Display and format a error messages, gives file, line, position and
   * extra parameters.
   */
  static void XMLCDECL
  errorDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...)
  {
      va_list args;
  
      callbacks++;
      if (quiet)
  	return;
      va_start(args, msg);
      fprintf(SAXdebug, "SAX.error: ");
      vfprintf(SAXdebug, msg, args);
      va_end(args);
  }
  
  /**
   * fatalErrorDebug:
   * @ctxt:  An XML parser context
   * @msg:  the message to display/transmit
   * @...:  extra parameters for the message display
   *
   * Display and format a fatalError messages, gives file, line, position and
   * extra parameters.
   */
  static void XMLCDECL
  fatalErrorDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...)
  {
      va_list args;
  
      callbacks++;
      if (quiet)
  	return;
      va_start(args, msg);
      fprintf(SAXdebug, "SAX.fatalError: ");
      vfprintf(SAXdebug, msg, args);
      va_end(args);
  }
  
  static xmlSAXHandler debugSAXHandlerStruct = {
      internalSubsetDebug,
      isStandaloneDebug,
      hasInternalSubsetDebug,
      hasExternalSubsetDebug,
      resolveEntityDebug,
      getEntityDebug,
      entityDeclDebug,
      notationDeclDebug,
      attributeDeclDebug,
      elementDeclDebug,
      unparsedEntityDeclDebug,
      setDocumentLocatorDebug,
      startDocumentDebug,
      endDocumentDebug,
      startElementDebug,
      endElementDebug,
      referenceDebug,
      charactersDebug,
      ignorableWhitespaceDebug,
      processingInstructionDebug,
      commentDebug,
      warningDebug,
      errorDebug,
      fatalErrorDebug,
      getParameterEntityDebug,
      cdataBlockDebug,
      externalSubsetDebug,
      1,
      NULL,
      NULL,
      NULL,
      NULL
  };
  
  static xmlSAXHandlerPtr debugSAXHandler = &debugSAXHandlerStruct;
  
  /*
   * SAX2 specific callbacks
   */
  /**
   * startElementNsDebug:
   * @ctxt:  An XML parser context
   * @name:  The element name
   *
   * called when an opening tag has been processed.
   */
  static void
  startElementNsDebug(void *ctx ATTRIBUTE_UNUSED,
                      const xmlChar *localname,
                      const xmlChar *prefix,
                      const xmlChar *URI,
  		    int nb_namespaces,
  		    const xmlChar **namespaces,
  		    int nb_attributes,
  		    int nb_defaulted,
  		    const xmlChar **attributes)
  {
      int i;
  
      callbacks++;
      if (quiet)
  	return;
      fprintf(SAXdebug, "SAX.startElementNs(%s", (char *) localname);
      if (prefix == NULL)
  	fprintf(SAXdebug, ", NULL");
      else
  	fprintf(SAXdebug, ", %s", (char *) prefix);
      if (URI == NULL)
  	fprintf(SAXdebug, ", NULL");
      else
  	fprintf(SAXdebug, ", '%s'", (char *) URI);
      fprintf(SAXdebug, ", %d", nb_namespaces);
  
      if (namespaces != NULL) {
          for (i = 0;i < nb_namespaces * 2;i++) {
  	    fprintf(SAXdebug, ", xmlns");
  	    if (namespaces[i] != NULL)
  	        fprintf(SAXdebug, ":%s", namespaces[i]);
  	    i++;
  	    fprintf(SAXdebug, "='%s'", namespaces[i]);
  	}
      }
      fprintf(SAXdebug, ", %d, %d", nb_attributes, nb_defaulted);
      if (attributes != NULL) {
          for (i = 0;i < nb_attributes * 5;i += 5) {
  	    if (attributes[i + 1] != NULL)
  		fprintf(SAXdebug, ", %s:%s='", attributes[i + 1], attributes[i]);
  	    else
  		fprintf(SAXdebug, ", %s='", attributes[i]);
  	    fprintf(SAXdebug, "%.4s...', %d", attributes[i + 3],
  		    (int)(attributes[i + 4] - attributes[i + 3]));
  	}
      }
      fprintf(SAXdebug, ")\n");
  }
  
  /**
   * endElementDebug:
   * @ctxt:  An XML parser context
   * @name:  The element name
   *
   * called when the end of an element has been detected.
   */
  static void
  endElementNsDebug(void *ctx ATTRIBUTE_UNUSED,
                    const xmlChar *localname,
                    const xmlChar *prefix,
                    const xmlChar *URI)
  {
      callbacks++;
      if (quiet)
  	return;
      fprintf(SAXdebug, "SAX.endElementNs(%s", (char *) localname);
      if (prefix == NULL)
  	fprintf(SAXdebug, ", NULL");
      else
  	fprintf(SAXdebug, ", %s", (char *) prefix);
      if (URI == NULL)
  	fprintf(SAXdebug, ", NULL)\n");
      else
  	fprintf(SAXdebug, ", '%s')\n", (char *) URI);
  }
  
  static xmlSAXHandler debugSAX2HandlerStruct = {
      internalSubsetDebug,
      isStandaloneDebug,
      hasInternalSubsetDebug,
      hasExternalSubsetDebug,
      resolveEntityDebug,
      getEntityDebug,
      entityDeclDebug,
      notationDeclDebug,
      attributeDeclDebug,
      elementDeclDebug,
      unparsedEntityDeclDebug,
      setDocumentLocatorDebug,
      startDocumentDebug,
      endDocumentDebug,
      NULL,
      NULL,
      referenceDebug,
      charactersDebug,
      ignorableWhitespaceDebug,
      processingInstructionDebug,
      commentDebug,
      warningDebug,
      errorDebug,
      fatalErrorDebug,
      getParameterEntityDebug,
      cdataBlockDebug,
      externalSubsetDebug,
      XML_SAX2_MAGIC,
      NULL,
      startElementNsDebug,
      endElementNsDebug,
      NULL
  };
  
  static xmlSAXHandlerPtr debugSAX2Handler = &debugSAX2HandlerStruct;
  
  #ifdef LIBXML_HTML_ENABLED
  /**
   * htmlstartElementDebug:
   * @ctxt:  An XML parser context
   * @name:  The element name
   *
   * called when an opening tag has been processed.
   */
  static void
  htmlstartElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar **atts)
  {
      int i;
  
      fprintf(SAXdebug, "SAX.startElement(%s", (char *) name);
      if (atts != NULL) {
          for (i = 0;(atts[i] != NULL);i++) {
  	    fprintf(SAXdebug, ", %s", atts[i++]);
  	    if (atts[i] != NULL) {
  		unsigned char output[40];
  		const unsigned char *att = atts[i];
  		int outlen, attlen;
  	        fprintf(SAXdebug, "='");
  		while ((attlen = strlen((char*)att)) > 0) {
  		    outlen = sizeof output - 1;
  		    htmlEncodeEntities(output, &outlen, att, &attlen, '\'');
  		    output[outlen] = 0;
  		    fprintf(SAXdebug, "%s", (char *) output);
  		    att += attlen;
  		}
  		fprintf(SAXdebug, "'");
  	    }
  	}
      }
      fprintf(SAXdebug, ")\n");
  }
  
  /**
   * htmlcharactersDebug:
   * @ctxt:  An XML parser context
   * @ch:  a xmlChar string
   * @len: the number of xmlChar
   *
   * receiving some chars from the parser.
   * Question: how much at a time ???
   */
  static void
  htmlcharactersDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
  {
      unsigned char output[40];
      int inlen = len, outlen = 30;
  
      htmlEncodeEntities(output, &outlen, ch, &inlen, 0);
      output[outlen] = 0;
  
      fprintf(SAXdebug, "SAX.characters(%s, %d)\n", output, len);
  }
  
  /**
   * htmlcdataDebug:
   * @ctxt:  An XML parser context
   * @ch:  a xmlChar string
   * @len: the number of xmlChar
   *
   * receiving some cdata chars from the parser.
   * Question: how much at a time ???
   */
  static void
  htmlcdataDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
  {
      unsigned char output[40];
      int inlen = len, outlen = 30;
  
      htmlEncodeEntities(output, &outlen, ch, &inlen, 0);
      output[outlen] = 0;
  
      fprintf(SAXdebug, "SAX.cdata(%s, %d)\n", output, len);
  }
  
  static xmlSAXHandler debugHTMLSAXHandlerStruct = {
      internalSubsetDebug,
      isStandaloneDebug,
      hasInternalSubsetDebug,
      hasExternalSubsetDebug,
      resolveEntityDebug,
      getEntityDebug,
      entityDeclDebug,
      notationDeclDebug,
      attributeDeclDebug,
      elementDeclDebug,
      unparsedEntityDeclDebug,
      setDocumentLocatorDebug,
      startDocumentDebug,
      endDocumentDebug,
      htmlstartElementDebug,
      endElementDebug,
      referenceDebug,
      htmlcharactersDebug,
      ignorableWhitespaceDebug,
      processingInstructionDebug,
      commentDebug,
      warningDebug,
      errorDebug,
      fatalErrorDebug,
      getParameterEntityDebug,
      htmlcdataDebug,
      externalSubsetDebug,
      1,
      NULL,
      NULL,
      NULL,
      NULL
  };
  
  static xmlSAXHandlerPtr debugHTMLSAXHandler = &debugHTMLSAXHandlerStruct;
  #endif /* LIBXML_HTML_ENABLED */
  
  /**
   * saxParseTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a file using the SAX API and check for errors.
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  saxParseTest(const char *filename, const char *result,
               const char *err ATTRIBUTE_UNUSED,
               int options) {
      int ret;
      char *temp;
  
      nb_tests++;
      temp = resultFilename(filename, temp_directory, ".res");
      if (temp == NULL) {
          fprintf(stderr, "out of memory\n");
          fatalError();
      }
      SAXdebug = fopen(temp, "wb");
      if (SAXdebug == NULL) {
          fprintf(stderr, "Failed to write to %s\n", temp);
  	free(temp);
  	return(-1);
      }
  
      /* for SAX we really want the callbacks though the context handlers */
      xmlSetStructuredErrorFunc(NULL, NULL);
      xmlSetGenericErrorFunc(NULL, testErrorHandler);
  
  #ifdef LIBXML_HTML_ENABLED
      if (options & XML_PARSE_HTML) {
  	htmlSAXParseFile(filename, NULL, emptySAXHandler, NULL);
  	ret = 0;
      } else
  #endif
      {
          xmlParserCtxtPtr ctxt = xmlCreateFileParserCtxt(filename);
          memcpy(ctxt->sax, emptySAXHandler, sizeof(xmlSAXHandler));
          xmlCtxtUseOptions(ctxt, options);
          xmlParseDocument(ctxt);
          ret = ctxt->wellFormed ? 0 : ctxt->errNo;
          xmlFreeDoc(ctxt->myDoc);
          xmlFreeParserCtxt(ctxt);
      }
      if (ret == XML_WAR_UNDECLARED_ENTITY) {
          fprintf(SAXdebug, "xmlSAXUserParseFile returned error %d\n", ret);
          ret = 0;
      }
      if (ret != 0) {
          fprintf(stderr, "Failed to parse %s\n", filename);
  	ret = 1;
  	goto done;
      }
  #ifdef LIBXML_HTML_ENABLED
      if (options & XML_PARSE_HTML) {
  	htmlSAXParseFile(filename, NULL, debugHTMLSAXHandler, NULL);
  	ret = 0;
      } else
  #endif
      {
          xmlParserCtxtPtr ctxt = xmlCreateFileParserCtxt(filename);
          if (options & XML_PARSE_SAX1) {
              memcpy(ctxt->sax, debugSAXHandler, sizeof(xmlSAXHandler));
              options -= XML_PARSE_SAX1;
          } else {
              memcpy(ctxt->sax, debugSAX2Handler, sizeof(xmlSAXHandler));
          }
          xmlCtxtUseOptions(ctxt, options);
          xmlParseDocument(ctxt);
          ret = ctxt->wellFormed ? 0 : ctxt->errNo;
          xmlFreeDoc(ctxt->myDoc);
          xmlFreeParserCtxt(ctxt);
      }
      if (ret == XML_WAR_UNDECLARED_ENTITY) {
          fprintf(SAXdebug, "xmlSAXUserParseFile returned error %d\n", ret);
          ret = 0;
      }
      fclose(SAXdebug);
      if (compareFiles(temp, result)) {
          fprintf(stderr, "Got a difference for %s\n", filename);
          ret = 1;
      }
  
  done:
      if (temp != NULL) {
          unlink(temp);
          free(temp);
      }
  
      /* switch back to structured error handling */
      xmlSetGenericErrorFunc(NULL, NULL);
      xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
  
      return(ret);
  }
  
  /************************************************************************
   *									*
   *		Parse to tree based tests				*
   *									*
   ************************************************************************/
  /**
   * oldParseTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages: unused
   *
   * Parse a file using the old xmlParseFile API, then serialize back
   * reparse the result and serialize again, then check for deviation
   * in serialization.
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  oldParseTest(const char *filename, const char *result,
               const char *err ATTRIBUTE_UNUSED,
  	     int options ATTRIBUTE_UNUSED) {
      xmlDocPtr doc;
      char *temp;
      int res = 0;
  
      nb_tests++;
      /*
       * base of the test, parse with the old API
       */
  #ifdef LIBXML_SAX1_ENABLED
      doc = xmlParseFile(filename);
  #else
      doc = xmlReadFile(filename, NULL, 0);
  #endif
      if (doc == NULL)
          return(1);
      temp = resultFilename(filename, temp_directory, ".res");
      if (temp == NULL) {
          fprintf(stderr, "out of memory\n");
          fatalError();
      }
      xmlSaveFile(temp, doc);
      if (compareFiles(temp, result)) {
          res = 1;
      }
      xmlFreeDoc(doc);
  
      /*
       * Parse the saved result to make sure the round trip is okay
       */
  #ifdef LIBXML_SAX1_ENABLED
      doc = xmlParseFile(temp);
  #else
      doc = xmlReadFile(temp, NULL, 0);
  #endif
      if (doc == NULL)
          return(1);
      xmlSaveFile(temp, doc);
      if (compareFiles(temp, result)) {
          res = 1;
      }
      xmlFreeDoc(doc);
  
      if (temp != NULL) {
          unlink(temp);
          free(temp);
      }
      return(res);
  }
  
  #ifdef LIBXML_PUSH_ENABLED
  /**
   * pushParseTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages: unused
   *
   * Parse a file using the Push API, then serialize back
   * to check for content.
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  pushParseTest(const char *filename, const char *result,
               const char *err ATTRIBUTE_UNUSED,
  	     int options) {
      xmlParserCtxtPtr ctxt;
      xmlDocPtr doc;
      const char *base;
      int size, res;
      int cur = 0;
      int chunkSize = 4;
  
      nb_tests++;
      /*
       * load the document in memory and work from there.
       */
      if (loadMem(filename, &base, &size) != 0) {
          fprintf(stderr, "Failed to load %s\n", filename);
  	return(-1);
      }
  
      if (chunkSize > size)
          chunkSize = size;
  
  #ifdef LIBXML_HTML_ENABLED
      if (options & XML_PARSE_HTML)
  	ctxt = htmlCreatePushParserCtxt(NULL, NULL, base + cur, chunkSize, filename,
  	                                XML_CHAR_ENCODING_NONE);
      else
  #endif
      ctxt = xmlCreatePushParserCtxt(NULL, NULL, base + cur, chunkSize, filename);
      xmlCtxtUseOptions(ctxt, options);
      cur += chunkSize;
      chunkSize = 1024;
      do {
          if (cur + chunkSize >= size) {
  #ifdef LIBXML_HTML_ENABLED
  	    if (options & XML_PARSE_HTML)
  		htmlParseChunk(ctxt, base + cur, size - cur, 1);
  	    else
  #endif
  	    xmlParseChunk(ctxt, base + cur, size - cur, 1);
  	    break;
  	} else {
  #ifdef LIBXML_HTML_ENABLED
  	    if (options & XML_PARSE_HTML)
  		htmlParseChunk(ctxt, base + cur, chunkSize, 0);
  	    else
  #endif
  	    xmlParseChunk(ctxt, base + cur, chunkSize, 0);
  	    cur += chunkSize;
  	}
      } while (cur < size);
      doc = ctxt->myDoc;
  #ifdef LIBXML_HTML_ENABLED
      if (options & XML_PARSE_HTML)
          res = 1;
      else
  #endif
      res = ctxt->wellFormed;
      xmlFreeParserCtxt(ctxt);
      free((char *)base);
      if (!res) {
  	xmlFreeDoc(doc);
  	fprintf(stderr, "Failed to parse %s\n", filename);
  	return(-1);
      }
  #ifdef LIBXML_HTML_ENABLED
      if (options & XML_PARSE_HTML)
  	htmlDocDumpMemory(doc, (xmlChar **) &base, &size);
      else
  #endif
      xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
      xmlFreeDoc(doc);
      res = compareFileMem(result, base, size);
      if ((base == NULL) || (res != 0)) {
  	if (base != NULL)
  	    xmlFree((char *)base);
          fprintf(stderr, "Result for %s failed in %s\n", filename, result);
  	return(-1);
      }
      xmlFree((char *)base);
      if (err != NULL) {
  	res = compareFileMem(err, testErrors, testErrorsSize);
  	if (res != 0) {
  	    fprintf(stderr, "Error for %s failed\n", filename);
  	    return(-1);
  	}
      }
      return(0);
  }
  #endif
  
  /**
   * memParseTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages: unused
   *
   * Parse a file using the old xmlReadMemory API, then serialize back
   * reparse the result and serialize again, then check for deviation
   * in serialization.
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  memParseTest(const char *filename, const char *result,
               const char *err ATTRIBUTE_UNUSED,
  	     int options ATTRIBUTE_UNUSED) {
      xmlDocPtr doc;
      const char *base;
      int size, res;
  
      nb_tests++;
      /*
       * load and parse the memory
       */
      if (loadMem(filename, &base, &size) != 0) {
          fprintf(stderr, "Failed to load %s\n", filename);
  	return(-1);
      }
  
      doc = xmlReadMemory(base, size, filename, NULL, 0);
      unloadMem(base);
      if (doc == NULL) {
          return(1);
      }
      xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
      xmlFreeDoc(doc);
      res = compareFileMem(result, base, size);
      if ((base == NULL) || (res != 0)) {
  	if (base != NULL)
  	    xmlFree((char *)base);
          fprintf(stderr, "Result for %s failed in %s\n", filename, result);
  	return(-1);
      }
      xmlFree((char *)base);
      return(0);
  }
  
  /**
   * noentParseTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages: unused
   *
   * Parse a file with entity resolution, then serialize back
   * reparse the result and serialize again, then check for deviation
   * in serialization.
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  noentParseTest(const char *filename, const char *result,
                 const char *err  ATTRIBUTE_UNUSED,
  	       int options) {
      xmlDocPtr doc;
      char *temp;
      int res = 0;
  
      nb_tests++;
      /*
       * base of the test, parse with the old API
       */
      doc = xmlReadFile(filename, NULL, options);
      if (doc == NULL)
          return(1);
      temp = resultFilename(filename, temp_directory, ".res");
      if (temp == NULL) {
          fprintf(stderr, "Out of memory\n");
          fatalError();
      }
      xmlSaveFile(temp, doc);
      if (compareFiles(temp, result)) {
          res = 1;
      }
      xmlFreeDoc(doc);
  
      /*
       * Parse the saved result to make sure the round trip is okay
       */
      doc = xmlReadFile(filename, NULL, options);
      if (doc == NULL)
          return(1);
      xmlSaveFile(temp, doc);
      if (compareFiles(temp, result)) {
          res = 1;
      }
      xmlFreeDoc(doc);
  
      if (temp != NULL) {
          unlink(temp);
          free(temp);
      }
      return(res);
  }
  
  /**
   * errParseTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a file using the xmlReadFile API and check for errors.
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  errParseTest(const char *filename, const char *result, const char *err,
               int options) {
      xmlDocPtr doc;
      const char *base = NULL;
      int size, res = 0;
  
      nb_tests++;
  #ifdef LIBXML_HTML_ENABLED
      if (options & XML_PARSE_HTML) {
          doc = htmlReadFile(filename, NULL, options);
      } else
  #endif
  #ifdef LIBXML_XINCLUDE_ENABLED
      if (options & XML_PARSE_XINCLUDE) {
  	doc = xmlReadFile(filename, NULL, options);
  	xmlXIncludeProcessFlags(doc, options);
      } else
  #endif
      {
  	xmlGetWarningsDefaultValue = 1;
  	doc = xmlReadFile(filename, NULL, options);
      }
      xmlGetWarningsDefaultValue = 0;
      if (result) {
  	if (doc == NULL) {
  	    base = "";
  	    size = 0;
  	} else {
  #ifdef LIBXML_HTML_ENABLED
  	    if (options & XML_PARSE_HTML) {
  		htmlDocDumpMemory(doc, (xmlChar **) &base, &size);
  	    } else
  #endif
  	    xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
  	}
  	res = compareFileMem(result, base, size);
      }
      if (doc != NULL) {
  	if (base != NULL)
  	    xmlFree((char *)base);
  	xmlFreeDoc(doc);
      }
      if (res != 0) {
          fprintf(stderr, "Result for %s failed in %s\n", filename, result);
          return(-1);
      }
      if (err != NULL) {
  	res = compareFileMem(err, testErrors, testErrorsSize);
  	if (res != 0) {
  	    fprintf(stderr, "Error for %s failed\n", filename);
  	    return(-1);
  	}
      } else if (options & XML_PARSE_DTDVALID) {
          if (testErrorsSize != 0)
  	    fprintf(stderr, "Validation for %s failed\n", filename);
      }
  
      return(0);
  }
  
  #ifdef LIBXML_READER_ENABLED
  /************************************************************************
   *									*
   *		Reader based tests					*
   *									*
   ************************************************************************/
  
  static void processNode(FILE *out, xmlTextReaderPtr reader) {
      const xmlChar *name, *value;
      int type, empty;
  
      type = xmlTextReaderNodeType(reader);
      empty = xmlTextReaderIsEmptyElement(reader);
  
      name = xmlTextReaderConstName(reader);
      if (name == NULL)
  	name = BAD_CAST "--";
  
      value = xmlTextReaderConstValue(reader);
  
  
      fprintf(out, "%d %d %s %d %d",
  	    xmlTextReaderDepth(reader),
  	    type,
  	    name,
  	    empty,
  	    xmlTextReaderHasValue(reader));
      if (value == NULL)
  	fprintf(out, "\n");
      else {
  	fprintf(out, " %s\n", value);
      }
  }
  static int
  streamProcessTest(const char *filename, const char *result, const char *err,
                    xmlTextReaderPtr reader, const char *rng,
                    int options ATTRIBUTE_UNUSED) {
      int ret;
      char *temp = NULL;
      FILE *t = NULL;
  
      if (reader == NULL)
          return(-1);
  
      nb_tests++;
      if (result != NULL) {
  	temp = resultFilename(filename, temp_directory, ".res");
  	if (temp == NULL) {
  	    fprintf(stderr, "Out of memory\n");
  	    fatalError();
  	}
  	t = fopen(temp, "wb");
  	if (t == NULL) {
  	    fprintf(stderr, "Can't open temp file %s\n", temp);
  	    free(temp);
  	    return(-1);
  	}
      }
  #ifdef LIBXML_SCHEMAS_ENABLED
      if (rng != NULL) {
  	ret = xmlTextReaderRelaxNGValidate(reader, rng);
  	if (ret < 0) {
  	    testErrorHandler(NULL, "Relax-NG schema %s failed to compile\n",
  	                     rng);
  	    fclose(t);
              if (temp != NULL) {
                  unlink(temp);
                  free(temp);
              }
  	    return(0);
  	}
      }
  #endif
      xmlGetWarningsDefaultValue = 1;
      ret = xmlTextReaderRead(reader);
      while (ret == 1) {
  	if ((t != NULL) && (rng == NULL))
  	    processNode(t, reader);
          ret = xmlTextReaderRead(reader);
      }
      if (ret != 0) {
          testErrorHandler(NULL, "%s : failed to parse\n", filename);
      }
      if (rng != NULL) {
          if (xmlTextReaderIsValid(reader) != 1) {
  	    testErrorHandler(NULL, "%s fails to validate\n", filename);
  	} else {
  	    testErrorHandler(NULL, "%s validates\n", filename);
  	}
      }
      xmlGetWarningsDefaultValue = 0;
      if (t != NULL) {
          fclose(t);
  	ret = compareFiles(temp, result);
          if (temp != NULL) {
              unlink(temp);
              free(temp);
          }
  	if (ret) {
  	    fprintf(stderr, "Result for %s failed in %s\n", filename, result);
  	    return(-1);
  	}
      }
      if (err != NULL) {
  	ret = compareFileMem(err, testErrors, testErrorsSize);
  	if (ret != 0) {
  	    fprintf(stderr, "Error for %s failed\n", filename);
  	    printf("%s", testErrors);
  	    return(-1);
  	}
      }
  
      return(0);
  }
  
  /**
   * streamParseTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a file using the reader API and check for errors.
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  streamParseTest(const char *filename, const char *result, const char *err,
                  int options) {
      xmlTextReaderPtr reader;
      int ret;
  
      reader = xmlReaderForFile(filename, NULL, options);
      ret = streamProcessTest(filename, result, err, reader, NULL, options);
      xmlFreeTextReader(reader);
      return(ret);
  }
  
  /**
   * walkerParseTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a file using the walker, i.e. a reader built from a atree.
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  walkerParseTest(const char *filename, const char *result, const char *err,
                  int options) {
      xmlDocPtr doc;
      xmlTextReaderPtr reader;
      int ret;
  
      doc = xmlReadFile(filename, NULL, options);
      if (doc == NULL) {
          fprintf(stderr, "Failed to parse %s\n", filename);
  	return(-1);
      }
      reader = xmlReaderWalker(doc);
      ret = streamProcessTest(filename, result, err, reader, NULL, options);
      xmlFreeTextReader(reader);
      xmlFreeDoc(doc);
      return(ret);
  }
  
  /**
   * streamMemParseTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a file using the reader API from memory and check for errors.
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  streamMemParseTest(const char *filename, const char *result, const char *err,
                     int options) {
      xmlTextReaderPtr reader;
      int ret;
      const char *base;
      int size;
  
      /*
       * load and parse the memory
       */
      if (loadMem(filename, &base, &size) != 0) {
          fprintf(stderr, "Failed to load %s\n", filename);
  	return(-1);
      }
      reader = xmlReaderForMemory(base, size, filename, NULL, options);
      ret = streamProcessTest(filename, result, err, reader, NULL, options);
      free((char *)base);
      xmlFreeTextReader(reader);
      return(ret);
  }
  #endif
  
  #ifdef LIBXML_XPATH_ENABLED
  #ifdef LIBXML_DEBUG_ENABLED
  /************************************************************************
   *									*
   *		XPath and XPointer based tests				*
   *									*
   ************************************************************************/
  
  static FILE *xpathOutput;
  static xmlDocPtr xpathDocument;
  
  static void
  ignoreGenericError(void *ctx ATTRIBUTE_UNUSED,
          const char *msg ATTRIBUTE_UNUSED, ...) {
  }
  
  static void
  testXPath(const char *str, int xptr, int expr) {
      xmlGenericErrorFunc handler = ignoreGenericError;
      xmlXPathObjectPtr res;
      xmlXPathContextPtr ctxt;
  
      /* Don't print generic errors to stderr. */
      initGenericErrorDefaultFunc(&handler);
  
      nb_tests++;
  #if defined(LIBXML_XPTR_ENABLED)
      if (xptr) {
  	ctxt = xmlXPtrNewContext(xpathDocument, NULL, NULL);
  	res = xmlXPtrEval(BAD_CAST str, ctxt);
      } else {
  #endif
  	ctxt = xmlXPathNewContext(xpathDocument);
  	ctxt->node = xmlDocGetRootElement(xpathDocument);
  	if (expr)
  	    res = xmlXPathEvalExpression(BAD_CAST str, ctxt);
  	else {
  	    /* res = xmlXPathEval(BAD_CAST str, ctxt); */
  	    xmlXPathCompExprPtr comp;
  
  	    comp = xmlXPathCompile(BAD_CAST str);
  	    if (comp != NULL) {
  		res = xmlXPathCompiledEval(comp, ctxt);
  		xmlXPathFreeCompExpr(comp);
  	    } else
  		res = NULL;
  	}
  #if defined(LIBXML_XPTR_ENABLED)
      }
  #endif
      xmlXPathDebugDumpObject(xpathOutput, res, 0);
      xmlXPathFreeObject(res);
      xmlXPathFreeContext(ctxt);
  
      /* Reset generic error handler. */
      initGenericErrorDefaultFunc(NULL);
  }
  
  /**
   * xpathExprTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a file containing XPath standalone expressions and evaluate them
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  xpathCommonTest(const char *filename, const char *result,
                  int xptr, int expr) {
      FILE *input;
      char expression[5000];
      int len, ret = 0;
      char *temp;
  
      temp = resultFilename(filename, temp_directory, ".res");
      if (temp == NULL) {
          fprintf(stderr, "Out of memory\n");
          fatalError();
      }
      xpathOutput = fopen(temp, "wb");
      if (xpathOutput == NULL) {
  	fprintf(stderr, "failed to open output file %s\n", temp);
          free(temp);
  	return(-1);
      }
  
      input = fopen(filename, "rb");
      if (input == NULL) {
          xmlGenericError(xmlGenericErrorContext,
  		"Cannot open %s for reading\n", filename);
          free(temp);
  	return(-1);
      }
      while (fgets(expression, 4500, input) != NULL) {
  	len = strlen(expression);
  	len--;
  	while ((len >= 0) &&
  	       ((expression[len] == '\n') || (expression[len] == '\t') ||
  		(expression[len] == '\r') || (expression[len] == ' '))) len--;
  	expression[len + 1] = 0;
  	if (len >= 0) {
  	    fprintf(xpathOutput,
  	            "\n========================\nExpression: %s\n",
  		    expression) ;
  	    testXPath(expression, xptr, expr);
  	}
      }
  
      fclose(input);
      fclose(xpathOutput);
      if (result != NULL) {
  	ret = compareFiles(temp, result);
  	if (ret) {
  	    fprintf(stderr, "Result for %s failed in %s\n", filename, result);
  	}
      }
  
      if (temp != NULL) {
          unlink(temp);
          free(temp);
      }
      return(ret);
  }
  
  /**
   * xpathExprTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a file containing XPath standalone expressions and evaluate them
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  xpathExprTest(const char *filename, const char *result,
                const char *err ATTRIBUTE_UNUSED,
                int options ATTRIBUTE_UNUSED) {
      return(xpathCommonTest(filename, result, 0, 1));
  }
  
  /**
   * xpathDocTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a file containing XPath expressions and evaluate them against
   * a set of corresponding documents.
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  xpathDocTest(const char *filename,
               const char *resul ATTRIBUTE_UNUSED,
               const char *err ATTRIBUTE_UNUSED,
               int options) {
  
      char pattern[500];
      char result[500];
      glob_t globbuf;
      size_t i;
      int ret = 0, res;
  
      xpathDocument = xmlReadFile(filename, NULL,
                                  options | XML_PARSE_DTDATTR | XML_PARSE_NOENT);
      if (xpathDocument == NULL) {
          fprintf(stderr, "Failed to load %s\n", filename);
  	return(-1);
      }
  
      res = snprintf(pattern, 499, "./test/XPath/tests/%s*",
              baseFilename(filename));
      if (res >= 499)
          pattern[499] = 0;
      globbuf.gl_offs = 0;
      glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
      for (i = 0;i < globbuf.gl_pathc;i++) {
          res = snprintf(result, 499, "result/XPath/tests/%s",
  	         baseFilename(globbuf.gl_pathv[i]));
          if (res >= 499)
              result[499] = 0;
  	res = xpathCommonTest(globbuf.gl_pathv[i], &result[0], 0, 0);
  	if (res != 0)
  	    ret = res;
      }
      globfree(&globbuf);
  
      xmlFreeDoc(xpathDocument);
      return(ret);
  }
  
  #ifdef LIBXML_XPTR_ENABLED
  /**
   * xptrDocTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a file containing XPath expressions and evaluate them against
   * a set of corresponding documents.
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  xptrDocTest(const char *filename,
              const char *resul ATTRIBUTE_UNUSED,
              const char *err ATTRIBUTE_UNUSED,
              int options) {
  
      char pattern[500];
      char result[500];
      glob_t globbuf;
      size_t i;
      int ret = 0, res;
  
      xpathDocument = xmlReadFile(filename, NULL,
                                  options | XML_PARSE_DTDATTR | XML_PARSE_NOENT);
      if (xpathDocument == NULL) {
          fprintf(stderr, "Failed to load %s\n", filename);
  	return(-1);
      }
  
      res = snprintf(pattern, 499, "./test/XPath/xptr/%s*",
              baseFilename(filename));
      if (res >= 499)
          pattern[499] = 0;
      globbuf.gl_offs = 0;
      glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
      for (i = 0;i < globbuf.gl_pathc;i++) {
          res = snprintf(result, 499, "result/XPath/xptr/%s",
  	         baseFilename(globbuf.gl_pathv[i]));
          if (res >= 499)
              result[499] = 0;
  	res = xpathCommonTest(globbuf.gl_pathv[i], &result[0], 1, 0);
  	if (res != 0)
  	    ret = res;
      }
      globfree(&globbuf);
  
      xmlFreeDoc(xpathDocument);
      return(ret);
  }
  #endif /* LIBXML_XPTR_ENABLED */
  
  /**
   * xmlidDocTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a file containing xml:id and check for errors and verify
   * that XPath queries will work on them as expected.
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  xmlidDocTest(const char *filename,
               const char *result,
               const char *err,
               int options) {
  
      int res = 0;
      int ret = 0;
      char *temp;
  
      xpathDocument = xmlReadFile(filename, NULL,
                                  options | XML_PARSE_DTDATTR | XML_PARSE_NOENT);
      if (xpathDocument == NULL) {
          fprintf(stderr, "Failed to load %s\n", filename);
  	return(-1);
      }
  
      temp = resultFilename(filename, temp_directory, ".res");
      if (temp == NULL) {
          fprintf(stderr, "Out of memory\n");
          fatalError();
      }
      xpathOutput = fopen(temp, "wb");
      if (xpathOutput == NULL) {
  	fprintf(stderr, "failed to open output file %s\n", temp);
          xmlFreeDoc(xpathDocument);
          free(temp);
  	return(-1);
      }
  
      testXPath("id('bar')", 0, 0);
  
      fclose(xpathOutput);
      if (result != NULL) {
  	ret = compareFiles(temp, result);
  	if (ret) {
  	    fprintf(stderr, "Result for %s failed in %s\n", filename, result);
  	    res = 1;
  	}
      }
  
      if (temp != NULL) {
          unlink(temp);
          free(temp);
      }
      xmlFreeDoc(xpathDocument);
  
      if (err != NULL) {
  	ret = compareFileMem(err, testErrors, testErrorsSize);
  	if (ret != 0) {
  	    fprintf(stderr, "Error for %s failed\n", filename);
  	    res = 1;
  	}
      }
      return(res);
  }
  
  #endif /* LIBXML_DEBUG_ENABLED */
  #endif /* XPATH */
  /************************************************************************
   *									*
   *			URI based tests					*
   *									*
   ************************************************************************/
  
  static void
  handleURI(const char *str, const char *base, FILE *o) {
      int ret;
      xmlURIPtr uri;
      xmlChar *res = NULL;
  
      uri = xmlCreateURI();
  
      if (base == NULL) {
  	ret = xmlParseURIReference(uri, str);
  	if (ret != 0)
  	    fprintf(o, "%s : error %d\n", str, ret);
  	else {
  	    xmlNormalizeURIPath(uri->path);
  	    xmlPrintURI(o, uri);
  	    fprintf(o, "\n");
  	}
      } else {
  	res = xmlBuildURI((xmlChar *)str, (xmlChar *) base);
  	if (res != NULL) {
  	    fprintf(o, "%s\n", (char *) res);
  	}
  	else
  	    fprintf(o, "::ERROR::\n");
      }
      if (res != NULL)
  	xmlFree(res);
      xmlFreeURI(uri);
  }
  
  /**
   * uriCommonTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a file containing URI and check for errors
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  uriCommonTest(const char *filename,
               const char *result,
               const char *err,
               const char *base) {
      char *temp;
      FILE *o, *f;
      char str[1024];
      int res = 0, i, ret;
  
      temp = resultFilename(filename, temp_directory, ".res");
      if (temp == NULL) {
          fprintf(stderr, "Out of memory\n");
          fatalError();
      }
      o = fopen(temp, "wb");
      if (o == NULL) {
  	fprintf(stderr, "failed to open output file %s\n", temp);
          free(temp);
  	return(-1);
      }
      f = fopen(filename, "rb");
      if (f == NULL) {
  	fprintf(stderr, "failed to open input file %s\n", filename);
  	fclose(o);
          if (temp != NULL) {
              unlink(temp);
              free(temp);
          }
  	return(-1);
      }
  
      while (1) {
  	/*
  	 * read one line in string buffer.
  	 */
  	if (fgets (&str[0], sizeof (str) - 1, f) == NULL)
  	   break;
  
  	/*
  	 * remove the ending spaces
  	 */
  	i = strlen(str);
  	while ((i > 0) &&
  	       ((str[i - 1] == '\n') || (str[i - 1] == '\r') ||
  		(str[i - 1] == ' ') || (str[i - 1] == '\t'))) {
  	    i--;
  	    str[i] = 0;
  	}
  	nb_tests++;
  	handleURI(str, base, o);
      }
  
      fclose(f);
      fclose(o);
  
      if (result != NULL) {
  	ret = compareFiles(temp, result);
  	if (ret) {
  	    fprintf(stderr, "Result for %s failed in %s\n", filename, result);
  	    res = 1;
  	}
      }
      if (err != NULL) {
  	ret = compareFileMem(err, testErrors, testErrorsSize);
  	if (ret != 0) {
  	    fprintf(stderr, "Error for %s failed\n", filename);
  	    res = 1;
  	}
      }
  
      if (temp != NULL) {
          unlink(temp);
          free(temp);
      }
      return(res);
  }
  
  /**
   * uriParseTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a file containing URI and check for errors
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  uriParseTest(const char *filename,
               const char *result,
               const char *err,
               int options ATTRIBUTE_UNUSED) {
      return(uriCommonTest(filename, result, err, NULL));
  }
  
  /**
   * uriBaseTest:
   * @filename: the file to parse
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a file containing URI, compose them against a fixed base and
   * check for errors
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  uriBaseTest(const char *filename,
               const char *result,
               const char *err,
               int options ATTRIBUTE_UNUSED) {
      return(uriCommonTest(filename, result, err,
                           "http://foo.com/path/to/index.html?orig#help"));
  }
  
  static int urip_success = 1;
  static int urip_current = 0;
  static const char *urip_testURLs[] = {
      "urip://example.com/a b.html",
      "urip://example.com/a%20b.html",
      "file:///path/to/a b.html",
      "file:///path/to/a%20b.html",
      "/path/to/a b.html",
      "/path/to/a%20b.html",
      "urip://example.com/r" "\xe9" "sum" "\xe9" ".html",
      "urip://example.com/test?a=1&b=2%263&c=4#foo",
      NULL
  };
  static const char *urip_rcvsURLs[] = {
      /* it is an URI the strings must be escaped */
      "urip://example.com/a%20b.html",
      /* check that % escaping is not broken */
      "urip://example.com/a%20b.html",
      /* it's an URI path the strings must be escaped */
      "file:///path/to/a%20b.html",
      /* check that % escaping is not broken */
      "file:///path/to/a%20b.html",
      /* this is not an URI, this is a path, so this should not be escaped */
      "/path/to/a b.html",
      /* check that paths with % are not broken */
      "/path/to/a%20b.html",
      /* out of context the encoding can't be guessed byte by byte conversion */
      "urip://example.com/r%E9sum%E9.html",
      /* verify we don't destroy URIs especially the query part */
      "urip://example.com/test?a=1&b=2%263&c=4#foo",
      NULL
  };
  static const char *urip_res = "<list/>";
  static const char *urip_cur = NULL;
  static int urip_rlen;
  
  /**
   * uripMatch:
   * @URI: an URI to test
   *
   * Check for an urip: query
   *
   * Returns 1 if yes and 0 if another Input module should be used
   */
  static int
  uripMatch(const char * URI) {
      if ((URI == NULL) || (!strcmp(URI, "file:///etc/xml/catalog")))
          return(0);
      /* Verify we received the escaped URL */
      if (strcmp(urip_rcvsURLs[urip_current], URI))
  	urip_success = 0;
      return(1);
  }
  
  /**
   * uripOpen:
   * @URI: an URI to test
   *
   * Return a pointer to the urip: query handler, in this example simply
   * the urip_current pointer...
   *
   * Returns an Input context or NULL in case or error
   */
  static void *
  uripOpen(const char * URI) {
      if ((URI == NULL) || (!strcmp(URI, "file:///etc/xml/catalog")))
          return(NULL);
      /* Verify we received the escaped URL */
      if (strcmp(urip_rcvsURLs[urip_current], URI))
  	urip_success = 0;
      urip_cur = urip_res;
      urip_rlen = strlen(urip_res);
      return((void *) urip_cur);
  }
  
  /**
   * uripClose:
   * @context: the read context
   *
   * Close the urip: query handler
   *
   * Returns 0 or -1 in case of error
   */
  static int
  uripClose(void * context) {
      if (context == NULL) return(-1);
      urip_cur = NULL;
      urip_rlen = 0;
      return(0);
  }
  
  /**
   * uripRead:
   * @context: the read context
   * @buffer: where to store data
   * @len: number of bytes to read
   *
   * Implement an urip: query read.
   *
   * Returns the number of bytes read or -1 in case of error
   */
  static int
  uripRead(void * context, char * buffer, int len) {
     const char *ptr = (const char *) context;
  
     if ((context == NULL) || (buffer == NULL) || (len < 0))
         return(-1);
  
     if (len > urip_rlen) len = urip_rlen;
     memcpy(buffer, ptr, len);
     urip_rlen -= len;
     return(len);
  }
  
  static int
  urip_checkURL(const char *URL) {
      xmlDocPtr doc;
  
      doc = xmlReadFile(URL, NULL, 0);
      if (doc == NULL)
          return(-1);
      xmlFreeDoc(doc);
      return(1);
  }
  
  /**
   * uriPathTest:
   * @filename: ignored
   * @result: ignored
   * @err: ignored
   *
   * Run a set of tests to check how Path and URI are handled before
   * being passed to the I/O layer
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  uriPathTest(const char *filename ATTRIBUTE_UNUSED,
               const char *result ATTRIBUTE_UNUSED,
               const char *err ATTRIBUTE_UNUSED,
               int options ATTRIBUTE_UNUSED) {
      int parsed;
      int failures = 0;
  
      /*
       * register the new I/O handlers
       */
      if (xmlRegisterInputCallbacks(uripMatch, uripOpen, uripRead, uripClose) < 0)
      {
          fprintf(stderr, "failed to register HTTP handler\n");
  	return(-1);
      }
  
      for (urip_current = 0;urip_testURLs[urip_current] != NULL;urip_current++) {
          urip_success = 1;
          parsed = urip_checkURL(urip_testURLs[urip_current]);
  	if (urip_success != 1) {
  	    fprintf(stderr, "failed the URL passing test for %s",
  	            urip_testURLs[urip_current]);
  	    failures++;
  	} else if (parsed != 1) {
  	    fprintf(stderr, "failed the parsing test for %s",
  	            urip_testURLs[urip_current]);
  	    failures++;
  	}
  	nb_tests++;
      }
  
      xmlPopInputCallbacks();
      return(failures);
  }
  
  #ifdef LIBXML_SCHEMAS_ENABLED
  /************************************************************************
   *									*
   *			Schemas tests					*
   *									*
   ************************************************************************/
  static int
  schemasOneTest(const char *sch,
                 const char *filename,
                 const char *result,
  	       const char *err,
  	       int options,
  	       xmlSchemaPtr schemas) {
      xmlDocPtr doc;
      xmlSchemaValidCtxtPtr ctxt;
      int ret = 0;
      int validResult = 0;
      char *temp;
      FILE *schemasOutput;
  
      doc = xmlReadFile(filename, NULL, options);
      if (doc == NULL) {
          fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch);
  	return(-1);
      }
  
      temp = resultFilename(result, temp_directory, ".res");
      if (temp == NULL) {
          fprintf(stderr, "Out of memory\n");
          fatalError();
      }
      schemasOutput = fopen(temp, "wb");
      if (schemasOutput == NULL) {
  	fprintf(stderr, "failed to open output file %s\n", temp);
  	xmlFreeDoc(doc);
          free(temp);
  	return(-1);
      }
  
      ctxt = xmlSchemaNewValidCtxt(schemas);
      xmlSchemaSetValidErrors(ctxt, testErrorHandler, testErrorHandler, ctxt);
      validResult = xmlSchemaValidateDoc(ctxt, doc);
      if (validResult == 0) {
  	fprintf(schemasOutput, "%s validates\n", filename);
      } else if (validResult > 0) {
  	fprintf(schemasOutput, "%s fails to validate\n", filename);
      } else {
  	fprintf(schemasOutput, "%s validation generated an internal error\n",
  	       filename);
      }
      fclose(schemasOutput);
      if (result) {
  	if (compareFiles(temp, result)) {
  	    fprintf(stderr, "Result for %s on %s failed\n", filename, sch);
  	    ret = 1;
  	}
      }
      if (temp != NULL) {
          unlink(temp);
          free(temp);
      }
  
      if ((validResult != 0) && (err != NULL)) {
  	if (compareFileMem(err, testErrors, testErrorsSize)) {
  	    fprintf(stderr, "Error for %s on %s failed\n", filename, sch);
  	    ret = 1;
  	}
      }
  
      xmlSchemaFreeValidCtxt(ctxt);
      xmlFreeDoc(doc);
      return(ret);
  }
  /**
   * schemasTest:
   * @filename: the schemas file
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a file containing URI, compose them against a fixed base and
   * check for errors
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  schemasTest(const char *filename,
              const char *resul ATTRIBUTE_UNUSED,
              const char *errr ATTRIBUTE_UNUSED,
              int options) {
      const char *base = baseFilename(filename);
      const char *base2;
      const char *instance;
      xmlSchemaParserCtxtPtr ctxt;
      xmlSchemaPtr schemas;
      int res = 0, len, ret;
      char pattern[500];
      char prefix[500];
      char result[500];
      char err[500];
      glob_t globbuf;
      size_t i;
      char count = 0;
  
      /* first compile the schemas if possible */
      ctxt = xmlSchemaNewParserCtxt(filename);
      xmlSchemaSetParserErrors(ctxt, testErrorHandler, testErrorHandler, ctxt);
      schemas = xmlSchemaParse(ctxt);
      xmlSchemaFreeParserCtxt(ctxt);
  
      /*
       * most of the mess is about the output filenames generated by the Makefile
       */
      len = strlen(base);
      if ((len > 499) || (len < 5)) {
          xmlSchemaFree(schemas);
  	return(-1);
      }
      len -= 4; /* remove trailing .xsd */
      if (base[len - 2] == '_') {
          len -= 2; /* remove subtest number */
      }
      if (base[len - 2] == '_') {
          len -= 2; /* remove subtest number */
      }
      memcpy(prefix, base, len);
      prefix[len] = 0;
  
      if (snprintf(pattern, 499, "./test/schemas/%s_?.xml", prefix) >= 499)
          pattern[499] = 0;
  
      if (base[len] == '_') {
          len += 2;
  	memcpy(prefix, base, len);
  	prefix[len] = 0;
      }
  
      globbuf.gl_offs = 0;
      glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
      for (i = 0;i < globbuf.gl_pathc;i++) {
          testErrorsSize = 0;
  	testErrors[0] = 0;
          instance = globbuf.gl_pathv[i];
  	base2 = baseFilename(instance);
  	len = strlen(base2);
  	if ((len > 6) && (base2[len - 6] == '_')) {
  	    count = base2[len - 5];
  	    ret = snprintf(result, 499, "result/schemas/%s_%c",
  		     prefix, count);
              if (ret >= 499)
  	        result[499] = 0;
  	    ret = snprintf(err, 499, "result/schemas/%s_%c.err",
  		     prefix, count);
              if (ret >= 499)
  	        err[499] = 0;
  	} else {
  	    fprintf(stderr, "don't know how to process %s\n", instance);
  	    continue;
  	}
  	if (schemas == NULL) {
  	} else {
  	    nb_tests++;
  	    ret = schemasOneTest(filename, instance, result, err,
  	                         options, schemas);
  	    if (ret != 0)
  		res = ret;
  	}
      }
      globfree(&globbuf);
      xmlSchemaFree(schemas);
  
      return(res);
  }
  
  /************************************************************************
   *									*
   *			Schemas tests					*
   *									*
   ************************************************************************/
  static int
  rngOneTest(const char *sch,
                 const char *filename,
                 const char *result,
  	       const char *err,
  	       int options,
  	       xmlRelaxNGPtr schemas) {
      xmlDocPtr doc;
      xmlRelaxNGValidCtxtPtr ctxt;
      int ret = 0;
      char *temp;
      FILE *schemasOutput;
  
      doc = xmlReadFile(filename, NULL, options);
      if (doc == NULL) {
          fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch);
  	return(-1);
      }
  
      temp = resultFilename(result, temp_directory, ".res");
      if (temp == NULL) {
          fprintf(stderr, "Out of memory\n");
          fatalError();
      }
      schemasOutput = fopen(temp, "wb");
      if (schemasOutput == NULL) {
  	fprintf(stderr, "failed to open output file %s\n", temp);
  	xmlFreeDoc(doc);
          free(temp);
  	return(-1);
      }
  
      ctxt = xmlRelaxNGNewValidCtxt(schemas);
      xmlRelaxNGSetValidErrors(ctxt, testErrorHandler, testErrorHandler, ctxt);
      ret = xmlRelaxNGValidateDoc(ctxt, doc);
      if (ret == 0) {
  	testErrorHandler(NULL, "%s validates\n", filename);
      } else if (ret > 0) {
  	testErrorHandler(NULL, "%s fails to validate\n", filename);
      } else {
  	testErrorHandler(NULL, "%s validation generated an internal error\n",
  	       filename);
      }
      fclose(schemasOutput);
      ret = 0;
      if (result) {
  	if (compareFiles(temp, result)) {
  	    fprintf(stderr, "Result for %s on %s failed\n", filename, sch);
  	    ret = 1;
  	}
      }
      if (temp != NULL) {
          unlink(temp);
          free(temp);
      }
  
      if (err != NULL) {
  	if (compareFileMem(err, testErrors, testErrorsSize)) {
  	    fprintf(stderr, "Error for %s on %s failed\n", filename, sch);
  	    ret = 1;
  	    printf("%s", testErrors);
  	}
      }
  
  
      xmlRelaxNGFreeValidCtxt(ctxt);
      xmlFreeDoc(doc);
      return(ret);
  }
  /**
   * rngTest:
   * @filename: the schemas file
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse an RNG schemas and then apply it to the related .xml
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  rngTest(const char *filename,
              const char *resul ATTRIBUTE_UNUSED,
              const char *errr ATTRIBUTE_UNUSED,
              int options) {
      const char *base = baseFilename(filename);
      const char *base2;
      const char *instance;
      xmlRelaxNGParserCtxtPtr ctxt;
      xmlRelaxNGPtr schemas;
      int res = 0, len, ret = 0;
      char pattern[500];
      char prefix[500];
      char result[500];
      char err[500];
      glob_t globbuf;
      size_t i;
      char count = 0;
  
      /* first compile the schemas if possible */
      ctxt = xmlRelaxNGNewParserCtxt(filename);
      xmlRelaxNGSetParserErrors(ctxt, testErrorHandler, testErrorHandler, ctxt);
      schemas = xmlRelaxNGParse(ctxt);
      xmlRelaxNGFreeParserCtxt(ctxt);
  
      /*
       * most of the mess is about the output filenames generated by the Makefile
       */
      len = strlen(base);
      if ((len > 499) || (len < 5)) {
          xmlRelaxNGFree(schemas);
  	return(-1);
      }
      len -= 4; /* remove trailing .rng */
      memcpy(prefix, base, len);
      prefix[len] = 0;
  
      if (snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix) >= 499)
          pattern[499] = 0;
  
      globbuf.gl_offs = 0;
      glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
      for (i = 0;i < globbuf.gl_pathc;i++) {
          testErrorsSize = 0;
  	testErrors[0] = 0;
          instance = globbuf.gl_pathv[i];
  	base2 = baseFilename(instance);
  	len = strlen(base2);
  	if ((len > 6) && (base2[len - 6] == '_')) {
  	    count = base2[len - 5];
  	    res = snprintf(result, 499, "result/relaxng/%s_%c",
  		     prefix, count);
              if (res >= 499)
  	        result[499] = 0;
  	    res = snprintf(err, 499, "result/relaxng/%s_%c.err",
  		     prefix, count);
              if (res >= 499)
  	        err[499] = 0;
  	} else {
  	    fprintf(stderr, "don't know how to process %s\n", instance);
  	    continue;
  	}
  	if (schemas == NULL) {
  	} else {
  	    nb_tests++;
  	    res = rngOneTest(filename, instance, result, err,
  	                         options, schemas);
  	    if (res != 0)
  		ret = res;
  	}
      }
      globfree(&globbuf);
      xmlRelaxNGFree(schemas);
  
      return(ret);
  }
  
  #ifdef LIBXML_READER_ENABLED
  /**
   * rngStreamTest:
   * @filename: the schemas file
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a set of files with streaming, applying an RNG schemas
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  rngStreamTest(const char *filename,
              const char *resul ATTRIBUTE_UNUSED,
              const char *errr ATTRIBUTE_UNUSED,
              int options) {
      const char *base = baseFilename(filename);
      const char *base2;
      const char *instance;
      int res = 0, len, ret;
      char pattern[500];
      char prefix[500];
      char result[500];
      char err[500];
      glob_t globbuf;
      size_t i;
      char count = 0;
      xmlTextReaderPtr reader;
      int disable_err = 0;
  
      /*
       * most of the mess is about the output filenames generated by the Makefile
       */
      len = strlen(base);
      if ((len > 499) || (len < 5)) {
  	fprintf(stderr, "len(base) == %d !\n", len);
  	return(-1);
      }
      len -= 4; /* remove trailing .rng */
      memcpy(prefix, base, len);
      prefix[len] = 0;
  
      /*
       * strictly unifying the error messages is nearly impossible this
       * hack is also done in the Makefile
       */
      if ((!strcmp(prefix, "tutor10_1")) || (!strcmp(prefix, "tutor10_2")) ||
          (!strcmp(prefix, "tutor3_2")) || (!strcmp(prefix, "307377")) ||
          (!strcmp(prefix, "tutor8_2")))
  	disable_err = 1;
  
      if (snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix) >= 499)
          pattern[499] = 0;
  
      globbuf.gl_offs = 0;
      glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
      for (i = 0;i < globbuf.gl_pathc;i++) {
          testErrorsSize = 0;
  	testErrors[0] = 0;
          instance = globbuf.gl_pathv[i];
  	base2 = baseFilename(instance);
  	len = strlen(base2);
  	if ((len > 6) && (base2[len - 6] == '_')) {
  	    count = base2[len - 5];
  	    ret = snprintf(result, 499, "result/relaxng/%s_%c",
  		     prefix, count);
              if (ret >= 499)
  	        result[499] = 0;
  	    ret = snprintf(err, 499, "result/relaxng/%s_%c.err",
  		     prefix, count);
              if (ret >= 499)
  	        err[499] = 0;
  	} else {
  	    fprintf(stderr, "don't know how to process %s\n", instance);
  	    continue;
  	}
  	reader = xmlReaderForFile(instance, NULL, options);
  	if (reader == NULL) {
  	    fprintf(stderr, "Failed to build reader for %s\n", instance);
  	}
  	if (disable_err == 1)
  	    ret = streamProcessTest(instance, result, NULL, reader, filename,
  	                            options);
  	else
  	    ret = streamProcessTest(instance, result, err, reader, filename,
  	                            options);
  	xmlFreeTextReader(reader);
  	if (ret != 0) {
  	    fprintf(stderr, "instance %s failed\n", instance);
  	    res = ret;
  	}
      }
      globfree(&globbuf);
  
      return(res);
  }
  #endif /* READER */
  
  #endif
  
  #ifdef LIBXML_PATTERN_ENABLED
  #ifdef LIBXML_READER_ENABLED
  /************************************************************************
   *									*
   *			Patterns tests					*
   *									*
   ************************************************************************/
  static void patternNode(FILE *out, xmlTextReaderPtr reader,
                          const char *pattern, xmlPatternPtr patternc,
  			xmlStreamCtxtPtr patstream) {
      xmlChar *path = NULL;
      int match = -1;
      int type, empty;
  
      type = xmlTextReaderNodeType(reader);
      empty = xmlTextReaderIsEmptyElement(reader);
  
      if (type == XML_READER_TYPE_ELEMENT) {
  	/* do the check only on element start */
  	match = xmlPatternMatch(patternc, xmlTextReaderCurrentNode(reader));
  
  	if (match) {
  	    path = xmlGetNodePath(xmlTextReaderCurrentNode(reader));
  	    fprintf(out, "Node %s matches pattern %s\n", path, pattern);
  	}
      }
      if (patstream != NULL) {
  	int ret;
  
  	if (type == XML_READER_TYPE_ELEMENT) {
  	    ret = xmlStreamPush(patstream,
  				xmlTextReaderConstLocalName(reader),
  				xmlTextReaderConstNamespaceUri(reader));
  	    if (ret < 0) {
  		fprintf(out, "xmlStreamPush() failure\n");
  		xmlFreeStreamCtxt(patstream);
  		patstream = NULL;
  	    } else if (ret != match) {
  		if (path == NULL) {
  		    path = xmlGetNodePath(
  				   xmlTextReaderCurrentNode(reader));
  		}
  		fprintf(out,
  			"xmlPatternMatch and xmlStreamPush disagree\n");
  		fprintf(out,
  			"  pattern %s node %s\n",
  			pattern, path);
  	    }
  
  
  	}
  	if ((type == XML_READER_TYPE_END_ELEMENT) ||
  	    ((type == XML_READER_TYPE_ELEMENT) && (empty))) {
  	    ret = xmlStreamPop(patstream);
  	    if (ret < 0) {
  		fprintf(out, "xmlStreamPop() failure\n");
  		xmlFreeStreamCtxt(patstream);
  		patstream = NULL;
  	    }
  	}
      }
      if (path != NULL)
  	xmlFree(path);
  }
  
  /**
   * patternTest:
   * @filename: the schemas file
   * @result: the file with expected result
   * @err: the file with error messages
   *
   * Parse a set of files with streaming, applying an RNG schemas
   *
   * Returns 0 in case of success, an error code otherwise
   */
  static int
  patternTest(const char *filename,
              const char *resul ATTRIBUTE_UNUSED,
              const char *err ATTRIBUTE_UNUSED,
              int options) {
      xmlPatternPtr patternc = NULL;
      xmlStreamCtxtPtr patstream = NULL;
      FILE *o, *f;
      char str[1024];
      char xml[500];
      char result[500];
      int len, i;
      int ret = 0, res;
      char *temp;
      xmlTextReaderPtr reader;
      xmlDocPtr doc;
  
      len = strlen(filename);
      len -= 4;
      memcpy(xml, filename, len);
      xml[len] = 0;
      if (snprintf(result, 499, "result/pattern/%s", baseFilename(xml)) >= 499)
          result[499] = 0;
      memcpy(xml + len, ".xml", 5);
  
      if (!checkTestFile(xml) && !update_results) {
  	fprintf(stderr, "Missing xml file %s\n", xml);
  	return(-1);
      }
      if (!checkTestFile(result) && !update_results) {
  	fprintf(stderr, "Missing result file %s\n", result);
  	return(-1);
      }
      f = fopen(filename, "rb");
      if (f == NULL) {
          fprintf(stderr, "Failed to open %s\n", filename);
  	return(-1);
      }
      temp = resultFilename(filename, temp_directory, ".res");
      if (temp == NULL) {
          fprintf(stderr, "Out of memory\n");
          fatalError();
      }
      o = fopen(temp, "wb");
      if (o == NULL) {
  	fprintf(stderr, "failed to open output file %s\n", temp);
  	fclose(f);
          free(temp);
  	return(-1);
      }
      while (1) {
  	/*
  	 * read one line in string buffer.
  	 */
  	if (fgets (&str[0], sizeof (str) - 1, f) == NULL)
  	   break;
  
  	/*
  	 * remove the ending spaces
  	 */
  	i = strlen(str);
  	while ((i > 0) &&
  	       ((str[i - 1] == '\n') || (str[i - 1] == '\r') ||
  		(str[i - 1] == ' ') || (str[i - 1] == '\t'))) {
  	    i--;
  	    str[i] = 0;
  	}
  	doc = xmlReadFile(xml, NULL, options);
  	if (doc == NULL) {
  	    fprintf(stderr, "Failed to parse %s\n", xml);
  	    ret = 1;
  	} else {
  	    xmlNodePtr root;
  	    const xmlChar *namespaces[22];
  	    int j;
  	    xmlNsPtr ns;
  
  	    root = xmlDocGetRootElement(doc);
  	    for (ns = root->nsDef, j = 0;ns != NULL && j < 20;ns=ns->next) {
  		namespaces[j++] = ns->href;
  		namespaces[j++] = ns->prefix;
  	    }
  	    namespaces[j++] = NULL;
  	    namespaces[j] = NULL;
  
  	    patternc = xmlPatterncompile((const xmlChar *) str, doc->dict,
  					 0, &namespaces[0]);
  	    if (patternc == NULL) {
  		testErrorHandler(NULL,
  			"Pattern %s failed to compile\n", str);
  		xmlFreeDoc(doc);
  		ret = 1;
  		continue;
  	    }
  	    patstream = xmlPatternGetStreamCtxt(patternc);
  	    if (patstream != NULL) {
  		ret = xmlStreamPush(patstream, NULL, NULL);
  		if (ret < 0) {
  		    fprintf(stderr, "xmlStreamPush() failure\n");
  		    xmlFreeStreamCtxt(patstream);
  		    patstream = NULL;
  		}
  	    }
  	    nb_tests++;
  
  	    reader = xmlReaderWalker(doc);
  	    res = xmlTextReaderRead(reader);
  	    while (res == 1) {
  		patternNode(o, reader, str, patternc, patstream);
  		res = xmlTextReaderRead(reader);
  	    }
  	    if (res != 0) {
  		fprintf(o, "%s : failed to parse\n", filename);
  	    }
  	    xmlFreeTextReader(reader);
  	    xmlFreeDoc(doc);
  	    xmlFreeStreamCtxt(patstream);
  	    patstream = NULL;
  	    xmlFreePattern(patternc);
  
  	}
      }
  
      fclose(f);
      fclose(o);
  
      ret = compareFiles(temp, result);
      if (ret) {
  	fprintf(stderr, "Result for %s failed in %s\n", filename, result);
  	ret = 1;
      }
      if (temp != NULL) {
          unlink(temp);
          free(temp);
      }
      return(ret);
  }
  #endif /* READER */
  #endif /* PATTERN */
  #ifdef LIBXML_C14N_ENABLED
  /************************************************************************
   *									*
   *			Canonicalization tests				*
   *									*
   ************************************************************************/
  static xmlXPathObjectPtr
  load_xpath_expr (xmlDocPtr parent_doc, const char* filename) {
      xmlXPathObjectPtr xpath;
      xmlDocPtr doc;
      xmlChar *expr;
      xmlXPathContextPtr ctx;
      xmlNodePtr node;
      xmlNsPtr ns;
  
      /*
       * load XPath expr as a file
       */
      xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
      xmlSubstituteEntitiesDefault(1);
  
      doc = xmlReadFile(filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT);
      if (doc == NULL) {
  	fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename);
  	return(NULL);
      }
  
      /*
       * Check the document is of the right kind
       */
      if(xmlDocGetRootElement(doc) == NULL) {
          fprintf(stderr,"Error: empty document for file \"%s\"\n", filename);
  	xmlFreeDoc(doc);
  	return(NULL);
      }
  
      node = doc->children;
      while(node != NULL && !xmlStrEqual(node->name, (const xmlChar *)"XPath")) {
  	node = node->next;
      }
  
      if(node == NULL) {
          fprintf(stderr,"Error: XPath element expected in the file  \"%s\"\n", filename);
  	xmlFreeDoc(doc);
  	return(NULL);
      }
  
      expr = xmlNodeGetContent(node);
      if(expr == NULL) {
          fprintf(stderr,"Error: XPath content element is NULL \"%s\"\n", filename);
  	xmlFreeDoc(doc);
  	return(NULL);
      }
  
      ctx = xmlXPathNewContext(parent_doc);
      if(ctx == NULL) {
          fprintf(stderr,"Error: unable to create new context\n");
          xmlFree(expr);
          xmlFreeDoc(doc);
          return(NULL);
      }
  
      /*
       * Register namespaces
       */
      ns = node->nsDef;
      while(ns != NULL) {
  	if(xmlXPathRegisterNs(ctx, ns->prefix, ns->href) != 0) {
  	    fprintf(stderr,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", ns->prefix, ns->href);
      xmlFree(expr);
  	    xmlXPathFreeContext(ctx);
  	    xmlFreeDoc(doc);
  	    return(NULL);
  	}
  	ns = ns->next;
      }
  
      /*
       * Evaluate xpath
       */
      xpath = xmlXPathEvalExpression(expr, ctx);
      if(xpath == NULL) {
          fprintf(stderr,"Error: unable to evaluate xpath expression\n");
  xmlFree(expr);
          xmlXPathFreeContext(ctx);
          xmlFreeDoc(doc);
          return(NULL);
      }
  
      /* print_xpath_nodes(xpath->nodesetval); */
  
      xmlFree(expr);
      xmlXPathFreeContext(ctx);
      xmlFreeDoc(doc);
      return(xpath);
  }
  
  /*
   * Macro used to grow the current buffer.
   */
  #define xxx_growBufferReentrant() {						\
      buffer_size *= 2;							\
      buffer = (xmlChar **)						\
  	xmlRealloc(buffer, buffer_size * sizeof(xmlChar*));	\
      if (buffer == NULL) {						\
  	perror("realloc failed");					\
  	return(NULL);							\
      }									\
  }
  
  static xmlChar **
  parse_list(xmlChar *str) {
      xmlChar **buffer;
      xmlChar **out = NULL;
      int buffer_size = 0;
      int len;
  
      if(str == NULL) {
  	return(NULL);
      }
  
      len = xmlStrlen(str);
      if((str[0] == '\'') && (str[len - 1] == '\'')) {
  	str[len - 1] = '\0';
  	str++;
      }
      /*
       * allocate an translation buffer.
       */
      buffer_size = 1000;
      buffer = (xmlChar **) xmlMalloc(buffer_size * sizeof(xmlChar*));
      if (buffer == NULL) {
  	perror("malloc failed");
  	return(NULL);
      }
      out = buffer;
  
      while(*str != '\0') {
  	if (out - buffer > buffer_size - 10) {
  	    int indx = out - buffer;
  
  	    xxx_growBufferReentrant();
  	    out = &buffer[indx];
  	}
  	(*out++) = str;
  	while(*str != ',' && *str != '\0') ++str;
  	if(*str == ',') *(str++) = '\0';
      }
      (*out) = NULL;
      return buffer;
  }
  
  static int
  c14nRunTest(const char* xml_filename, int with_comments, int mode,
  	    const char* xpath_filename, const char *ns_filename,
  	    const char* result_file) {
      xmlDocPtr doc;
      xmlXPathObjectPtr xpath = NULL;
      xmlChar *result = NULL;
      int ret;
      xmlChar **inclusive_namespaces = NULL;
      const char *nslist = NULL;
      int nssize;
  
  
      /*
       * build an XML tree from a the file; we need to add default
       * attributes and resolve all character and entities references
       */
      xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
      xmlSubstituteEntitiesDefault(1);
  
      doc = xmlReadFile(xml_filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT);
      if (doc == NULL) {
  	fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_filename);
  	return(-1);
      }
  
      /*
       * Check the document is of the right kind
       */
      if(xmlDocGetRootElement(doc) == NULL) {
          fprintf(stderr,"Error: empty document for file \"%s\"\n", xml_filename);
  	xmlFreeDoc(doc);
  	return(-1);
      }
  
      /*
       * load xpath file if specified
       */
      if(xpath_filename) {
  	xpath = load_xpath_expr(doc, xpath_filename);
  	if(xpath == NULL) {
  	    fprintf(stderr,"Error: unable to evaluate xpath expression\n");
  	    xmlFreeDoc(doc);
  	    return(-1);
  	}
      }
  
      if (ns_filename != NULL) {
          if (loadMem(ns_filename, &nslist, &nssize)) {
  	    fprintf(stderr,"Error: unable to evaluate xpath expression\n");
  	    if(xpath != NULL) xmlXPathFreeObject(xpath);
  	    xmlFreeDoc(doc);
  	    return(-1);
  	}
          inclusive_namespaces = parse_list((xmlChar *) nslist);
      }
  
      /*
       * Canonical form
       */
      /* fprintf(stderr,"File \"%s\" loaded: start canonization\n", xml_filename); */
      ret = xmlC14NDocDumpMemory(doc,
  	    (xpath) ? xpath->nodesetval : NULL,
  	    mode, inclusive_namespaces,
  	    with_comments, &result);
      if (ret >= 0) {
  	if(result != NULL) {
  	    if (compareFileMem(result_file, (const char *) result, ret)) {
  		fprintf(stderr, "Result mismatch for %s\n", xml_filename);
  		fprintf(stderr, "RESULT:\n%s\n", (const char*)result);
  	        ret = -1;
  	    }
  	}
      } else {
  	fprintf(stderr,"Error: failed to canonicalize XML file \"%s\" (ret=%d)\n", xml_filename, ret);
  	ret = -1;
      }
  
      /*
       * Cleanup
       */
      if (result != NULL) xmlFree(result);
      if(xpath != NULL) xmlXPathFreeObject(xpath);
      if (inclusive_namespaces != NULL) xmlFree(inclusive_namespaces);
      if (nslist != NULL) free((char *) nslist);
      xmlFreeDoc(doc);
  
      return(ret);
  }
  
  static int
  c14nCommonTest(const char *filename, int with_comments, int mode,
                 const char *subdir) {
      char buf[500];
      char prefix[500];
      const char *base;
      int len;
      char *result = NULL;
      char *xpath = NULL;
      char *ns = NULL;
      int ret = 0;
  
      base = baseFilename(filename);
      len = strlen(base);
      len -= 4;
      memcpy(prefix, base, len);
      prefix[len] = 0;
  
      if (snprintf(buf, 499, "result/c14n/%s/%s", subdir, prefix) >= 499)
          buf[499] = 0;
      if (!checkTestFile(buf) && !update_results) {
          fprintf(stderr, "Missing result file %s", buf);
  	return(-1);
      }
      result = strdup(buf);
      if (snprintf(buf, 499, "test/c14n/%s/%s.xpath", subdir, prefix) >= 499)
          buf[499] = 0;
      if (checkTestFile(buf)) {
  	xpath = strdup(buf);
      }
      if (snprintf(buf, 499, "test/c14n/%s/%s.ns", subdir, prefix) >= 499)
          buf[499] = 0;
      if (checkTestFile(buf)) {
  	ns = strdup(buf);
      }
  
      nb_tests++;
      if (c14nRunTest(filename, with_comments, mode,
                      xpath, ns, result) < 0)
          ret = 1;
  
      if (result != NULL) free(result);
      if (xpath != NULL) free(xpath);
      if (ns != NULL) free(ns);
      return(ret);
  }
  
  static int
  c14nWithCommentTest(const char *filename,
                      const char *resul ATTRIBUTE_UNUSED,
  		    const char *err ATTRIBUTE_UNUSED,
  		    int options ATTRIBUTE_UNUSED) {
      return(c14nCommonTest(filename, 1, XML_C14N_1_0, "with-comments"));
  }
  static int
  c14nWithoutCommentTest(const char *filename,
                      const char *resul ATTRIBUTE_UNUSED,
  		    const char *err ATTRIBUTE_UNUSED,
  		    int options ATTRIBUTE_UNUSED) {
      return(c14nCommonTest(filename, 0, XML_C14N_1_0, "without-comments"));
  }
  static int
  c14nExcWithoutCommentTest(const char *filename,
                      const char *resul ATTRIBUTE_UNUSED,
  		    const char *err ATTRIBUTE_UNUSED,
  		    int options ATTRIBUTE_UNUSED) {
      return(c14nCommonTest(filename, 0, XML_C14N_EXCLUSIVE_1_0, "exc-without-comments"));
  }
  static int
  c14n11WithoutCommentTest(const char *filename,
                      const char *resul ATTRIBUTE_UNUSED,
  		    const char *err ATTRIBUTE_UNUSED,
  		    int options ATTRIBUTE_UNUSED) {
      return(c14nCommonTest(filename, 0, XML_C14N_1_1, "1-1-without-comments"));
  }
  #endif
  #if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED)
  /************************************************************************
   *									*
   *			Catalog and threads test			*
   *									*
   ************************************************************************/
  
  /*
   * mostly a cut and paste from testThreads.c
   */
  #define	MAX_ARGC	20
  
  typedef struct {
      const char *filename;
      int okay;
  } xmlThreadParams;
  
  static const char *catalog = "test/threads/complex.xml";
  static xmlThreadParams threadParams[] = {
      { "test/threads/abc.xml", 0 },
      { "test/threads/acb.xml", 0 },
      { "test/threads/bac.xml", 0 },
      { "test/threads/bca.xml", 0 },
      { "test/threads/cab.xml", 0 },
      { "test/threads/cba.xml", 0 },
      { "test/threads/invalid.xml", 0 }
  };
  static const unsigned int num_threads = sizeof(threadParams) /
                                          sizeof(threadParams[0]);
  
  #ifndef xmlDoValidityCheckingDefaultValue
  #error xmlDoValidityCheckingDefaultValue is not a macro
  #endif
  #ifndef xmlGenericErrorContext
  #error xmlGenericErrorContext is not a macro
  #endif
  
  static void *
  thread_specific_data(void *private_data)
  {
      xmlDocPtr myDoc;
      xmlThreadParams *params = (xmlThreadParams *) private_data;
      const char *filename = params->filename;
      int okay = 1;
  
      if (!strcmp(filename, "test/threads/invalid.xml")) {
          xmlDoValidityCheckingDefaultValue = 0;
          xmlGenericErrorContext = stdout;
      } else {
          xmlDoValidityCheckingDefaultValue = 1;
          xmlGenericErrorContext = stderr;
      }
  #ifdef LIBXML_SAX1_ENABLED
      myDoc = xmlParseFile(filename);
  #else
      myDoc = xmlReadFile(filename, NULL, XML_WITH_CATALOG);
  #endif
      if (myDoc) {
          xmlFreeDoc(myDoc);
      } else {
          printf("parse failed\n");
          okay = 0;
      }
      if (!strcmp(filename, "test/threads/invalid.xml")) {
          if (xmlDoValidityCheckingDefaultValue != 0) {
              printf("ValidityCheckingDefaultValue override failed\n");
              okay = 0;
          }
          if (xmlGenericErrorContext != stdout) {
              printf("xmlGenericErrorContext override failed\n");
              okay = 0;
          }
      } else {
          if (xmlDoValidityCheckingDefaultValue != 1) {
              printf("ValidityCheckingDefaultValue override failed\n");
              okay = 0;
          }
          if (xmlGenericErrorContext != stderr) {
              printf("xmlGenericErrorContext override failed\n");
              okay = 0;
          }
      }
      params->okay = okay;
      return(NULL);
  }
  
  #if defined(_WIN32) && !defined(__CYGWIN__)
  #include <windows.h>
  #include <string.h>
  
  #define TEST_REPEAT_COUNT 500
  
  static HANDLE tid[MAX_ARGC];
  
  static DWORD WINAPI
  win32_thread_specific_data(void *private_data)
  {
      thread_specific_data(private_data);
      return(0);
  }
  
  static int
  testThread(void)
  {
      unsigned int i, repeat;
      BOOL ret;
      int res = 0;
  
      xmlInitParser();
      for (repeat = 0; repeat < TEST_REPEAT_COUNT; repeat++) {
          xmlLoadCatalog(catalog);
          nb_tests++;
  
          for (i = 0; i < num_threads; i++) {
              tid[i] = (HANDLE) - 1;
          }
  
          for (i = 0; i < num_threads; i++) {
              DWORD useless;
  
              tid[i] = CreateThread(NULL, 0,
                                    win32_thread_specific_data,
  				  (void *) &threadParams[i], 0,
                                    &useless);
              if (tid[i] == NULL) {
                  fprintf(stderr, "CreateThread failed\n");
                  return(1);
              }
          }
  
          if (WaitForMultipleObjects(num_threads, tid, TRUE, INFINITE) ==
              WAIT_FAILED) {
              fprintf(stderr, "WaitForMultipleObjects failed\n");
  	    return(1);
  	}
  
          for (i = 0; i < num_threads; i++) {
              DWORD exitCode;
              ret = GetExitCodeThread(tid[i], &exitCode);
              if (ret == 0) {
                  fprintf(stderr, "GetExitCodeThread failed\n");
                  return(1);
              }
              CloseHandle(tid[i]);
          }
  
          xmlCatalogCleanup();
          for (i = 0; i < num_threads; i++) {
              if (threadParams[i].okay == 0) {
                  fprintf(stderr, "Thread %d handling %s failed\n",
  		        i, threadParams[i].filename);
  	        res = 1;
  	    }
          }
      }
  
      return (res);
  }
  
  #elif defined __BEOS__
  #include <OS.h>
  
  static thread_id tid[MAX_ARGC];
  
  static int
  testThread(void)
  {
      unsigned int i, repeat;
      status_t ret;
      int res = 0;
  
      xmlInitParser();
      for (repeat = 0; repeat < 500; repeat++) {
          xmlLoadCatalog(catalog);
          for (i = 0; i < num_threads; i++) {
              tid[i] = (thread_id) - 1;
          }
          for (i = 0; i < num_threads; i++) {
              tid[i] =
                  spawn_thread(thread_specific_data, "xmlTestThread",
                               B_NORMAL_PRIORITY, (void *) &threadParams[i]);
              if (tid[i] < B_OK) {
                  fprintf(stderr, "beos_thread_create failed\n");
                  return (1);
              }
              printf("beos_thread_create %d -> %d\n", i, tid[i]);
          }
          for (i = 0; i < num_threads; i++) {
              void *result;
              ret = wait_for_thread(tid[i], &result);
              printf("beos_thread_wait %d -> %d\n", i, ret);
              if (ret != B_OK) {
                  fprintf(stderr, "beos_thread_wait failed\n");
                  return (1);
              }
          }
  
          xmlCatalogCleanup();
          ret = B_OK;
          for (i = 0; i < num_threads; i++)
              if (threadParams[i].okay == 0) {
                  printf("Thread %d handling %s failed\n", i,
                         threadParams[i].filename);
                  ret = B_ERROR;
              }
      }
      if (ret != B_OK)
          return(1);
      return (0);
  }
  
  #elif defined HAVE_PTHREAD_H
  #include <pthread.h>
  
  static pthread_t tid[MAX_ARGC];
  
  static int
  testThread(void)
  {
      unsigned int i, repeat;
      int ret;
      int res = 0;
  
      xmlInitParser();
  
      for (repeat = 0; repeat < 500; repeat++) {
          xmlLoadCatalog(catalog);
          nb_tests++;
  
          for (i = 0; i < num_threads; i++) {
              tid[i] = (pthread_t) - 1;
          }
  
          for (i = 0; i < num_threads; i++) {
              ret = pthread_create(&tid[i], 0, thread_specific_data,
                                   (void *) &threadParams[i]);
              if (ret != 0) {
                  fprintf(stderr, "pthread_create failed\n");
                  return (1);
              }
          }
          for (i = 0; i < num_threads; i++) {
              void *result;
              ret = pthread_join(tid[i], &result);
              if (ret != 0) {
                  fprintf(stderr, "pthread_join failed\n");
                  return (1);
              }
          }
  
          xmlCatalogCleanup();
          for (i = 0; i < num_threads; i++)
              if (threadParams[i].okay == 0) {
                  fprintf(stderr, "Thread %d handling %s failed\n",
                          i, threadParams[i].filename);
                  res = 1;
              }
      }
      return (res);
  }
  
  #else
  static int
  testThread(void)
  {
      fprintf(stderr,
              "Specific platform thread support not detected\n");
      return (-1);
  }
  #endif
  static int
  threadsTest(const char *filename ATTRIBUTE_UNUSED,
  	    const char *resul ATTRIBUTE_UNUSED,
  	    const char *err ATTRIBUTE_UNUSED,
  	    int options ATTRIBUTE_UNUSED) {
      return(testThread());
  }
  #endif
  /************************************************************************
   *									*
   *			Tests Descriptions				*
   *									*
   ************************************************************************/
  
  static
  testDesc testDescriptions[] = {
      { "XML regression tests" ,
        oldParseTest, "./test/*", "result/", "", NULL,
        0 },
      { "XML regression tests on memory" ,
        memParseTest, "./test/*", "result/", "", NULL,
        0 },
      { "XML entity subst regression tests" ,
        noentParseTest, "./test/*", "result/noent/", "", NULL,
        XML_PARSE_NOENT },
      { "XML Namespaces regression tests",
        errParseTest, "./test/namespaces/*", "result/namespaces/", "", ".err",
        0 },
      { "Error cases regression tests",
        errParseTest, "./test/errors/*.xml", "result/errors/", "", ".err",
        0 },
      { "Error cases regression tests with entity substitution",
        errParseTest, "./test/errors/*.xml", "result/errors/", NULL, ".ent",
        XML_PARSE_NOENT },
      { "Error cases regression tests (old 1.0)",
        errParseTest, "./test/errors10/*.xml", "result/errors10/", "", ".err",
        XML_PARSE_OLD10 },
  #ifdef LIBXML_READER_ENABLED
      { "Error cases stream regression tests",
        streamParseTest, "./test/errors/*.xml", "result/errors/", NULL, ".str",
        0 },
      { "Reader regression tests",
        streamParseTest, "./test/*", "result/", ".rdr", NULL,
        0 },
      { "Reader entities substitution regression tests",
        streamParseTest, "./test/*", "result/", ".rde", NULL,
        XML_PARSE_NOENT },
      { "Reader on memory regression tests",
        streamMemParseTest, "./test/*", "result/", ".rdr", NULL,
        0 },
      { "Walker regression tests",
        walkerParseTest, "./test/*", "result/", ".rdr", NULL,
        0 },
  #endif
  #ifdef LIBXML_SAX1_ENABLED
      { "SAX1 callbacks regression tests" ,
        saxParseTest, "./test/*", "result/", ".sax", NULL,
        XML_PARSE_SAX1 },
  #endif
      { "SAX2 callbacks regression tests" ,
        saxParseTest, "./test/*", "result/", ".sax2", NULL,
        0 },
      { "SAX2 callbacks regression tests with entity substitution" ,
        saxParseTest, "./test/*", "result/noent/", ".sax2", NULL,
        XML_PARSE_NOENT },
  #ifdef LIBXML_PUSH_ENABLED
      { "XML push regression tests" ,
        pushParseTest, "./test/*", "result/", "", NULL,
        0 },
  #endif
  #ifdef LIBXML_HTML_ENABLED
      { "HTML regression tests" ,
        errParseTest, "./test/HTML/*", "result/HTML/", "", ".err",
        XML_PARSE_HTML },
  #ifdef LIBXML_PUSH_ENABLED
      { "Push HTML regression tests" ,
        pushParseTest, "./test/HTML/*", "result/HTML/", "", ".err",
        XML_PARSE_HTML },
  #endif
      { "HTML SAX regression tests" ,
        saxParseTest, "./test/HTML/*", "result/HTML/", ".sax", NULL,
        XML_PARSE_HTML },
  #endif
  #ifdef LIBXML_VALID_ENABLED
      { "Valid documents regression tests" ,
        errParseTest, "./test/VCM/*", NULL, NULL, NULL,
        XML_PARSE_DTDVALID },
      { "Validity checking regression tests" ,
        errParseTest, "./test/VC/*", "result/VC/", NULL, "",
        XML_PARSE_DTDVALID },
  #ifdef LIBXML_READER_ENABLED
      { "Streaming validity checking regression tests" ,
        streamParseTest, "./test/valid/*.xml", "result/valid/", NULL, ".err.rdr",
        XML_PARSE_DTDVALID },
      { "Streaming validity error checking regression tests" ,
        streamParseTest, "./test/VC/*", "result/VC/", NULL, ".rdr",
        XML_PARSE_DTDVALID },
  #endif
      { "General documents valid regression tests" ,
        errParseTest, "./test/valid/*", "result/valid/", "", ".err",
        XML_PARSE_DTDVALID },
  #endif
  #ifdef LIBXML_XINCLUDE_ENABLED
      { "XInclude regression tests" ,
        errParseTest, "./test/XInclude/docs/*", "result/XInclude/", "", NULL,
        /* Ignore errors at this point ".err", */
        XML_PARSE_XINCLUDE },
  #ifdef LIBXML_READER_ENABLED
      { "XInclude xmlReader regression tests",
        streamParseTest, "./test/XInclude/docs/*", "result/XInclude/", ".rdr",
        /* Ignore errors at this point ".err", */
        NULL, XML_PARSE_XINCLUDE },
  #endif
      { "XInclude regression tests stripping include nodes" ,
        errParseTest, "./test/XInclude/docs/*", "result/XInclude/", "", NULL,
        /* Ignore errors at this point ".err", */
        XML_PARSE_XINCLUDE | XML_PARSE_NOXINCNODE },
  #ifdef LIBXML_READER_ENABLED
      { "XInclude xmlReader regression tests stripping include nodes",
        streamParseTest, "./test/XInclude/docs/*", "result/XInclude/", ".rdr",
        /* Ignore errors at this point ".err", */
        NULL, XML_PARSE_XINCLUDE | XML_PARSE_NOXINCNODE },
  #endif
  #endif
  #ifdef LIBXML_XPATH_ENABLED
  #ifdef LIBXML_DEBUG_ENABLED
      { "XPath expressions regression tests" ,
        xpathExprTest, "./test/XPath/expr/*", "result/XPath/expr/", "", NULL,
        0 },
      { "XPath document queries regression tests" ,
        xpathDocTest, "./test/XPath/docs/*", NULL, NULL, NULL,
        0 },
  #ifdef LIBXML_XPTR_ENABLED
      { "XPointer document queries regression tests" ,
        xptrDocTest, "./test/XPath/docs/*", NULL, NULL, NULL,
        0 },
  #endif
      { "xml:id regression tests" ,
        xmlidDocTest, "./test/xmlid/*", "result/xmlid/", "", ".err",
        0 },
  #endif
  #endif
      { "URI parsing tests" ,
        uriParseTest, "./test/URI/*.uri", "result/URI/", "", NULL,
        0 },
      { "URI base composition tests" ,
        uriBaseTest, "./test/URI/*.data", "result/URI/", "", NULL,
        0 },
      { "Path URI conversion tests" ,
        uriPathTest, NULL, NULL, NULL, NULL,
        0 },
  #ifdef LIBXML_SCHEMAS_ENABLED
      { "Schemas regression tests" ,
        schemasTest, "./test/schemas/*_*.xsd", NULL, NULL, NULL,
        0 },
      { "Relax-NG regression tests" ,
        rngTest, "./test/relaxng/*.rng", NULL, NULL, NULL,
        XML_PARSE_DTDATTR | XML_PARSE_NOENT },
  #ifdef LIBXML_READER_ENABLED
      { "Relax-NG streaming regression tests" ,
        rngStreamTest, "./test/relaxng/*.rng", NULL, NULL, NULL,
        XML_PARSE_DTDATTR | XML_PARSE_NOENT },
  #endif
  #endif
  #ifdef LIBXML_PATTERN_ENABLED
  #ifdef LIBXML_READER_ENABLED
      { "Pattern regression tests" ,
        patternTest, "./test/pattern/*.pat", "result/pattern/", NULL, NULL,
        0 },
  #endif
  #endif
  #ifdef LIBXML_C14N_ENABLED
      { "C14N with comments regression tests" ,
        c14nWithCommentTest, "./test/c14n/with-comments/*.xml", NULL, NULL, NULL,
        0 },
      { "C14N without comments regression tests" ,
        c14nWithoutCommentTest, "./test/c14n/without-comments/*.xml", NULL, NULL, NULL,
        0 },
      { "C14N exclusive without comments regression tests" ,
        c14nExcWithoutCommentTest, "./test/c14n/exc-without-comments/*.xml", NULL, NULL, NULL,
        0 },
      { "C14N 1.1 without comments regression tests" ,
        c14n11WithoutCommentTest, "./test/c14n/1-1-without-comments/*.xml", NULL, NULL, NULL,
        0 },
  #endif
  #if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED)
      { "Catalog and Threads regression tests" ,
        threadsTest, NULL, NULL, NULL, NULL,
        0 },
  #endif
      {NULL, NULL, NULL, NULL, NULL, NULL, 0}
  };
  
  /************************************************************************
   *									*
   *		The main code driving the tests				*
   *									*
   ************************************************************************/
  
  static int
  launchTests(testDescPtr tst) {
      int res = 0, err = 0;
      size_t i;
      char *result;
      char *error;
      int mem;
      xmlCharEncodingHandlerPtr ebcdicHandler, eucJpHandler;
  
      ebcdicHandler = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_EBCDIC);
      eucJpHandler = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_EUC_JP);
  
      if (tst == NULL) return(-1);
      if (tst->in != NULL) {
  	glob_t globbuf;
  
  	globbuf.gl_offs = 0;
  	glob(tst->in, GLOB_DOOFFS, NULL, &globbuf);
  	for (i = 0;i < globbuf.gl_pathc;i++) {
  	    if (!checkTestFile(globbuf.gl_pathv[i]))
  	        continue;
              if (((ebcdicHandler == NULL) &&
                   (strstr(globbuf.gl_pathv[i], "ebcdic") != NULL)) ||
                  ((eucJpHandler == NULL) &&
                   (strstr(globbuf.gl_pathv[i], "icu_parse_test") != NULL)))
                  continue;
  	    if (tst->suffix != NULL) {
  		result = resultFilename(globbuf.gl_pathv[i], tst->out,
  					tst->suffix);
  		if (result == NULL) {
  		    fprintf(stderr, "Out of memory !\n");
  		    fatalError();
  		}
  	    } else {
  	        result = NULL;
  	    }
  	    if (tst->err != NULL) {
  		error = resultFilename(globbuf.gl_pathv[i], tst->out,
  		                        tst->err);
  		if (error == NULL) {
  		    fprintf(stderr, "Out of memory !\n");
  		    fatalError();
  		}
  	    } else {
  	        error = NULL;
  	    }
  	    if ((result) &&(!checkTestFile(result)) && !update_results) {
  	        fprintf(stderr, "Missing result file %s\n", result);
  	    } else if ((error) &&(!checkTestFile(error)) && !update_results) {
  	        fprintf(stderr, "Missing error file %s\n", error);
  	    } else {
  		mem = xmlMemUsed();
  		extraMemoryFromResolver = 0;
  		testErrorsSize = 0;
  		testErrors[0] = 0;
  		res = tst->func(globbuf.gl_pathv[i], result, error,
  		                tst->options | XML_PARSE_COMPACT);
  		xmlResetLastError();
  		if (res != 0) {
  		    fprintf(stderr, "File %s generated an error\n",
  		            globbuf.gl_pathv[i]);
  		    nb_errors++;
  		    err++;
  		}
  		else if (xmlMemUsed() != mem) {
  		    if ((xmlMemUsed() != mem) &&
  		        (extraMemoryFromResolver == 0)) {
  			fprintf(stderr, "File %s leaked %d bytes\n",
  				globbuf.gl_pathv[i], xmlMemUsed() - mem);
  			nb_leaks++;
  			err++;
  		    }
  		}
  		testErrorsSize = 0;
  	    }
  	    if (result)
  		free(result);
  	    if (error)
  		free(error);
  	}
  	globfree(&globbuf);
      } else {
          testErrorsSize = 0;
  	testErrors[0] = 0;
  	extraMemoryFromResolver = 0;
          res = tst->func(NULL, NULL, NULL, tst->options);
  	if (res != 0) {
  	    nb_errors++;
  	    err++;
  	}
      }
  
      xmlCharEncCloseFunc(ebcdicHandler);
      xmlCharEncCloseFunc(eucJpHandler);
  
      return(err);
  }
  
  static int verbose = 0;
  static int tests_quiet = 0;
  
  static int
  runtest(int i) {
      int ret = 0, res;
      int old_errors, old_tests, old_leaks;
  
      old_errors = nb_errors;
      old_tests = nb_tests;
      old_leaks = nb_leaks;
      if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL))
  	printf("## %s\n", testDescriptions[i].desc);
      res = launchTests(&testDescriptions[i]);
      if (res != 0)
  	ret++;
      if (verbose) {
  	if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
  	    printf("Ran %d tests, no errors\n", nb_tests - old_tests);
  	else
  	    printf("Ran %d tests, %d errors, %d leaks\n",
  		   nb_tests - old_tests,
  		   nb_errors - old_errors,
  		   nb_leaks - old_leaks);
      }
      return(ret);
  }
  
  int
  main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
      int i, a, ret = 0;
      int subset = 0;
  
  #if defined(_WIN32) && !defined(__CYGWIN__)
      setvbuf(stdout, NULL, _IONBF, 0);
      setvbuf(stderr, NULL, _IONBF, 0);
  #endif
  
  #if defined(_MSC_VER) && _MSC_VER >= 1400 && _MSC_VER < 1900
      _set_output_format(_TWO_DIGIT_EXPONENT);
  #endif
  
      initializeLibxml2();
  
      for (a = 1; a < argc;a++) {
          if (!strcmp(argv[a], "-v"))
  	    verbose = 1;
          else if (!strcmp(argv[a], "-u"))
  	    update_results = 1;
          else if (!strcmp(argv[a], "-quiet"))
  	    tests_quiet = 1;
          else if (!strcmp(argv[a], "--out"))
  	    temp_directory = argv[++a];
  	else {
  	    for (i = 0; testDescriptions[i].func != NULL; i++) {
  	        if (strstr(testDescriptions[i].desc, argv[a])) {
  		    ret += runtest(i);
  		    subset++;
  		}
  	    }
  	}
      }
      if (subset == 0) {
  	for (i = 0; testDescriptions[i].func != NULL; i++) {
  	    ret += runtest(i);
  	}
      }
      if ((nb_errors == 0) && (nb_leaks == 0)) {
          ret = 0;
  	printf("Total %d tests, no errors\n",
  	       nb_tests);
      } else {
          ret = 1;
  	printf("Total %d tests, %d errors, %d leaks\n",
  	       nb_tests, nb_errors, nb_leaks);
      }
      xmlCleanupParser();
      xmlMemoryDump();
  
      return(ret);
  }
  
  #else /* ! LIBXML_OUTPUT_ENABLED */
  int
  main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
      fprintf(stderr, "runtest requires output to be enabled in libxml2\n");
      return(1);
  }
  #endif
  