text: /*
   * xmlIO.c : implementation of the I/O interfaces used by the parser
   *
   * See Copyright for the status of this software.
   *
   * daniel@veillard.com
   *
   * 14 Nov 2000 ht - for VMS, truncated name of long functions to under 32 char
   */
  
  #define IN_LIBXML
  #include "libxml.h"
  
  #include <string.h>
  #include <stdlib.h>
  #include <errno.h>
  
  #ifdef HAVE_SYS_TYPES_H
  #include <sys/types.h>
  #endif
  #ifdef HAVE_SYS_STAT_H
  #include <sys/stat.h>
  #endif
  #ifdef HAVE_FCNTL_H
  #include <fcntl.h>
  #endif
  #ifdef HAVE_UNISTD_H
  #include <unistd.h>
  #endif
  #ifdef LIBXML_ZLIB_ENABLED
  #include <zlib.h>
  #endif
  #ifdef LIBXML_LZMA_ENABLED
  #include <lzma.h>
  #endif
  
  #if defined(_WIN32)
  #define WIN32_LEAN_AND_MEAN
  #include <windows.h>
  #endif
  
  #ifndef S_ISDIR
  #  ifdef _S_ISDIR
  #    define S_ISDIR(x) _S_ISDIR(x)
  #  elif defined(S_IFDIR)
  #    ifdef S_IFMT
  #      define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
  #    elif defined(_S_IFMT)
  #      define S_ISDIR(m) (((m) & _S_IFMT) == S_IFDIR)
  #    endif
  #  endif
  #endif
  
  #include <libxml/xmlmemory.h>
  #include <libxml/parser.h>
  #include <libxml/parserInternals.h>
  #include <libxml/xmlIO.h>
  #include <libxml/uri.h>
  #include <libxml/nanohttp.h>
  #include <libxml/nanoftp.h>
  #include <libxml/xmlerror.h>
  #ifdef LIBXML_CATALOG_ENABLED
  #include <libxml/catalog.h>
  #endif
  #include <libxml/globals.h>
  
  #include "buf.h"
  #include "enc.h"
  
  /* #define VERBOSE_FAILURE */
  /* #define DEBUG_EXTERNAL_ENTITIES */
  /* #define DEBUG_INPUT */
  
  #ifdef DEBUG_INPUT
  #define MINLEN 40
  #else
  #define MINLEN 4000
  #endif
  
  /*
   * Input I/O callback sets
   */
  typedef struct _xmlInputCallback {
      xmlInputMatchCallback matchcallback;
      xmlInputOpenCallback opencallback;
      xmlInputReadCallback readcallback;
      xmlInputCloseCallback closecallback;
  } xmlInputCallback;
  
  #define MAX_INPUT_CALLBACK 15
  
  static xmlInputCallback xmlInputCallbackTable[MAX_INPUT_CALLBACK];
  static int xmlInputCallbackNr = 0;
  static int xmlInputCallbackInitialized = 0;
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /*
   * Output I/O callback sets
   */
  typedef struct _xmlOutputCallback {
      xmlOutputMatchCallback matchcallback;
      xmlOutputOpenCallback opencallback;
      xmlOutputWriteCallback writecallback;
      xmlOutputCloseCallback closecallback;
  } xmlOutputCallback;
  
  #define MAX_OUTPUT_CALLBACK 15
  
  static xmlOutputCallback xmlOutputCallbackTable[MAX_OUTPUT_CALLBACK];
  static int xmlOutputCallbackNr = 0;
  static int xmlOutputCallbackInitialized = 0;
  
  xmlOutputBufferPtr
  xmlAllocOutputBufferInternal(xmlCharEncodingHandlerPtr encoder);
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /************************************************************************
   *									*
   *		Tree memory error handler				*
   *									*
   ************************************************************************/
  
  static const char *IOerr[] = {
      "Unknown IO error",         /* UNKNOWN */
      "Permission denied",	/* EACCES */
      "Resource temporarily unavailable",/* EAGAIN */
      "Bad file descriptor",	/* EBADF */
      "Bad message",		/* EBADMSG */
      "Resource busy",		/* EBUSY */
      "Operation canceled",	/* ECANCELED */
      "No child processes",	/* ECHILD */
      "Resource deadlock avoided",/* EDEADLK */
      "Domain error",		/* EDOM */
      "File exists",		/* EEXIST */
      "Bad address",		/* EFAULT */
      "File too large",		/* EFBIG */
      "Operation in progress",	/* EINPROGRESS */
      "Interrupted function call",/* EINTR */
      "Invalid argument",		/* EINVAL */
      "Input/output error",	/* EIO */
      "Is a directory",		/* EISDIR */
      "Too many open files",	/* EMFILE */
      "Too many links",		/* EMLINK */
      "Inappropriate message buffer length",/* EMSGSIZE */
      "Filename too long",	/* ENAMETOOLONG */
      "Too many open files in system",/* ENFILE */
      "No such device",		/* ENODEV */
      "No such file or directory",/* ENOENT */
      "Exec format error",	/* ENOEXEC */
      "No locks available",	/* ENOLCK */
      "Not enough space",		/* ENOMEM */
      "No space left on device",	/* ENOSPC */
      "Function not implemented",	/* ENOSYS */
      "Not a directory",		/* ENOTDIR */
      "Directory not empty",	/* ENOTEMPTY */
      "Not supported",		/* ENOTSUP */
      "Inappropriate I/O control operation",/* ENOTTY */
      "No such device or address",/* ENXIO */
      "Operation not permitted",	/* EPERM */
      "Broken pipe",		/* EPIPE */
      "Result too large",		/* ERANGE */
      "Read-only file system",	/* EROFS */
      "Invalid seek",		/* ESPIPE */
      "No such process",		/* ESRCH */
      "Operation timed out",	/* ETIMEDOUT */
      "Improper link",		/* EXDEV */
      "Attempt to load network entity %s", /* XML_IO_NETWORK_ATTEMPT */
      "encoder error",		/* XML_IO_ENCODER */
      "flush error",
      "write error",
      "no input",
      "buffer full",
      "loading error",
      "not a socket",		/* ENOTSOCK */
      "already connected",	/* EISCONN */
      "connection refused",	/* ECONNREFUSED */
      "unreachable network",	/* ENETUNREACH */
      "address in use",		/* EADDRINUSE */
      "already in use",		/* EALREADY */
      "unknown address family",	/* EAFNOSUPPORT */
  };
  
  #if defined(_WIN32)
  /**
   * __xmlIOWin32UTF8ToWChar:
   * @u8String:  uft-8 string
   *
   * Convert a string from utf-8 to wchar (WINDOWS ONLY!)
   */
  static wchar_t *
  __xmlIOWin32UTF8ToWChar(const char *u8String)
  {
      wchar_t *wString = NULL;
  
      if (u8String) {
          int wLen =
              MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, u8String,
                                  -1, NULL, 0);
          if (wLen) {
              wString = xmlMalloc(wLen * sizeof(wchar_t));
              if (wString) {
                  if (MultiByteToWideChar
                      (CP_UTF8, 0, u8String, -1, wString, wLen) == 0) {
                      xmlFree(wString);
                      wString = NULL;
                  }
              }
          }
      }
  
      return wString;
  }
  #endif
  
  /**
   * xmlIOErrMemory:
   * @extra:  extra information
   *
   * Handle an out of memory condition
   */
  static void
  xmlIOErrMemory(const char *extra)
  {
      __xmlSimpleError(XML_FROM_IO, XML_ERR_NO_MEMORY, NULL, NULL, extra);
  }
  
  /**
   * __xmlIOErr:
   * @code:  the error number
   * @
   * @extra:  extra information
   *
   * Handle an I/O error
   */
  void
  __xmlIOErr(int domain, int code, const char *extra)
  {
      unsigned int idx;
  
      if (code == 0) {
  	if (errno == 0) code = 0;
  #ifdef EACCES
          else if (errno == EACCES) code = XML_IO_EACCES;
  #endif
  #ifdef EAGAIN
          else if (errno == EAGAIN) code = XML_IO_EAGAIN;
  #endif
  #ifdef EBADF
          else if (errno == EBADF) code = XML_IO_EBADF;
  #endif
  #ifdef EBADMSG
          else if (errno == EBADMSG) code = XML_IO_EBADMSG;
  #endif
  #ifdef EBUSY
          else if (errno == EBUSY) code = XML_IO_EBUSY;
  #endif
  #ifdef ECANCELED
          else if (errno == ECANCELED) code = XML_IO_ECANCELED;
  #endif
  #ifdef ECHILD
          else if (errno == ECHILD) code = XML_IO_ECHILD;
  #endif
  #ifdef EDEADLK
          else if (errno == EDEADLK) code = XML_IO_EDEADLK;
  #endif
  #ifdef EDOM
          else if (errno == EDOM) code = XML_IO_EDOM;
  #endif
  #ifdef EEXIST
          else if (errno == EEXIST) code = XML_IO_EEXIST;
  #endif
  #ifdef EFAULT
          else if (errno == EFAULT) code = XML_IO_EFAULT;
  #endif
  #ifdef EFBIG
          else if (errno == EFBIG) code = XML_IO_EFBIG;
  #endif
  #ifdef EINPROGRESS
          else if (errno == EINPROGRESS) code = XML_IO_EINPROGRESS;
  #endif
  #ifdef EINTR
          else if (errno == EINTR) code = XML_IO_EINTR;
  #endif
  #ifdef EINVAL
          else if (errno == EINVAL) code = XML_IO_EINVAL;
  #endif
  #ifdef EIO
          else if (errno == EIO) code = XML_IO_EIO;
  #endif
  #ifdef EISDIR
          else if (errno == EISDIR) code = XML_IO_EISDIR;
  #endif
  #ifdef EMFILE
          else if (errno == EMFILE) code = XML_IO_EMFILE;
  #endif
  #ifdef EMLINK
          else if (errno == EMLINK) code = XML_IO_EMLINK;
  #endif
  #ifdef EMSGSIZE
          else if (errno == EMSGSIZE) code = XML_IO_EMSGSIZE;
  #endif
  #ifdef ENAMETOOLONG
          else if (errno == ENAMETOOLONG) code = XML_IO_ENAMETOOLONG;
  #endif
  #ifdef ENFILE
          else if (errno == ENFILE) code = XML_IO_ENFILE;
  #endif
  #ifdef ENODEV
          else if (errno == ENODEV) code = XML_IO_ENODEV;
  #endif
  #ifdef ENOENT
          else if (errno == ENOENT) code = XML_IO_ENOENT;
  #endif
  #ifdef ENOEXEC
          else if (errno == ENOEXEC) code = XML_IO_ENOEXEC;
  #endif
  #ifdef ENOLCK
          else if (errno == ENOLCK) code = XML_IO_ENOLCK;
  #endif
  #ifdef ENOMEM
          else if (errno == ENOMEM) code = XML_IO_ENOMEM;
  #endif
  #ifdef ENOSPC
          else if (errno == ENOSPC) code = XML_IO_ENOSPC;
  #endif
  #ifdef ENOSYS
          else if (errno == ENOSYS) code = XML_IO_ENOSYS;
  #endif
  #ifdef ENOTDIR
          else if (errno == ENOTDIR) code = XML_IO_ENOTDIR;
  #endif
  #ifdef ENOTEMPTY
          else if (errno == ENOTEMPTY) code = XML_IO_ENOTEMPTY;
  #endif
  #ifdef ENOTSUP
          else if (errno == ENOTSUP) code = XML_IO_ENOTSUP;
  #endif
  #ifdef ENOTTY
          else if (errno == ENOTTY) code = XML_IO_ENOTTY;
  #endif
  #ifdef ENXIO
          else if (errno == ENXIO) code = XML_IO_ENXIO;
  #endif
  #ifdef EPERM
          else if (errno == EPERM) code = XML_IO_EPERM;
  #endif
  #ifdef EPIPE
          else if (errno == EPIPE) code = XML_IO_EPIPE;
  #endif
  #ifdef ERANGE
          else if (errno == ERANGE) code = XML_IO_ERANGE;
  #endif
  #ifdef EROFS
          else if (errno == EROFS) code = XML_IO_EROFS;
  #endif
  #ifdef ESPIPE
          else if (errno == ESPIPE) code = XML_IO_ESPIPE;
  #endif
  #ifdef ESRCH
          else if (errno == ESRCH) code = XML_IO_ESRCH;
  #endif
  #ifdef ETIMEDOUT
          else if (errno == ETIMEDOUT) code = XML_IO_ETIMEDOUT;
  #endif
  #ifdef EXDEV
          else if (errno == EXDEV) code = XML_IO_EXDEV;
  #endif
  #ifdef ENOTSOCK
          else if (errno == ENOTSOCK) code = XML_IO_ENOTSOCK;
  #endif
  #ifdef EISCONN
          else if (errno == EISCONN) code = XML_IO_EISCONN;
  #endif
  #ifdef ECONNREFUSED
          else if (errno == ECONNREFUSED) code = XML_IO_ECONNREFUSED;
  #endif
  #ifdef ETIMEDOUT
          else if (errno == ETIMEDOUT) code = XML_IO_ETIMEDOUT;
  #endif
  #ifdef ENETUNREACH
          else if (errno == ENETUNREACH) code = XML_IO_ENETUNREACH;
  #endif
  #ifdef EADDRINUSE
          else if (errno == EADDRINUSE) code = XML_IO_EADDRINUSE;
  #endif
  #ifdef EINPROGRESS
          else if (errno == EINPROGRESS) code = XML_IO_EINPROGRESS;
  #endif
  #ifdef EALREADY
          else if (errno == EALREADY) code = XML_IO_EALREADY;
  #endif
  #ifdef EAFNOSUPPORT
          else if (errno == EAFNOSUPPORT) code = XML_IO_EAFNOSUPPORT;
  #endif
          else code = XML_IO_UNKNOWN;
      }
      idx = 0;
      if (code >= XML_IO_UNKNOWN) idx = code - XML_IO_UNKNOWN;
      if (idx >= (sizeof(IOerr) / sizeof(IOerr[0]))) idx = 0;
  
      __xmlSimpleError(domain, code, NULL, IOerr[idx], extra);
  }
  
  /**
   * xmlIOErr:
   * @code:  the error number
   * @extra:  extra information
   *
   * Handle an I/O error
   */
  static void
  xmlIOErr(int code, const char *extra)
  {
      __xmlIOErr(XML_FROM_IO, code, extra);
  }
  
  /**
   * __xmlLoaderErr:
   * @ctx: the parser context
   * @extra:  extra information
   *
   * Handle a resource access error
   */
  void
  __xmlLoaderErr(void *ctx, const char *msg, const char *filename)
  {
      xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
      xmlStructuredErrorFunc schannel = NULL;
      xmlGenericErrorFunc channel = NULL;
      void *data = NULL;
      xmlErrorLevel level = XML_ERR_ERROR;
  
      if ((ctxt != NULL) && (ctxt->disableSAX != 0) &&
          (ctxt->instate == XML_PARSER_EOF))
  	return;
      if ((ctxt != NULL) && (ctxt->sax != NULL)) {
          if (ctxt->validate) {
  	    channel = ctxt->sax->error;
  	    level = XML_ERR_ERROR;
  	} else {
  	    channel = ctxt->sax->warning;
  	    level = XML_ERR_WARNING;
  	}
  	if (ctxt->sax->initialized == XML_SAX2_MAGIC)
  	    schannel = ctxt->sax->serror;
  	data = ctxt->userData;
      }
      __xmlRaiseError(schannel, channel, data, ctxt, NULL, XML_FROM_IO,
                      XML_IO_LOAD_ERROR, level, NULL, 0,
  		    filename, NULL, NULL, 0, 0,
  		    msg, filename);
  
  }
  
  /************************************************************************
   *									*
   *		Tree memory error handler				*
   *									*
   ************************************************************************/
  /**
   * xmlNormalizeWindowsPath:
   * @path: the input file path
   *
   * This function is obsolete. Please see xmlURIFromPath in uri.c for
   * a better solution.
   *
   * Returns a canonicalized version of the path
   */
  xmlChar *
  xmlNormalizeWindowsPath(const xmlChar *path)
  {
      return xmlCanonicPath(path);
  }
  
  /**
   * xmlCleanupInputCallbacks:
   *
   * clears the entire input callback table. this includes the
   * compiled-in I/O.
   */
  void
  xmlCleanupInputCallbacks(void)
  {
      int i;
  
      if (!xmlInputCallbackInitialized)
          return;
  
      for (i = xmlInputCallbackNr - 1; i >= 0; i--) {
          xmlInputCallbackTable[i].matchcallback = NULL;
          xmlInputCallbackTable[i].opencallback = NULL;
          xmlInputCallbackTable[i].readcallback = NULL;
          xmlInputCallbackTable[i].closecallback = NULL;
      }
  
      xmlInputCallbackNr = 0;
      xmlInputCallbackInitialized = 0;
  }
  
  /**
   * xmlPopInputCallbacks:
   *
   * Clear the top input callback from the input stack. this includes the
   * compiled-in I/O.
   *
   * Returns the number of input callback registered or -1 in case of error.
   */
  int
  xmlPopInputCallbacks(void)
  {
      if (!xmlInputCallbackInitialized)
          return(-1);
  
      if (xmlInputCallbackNr <= 0)
          return(-1);
  
      xmlInputCallbackNr--;
      xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = NULL;
      xmlInputCallbackTable[xmlInputCallbackNr].opencallback = NULL;
      xmlInputCallbackTable[xmlInputCallbackNr].readcallback = NULL;
      xmlInputCallbackTable[xmlInputCallbackNr].closecallback = NULL;
  
      return(xmlInputCallbackNr);
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlCleanupOutputCallbacks:
   *
   * clears the entire output callback table. this includes the
   * compiled-in I/O callbacks.
   */
  void
  xmlCleanupOutputCallbacks(void)
  {
      int i;
  
      if (!xmlOutputCallbackInitialized)
          return;
  
      for (i = xmlOutputCallbackNr - 1; i >= 0; i--) {
          xmlOutputCallbackTable[i].matchcallback = NULL;
          xmlOutputCallbackTable[i].opencallback = NULL;
          xmlOutputCallbackTable[i].writecallback = NULL;
          xmlOutputCallbackTable[i].closecallback = NULL;
      }
  
      xmlOutputCallbackNr = 0;
      xmlOutputCallbackInitialized = 0;
  }
  
  /**
   * xmlPopOutputCallbacks:
   *
   * Remove the top output callbacks from the output stack. This includes the
   * compiled-in I/O.
   *
   * Returns the number of output callback registered or -1 in case of error.
   */
  int
  xmlPopOutputCallbacks(void)
  {
      if (!xmlOutputCallbackInitialized)
          return(-1);
  
      if (xmlOutputCallbackNr <= 0)
          return(-1);
  
      xmlOutputCallbackNr--;
      xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = NULL;
      xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = NULL;
      xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = NULL;
      xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = NULL;
  
      return(xmlOutputCallbackNr);
  }
  
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /************************************************************************
   *									*
   *		Standard I/O for file accesses				*
   *									*
   ************************************************************************/
  
  #if defined(_WIN32)
  
  /**
   *  xmlWrapOpenUtf8:
   * @path:  the path in utf-8 encoding
   * @mode:  type of access (0 - read, 1 - write)
   *
   * function opens the file specified by @path
   *
   */
  static FILE*
  xmlWrapOpenUtf8(const char *path,int mode)
  {
      FILE *fd = NULL;
      wchar_t *wPath;
  
      wPath = __xmlIOWin32UTF8ToWChar(path);
      if(wPath)
      {
         fd = _wfopen(wPath, mode ? L"wb" : L"rb");
         xmlFree(wPath);
      }
      /* maybe path in native encoding */
      if(fd == NULL)
         fd = fopen(path, mode ? "wb" : "rb");
  
      return fd;
  }
  
  #ifdef LIBXML_ZLIB_ENABLED
  static gzFile
  xmlWrapGzOpenUtf8(const char *path, const char *mode)
  {
      gzFile fd;
      wchar_t *wPath;
  
      fd = gzopen (path, mode);
      if (fd)
          return fd;
  
      wPath = __xmlIOWin32UTF8ToWChar(path);
      if(wPath)
      {
  	int d, m = (strstr(mode, "r") ? O_RDONLY : O_RDWR);
  #ifdef _O_BINARY
          m |= (strstr(mode, "b") ? _O_BINARY : 0);
  #endif
  	d = _wopen(wPath, m);
  	if (d >= 0)
  	    fd = gzdopen(d, mode);
          xmlFree(wPath);
      }
  
      return fd;
  }
  #endif
  
  /**
   *  xmlWrapStatUtf8:
   * @path:  the path in utf-8 encoding
   * @info:  structure that stores results
   *
   * function obtains information about the file or directory
   *
   */
  static int
  xmlWrapStatUtf8(const char *path, struct _stat *info) {
      int retval = -1;
      wchar_t *wPath;
  
      wPath = __xmlIOWin32UTF8ToWChar(path);
      if (wPath) {
         retval = _wstat(wPath, info);
         xmlFree(wPath);
      }
      /* maybe path in native encoding */
      if(retval < 0)
         retval = _stat(path, info);
      return retval;
  }
  
  #endif
  
  /**
   * xmlCheckFilename:
   * @path:  the path to check
   *
   * function checks to see if @path is a valid source
   * (file, socket...) for XML.
   *
   * if stat is not available on the target machine,
   * returns 1.  if stat fails, returns 0 (if calling
   * stat on the filename fails, it can't be right).
   * if stat succeeds and the file is a directory,
   * returns 2.  otherwise returns 1.
   */
  
  int
  xmlCheckFilename (const char *path)
  {
  #ifdef HAVE_STAT
  #if defined(_WIN32)
      struct _stat stat_buffer;
  #else
      struct stat stat_buffer;
  #endif
  #endif
      if (path == NULL)
  	return(0);
  
  #ifdef HAVE_STAT
  #if defined(_WIN32)
      /*
       * On Windows stat and wstat do not work with long pathname,
       * which start with '\\?\'
       */
      if ((path[0] == '\\') && (path[1] == '\\') && (path[2] == '?') &&
  	(path[3] == '\\') )
  	    return 1;
  
      if (xmlWrapStatUtf8(path, &stat_buffer) == -1)
          return 0;
  #else
      if (stat(path, &stat_buffer) == -1)
          return 0;
  #endif
  #ifdef S_ISDIR
      if (S_ISDIR(stat_buffer.st_mode))
          return 2;
  #endif
  #endif /* HAVE_STAT */
      return 1;
  }
  
  /**
   * xmlInputReadCallbackNop:
   *
   * No Operation xmlInputReadCallback function, does nothing.
   *
   * Returns zero
   */
  int
  xmlInputReadCallbackNop(void *context ATTRIBUTE_UNUSED,
                          char *buffer ATTRIBUTE_UNUSED,
                          int len ATTRIBUTE_UNUSED) {
      return(0);
  }
  
  /**
   * xmlFdRead:
   * @context:  the I/O context
   * @buffer:  where to drop data
   * @len:  number of bytes to read
   *
   * Read @len bytes to @buffer from the I/O channel.
   *
   * Returns the number of bytes written
   */
  static int
  xmlFdRead (void * context, char * buffer, int len) {
      int ret;
  
      ret = read((int) (ptrdiff_t) context, &buffer[0], len);
      if (ret < 0) xmlIOErr(0, "read()");
      return(ret);
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlFdWrite:
   * @context:  the I/O context
   * @buffer:  where to get data
   * @len:  number of bytes to write
   *
   * Write @len bytes from @buffer to the I/O channel.
   *
   * Returns the number of bytes written
   */
  static int
  xmlFdWrite (void * context, const char * buffer, int len) {
      int ret = 0;
  
      if (len > 0) {
  	ret = write((int) (ptrdiff_t) context, &buffer[0], len);
  	if (ret < 0) xmlIOErr(0, "write()");
      }
      return(ret);
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /**
   * xmlFdClose:
   * @context:  the I/O context
   *
   * Close an I/O channel
   *
   * Returns 0 in case of success and error code otherwise
   */
  static int
  xmlFdClose (void * context) {
      int ret;
      ret = close((int) (ptrdiff_t) context);
      if (ret < 0) xmlIOErr(0, "close()");
      return(ret);
  }
  
  /**
   * xmlFileMatch:
   * @filename:  the URI for matching
   *
   * input from FILE *
   *
   * Returns 1 if matches, 0 otherwise
   */
  int
  xmlFileMatch (const char *filename ATTRIBUTE_UNUSED) {
      return(1);
  }
  
  /**
   * xmlFileOpen_real:
   * @filename:  the URI for matching
   *
   * input from FILE *, supports compressed input
   * if @filename is " " then the standard input is used
   *
   * Returns an I/O context or NULL in case of error
   */
  static void *
  xmlFileOpen_real (const char *filename) {
      const char *path = filename;
      FILE *fd;
  
      if (filename == NULL)
          return(NULL);
  
      if (!strcmp(filename, "-")) {
  	fd = stdin;
  	return((void *) fd);
      }
  
      if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) {
  #if defined (_WIN32)
  	path = &filename[17];
  #else
  	path = &filename[16];
  #endif
      } else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
  #if defined (_WIN32)
  	path = &filename[8];
  #else
  	path = &filename[7];
  #endif
      } else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:/", 6)) {
          /* lots of generators seems to lazy to read RFC 1738 */
  #if defined (_WIN32)
  	path = &filename[6];
  #else
  	path = &filename[5];
  #endif
      }
  
      /* Do not check DDNAME on zOS ! */
  #if !defined(__MVS__)
      if (!xmlCheckFilename(path))
          return(NULL);
  #endif
  
  #if defined(_WIN32)
      fd = xmlWrapOpenUtf8(path, 0);
  #else
      fd = fopen(path, "rb");
  #endif /* WIN32 */
      if (fd == NULL) xmlIOErr(0, path);
      return((void *) fd);
  }
  
  /**
   * xmlFileOpen:
   * @filename:  the URI for matching
   *
   * Wrapper around xmlFileOpen_real that try it with an unescaped
   * version of @filename, if this fails fallback to @filename
   *
   * Returns a handler or NULL in case or failure
   */
  void *
  xmlFileOpen (const char *filename) {
      char *unescaped;
      void *retval;
  
      retval = xmlFileOpen_real(filename);
      if (retval == NULL) {
  	unescaped = xmlURIUnescapeString(filename, 0, NULL);
  	if (unescaped != NULL) {
  	    retval = xmlFileOpen_real(unescaped);
  	    xmlFree(unescaped);
  	}
      }
  
      return retval;
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlFileOpenW:
   * @filename:  the URI for matching
   *
   * output to from FILE *,
   * if @filename is "-" then the standard output is used
   *
   * Returns an I/O context or NULL in case of error
   */
  static void *
  xmlFileOpenW (const char *filename) {
      const char *path = NULL;
      FILE *fd;
  
      if (!strcmp(filename, "-")) {
  	fd = stdout;
  	return((void *) fd);
      }
  
      if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
  #if defined (_WIN32)
  	path = &filename[17];
  #else
  	path = &filename[16];
  #endif
      else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
  #if defined (_WIN32)
  	path = &filename[8];
  #else
  	path = &filename[7];
  #endif
      } else
  	path = filename;
  
      if (path == NULL)
  	return(NULL);
  
  #if defined(_WIN32)
      fd = xmlWrapOpenUtf8(path, 1);
  #elif(__MVS__)
      fd = fopen(path, "w");
  #else
      fd = fopen(path, "wb");
  #endif /* WIN32 */
  
      if (fd == NULL) xmlIOErr(0, path);
      return((void *) fd);
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /**
   * xmlFileRead:
   * @context:  the I/O context
   * @buffer:  where to drop data
   * @len:  number of bytes to write
   *
   * Read @len bytes to @buffer from the I/O channel.
   *
   * Returns the number of bytes written or < 0 in case of failure
   */
  int
  xmlFileRead (void * context, char * buffer, int len) {
      int ret;
      if ((context == NULL) || (buffer == NULL))
          return(-1);
      ret = fread(&buffer[0], 1,  len, (FILE *) context);
      if (ret < 0) xmlIOErr(0, "fread()");
      return(ret);
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlFileWrite:
   * @context:  the I/O context
   * @buffer:  where to drop data
   * @len:  number of bytes to write
   *
   * Write @len bytes from @buffer to the I/O channel.
   *
   * Returns the number of bytes written
   */
  static int
  xmlFileWrite (void * context, const char * buffer, int len) {
      int items;
  
      if ((context == NULL) || (buffer == NULL))
          return(-1);
      items = fwrite(&buffer[0], len, 1, (FILE *) context);
      if ((items == 0) && (ferror((FILE *) context))) {
          xmlIOErr(0, "fwrite()");
  	return(-1);
      }
      return(items * len);
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /**
   * xmlFileClose:
   * @context:  the I/O context
   *
   * Close an I/O channel
   *
   * Returns 0 or -1 in case of error
   */
  int
  xmlFileClose (void * context) {
      FILE *fil;
      int ret;
  
      if (context == NULL)
          return(-1);
      fil = (FILE *) context;
      if ((fil == stdout) || (fil == stderr)) {
          ret = fflush(fil);
  	if (ret < 0)
  	    xmlIOErr(0, "fflush()");
  	return(0);
      }
      if (fil == stdin)
  	return(0);
      ret = ( fclose((FILE *) context) == EOF ) ? -1 : 0;
      if (ret < 0)
          xmlIOErr(0, "fclose()");
      return(ret);
  }
  
  /**
   * xmlFileFlush:
   * @context:  the I/O context
   *
   * Flush an I/O channel
   */
  static int
  xmlFileFlush (void * context) {
      int ret;
  
      if (context == NULL)
          return(-1);
      ret = ( fflush((FILE *) context) == EOF ) ? -1 : 0;
      if (ret < 0)
          xmlIOErr(0, "fflush()");
      return(ret);
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlBufferWrite:
   * @context:  the xmlBuffer
   * @buffer:  the data to write
   * @len:  number of bytes to write
   *
   * Write @len bytes from @buffer to the xml buffer
   *
   * Returns the number of bytes written
   */
  static int
  xmlBufferWrite (void * context, const char * buffer, int len) {
      int ret;
  
      ret = xmlBufferAdd((xmlBufferPtr) context, (const xmlChar *) buffer, len);
      if (ret != 0)
          return(-1);
      return(len);
  }
  #endif
  
  #ifdef LIBXML_ZLIB_ENABLED
  /************************************************************************
   *									*
   *		I/O for compressed file accesses			*
   *									*
   ************************************************************************/
  /**
   * xmlGzfileMatch:
   * @filename:  the URI for matching
   *
   * input from compressed file test
   *
   * Returns 1 if matches, 0 otherwise
   */
  static int
  xmlGzfileMatch (const char *filename ATTRIBUTE_UNUSED) {
      return(1);
  }
  
  /**
   * xmlGzfileOpen_real:
   * @filename:  the URI for matching
   *
   * input from compressed file open
   * if @filename is " " then the standard input is used
   *
   * Returns an I/O context or NULL in case of error
   */
  static void *
  xmlGzfileOpen_real (const char *filename) {
      const char *path = NULL;
      gzFile fd;
  
      if (!strcmp(filename, "-")) {
          int duped_fd = dup(fileno(stdin));
          fd = gzdopen(duped_fd, "rb");
          if (fd == Z_NULL && duped_fd >= 0) {
              close(duped_fd);  /* gzdOpen() does not close on failure */
          }
  
  	return((void *) fd);
      }
  
      if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
  #if defined (_WIN32)
  	path = &filename[17];
  #else
  	path = &filename[16];
  #endif
      else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
  #if defined (_WIN32)
  	path = &filename[8];
  #else
  	path = &filename[7];
  #endif
      } else
  	path = filename;
  
      if (path == NULL)
  	return(NULL);
      if (!xmlCheckFilename(path))
          return(NULL);
  
  #if defined(_WIN32)
      fd = xmlWrapGzOpenUtf8(path, "rb");
  #else
      fd = gzopen(path, "rb");
  #endif
      return((void *) fd);
  }
  
  /**
   * xmlGzfileOpen:
   * @filename:  the URI for matching
   *
   * Wrapper around xmlGzfileOpen if the open fais, it will
   * try to unescape @filename
   */
  static void *
  xmlGzfileOpen (const char *filename) {
      char *unescaped;
      void *retval;
  
      retval = xmlGzfileOpen_real(filename);
      if (retval == NULL) {
  	unescaped = xmlURIUnescapeString(filename, 0, NULL);
  	if (unescaped != NULL) {
  	    retval = xmlGzfileOpen_real(unescaped);
  	}
  	xmlFree(unescaped);
      }
      return retval;
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlGzfileOpenW:
   * @filename:  the URI for matching
   * @compression:  the compression factor (0 - 9 included)
   *
   * input from compressed file open
   * if @filename is " " then the standard input is used
   *
   * Returns an I/O context or NULL in case of error
   */
  static void *
  xmlGzfileOpenW (const char *filename, int compression) {
      const char *path = NULL;
      char mode[15];
      gzFile fd;
  
      snprintf(mode, sizeof(mode), "wb%d", compression);
      if (!strcmp(filename, "-")) {
          int duped_fd = dup(fileno(stdout));
          fd = gzdopen(duped_fd, "rb");
          if (fd == Z_NULL && duped_fd >= 0) {
              close(duped_fd);  /* gzdOpen() does not close on failure */
          }
  
  	return((void *) fd);
      }
  
      if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
  #if defined (_WIN32)
  	path = &filename[17];
  #else
  	path = &filename[16];
  #endif
      else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
  #if defined (_WIN32)
  	path = &filename[8];
  #else
  	path = &filename[7];
  #endif
      } else
  	path = filename;
  
      if (path == NULL)
  	return(NULL);
  
  #if defined(_WIN32)
      fd = xmlWrapGzOpenUtf8(path, mode);
  #else
      fd = gzopen(path, mode);
  #endif
      return((void *) fd);
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /**
   * xmlGzfileRead:
   * @context:  the I/O context
   * @buffer:  where to drop data
   * @len:  number of bytes to write
   *
   * Read @len bytes to @buffer from the compressed I/O channel.
   *
   * Returns the number of bytes read.
   */
  static int
  xmlGzfileRead (void * context, char * buffer, int len) {
      int ret;
  
      ret = gzread((gzFile) context, &buffer[0], len);
      if (ret < 0) xmlIOErr(0, "gzread()");
      return(ret);
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlGzfileWrite:
   * @context:  the I/O context
   * @buffer:  where to drop data
   * @len:  number of bytes to write
   *
   * Write @len bytes from @buffer to the compressed I/O channel.
   *
   * Returns the number of bytes written
   */
  static int
  xmlGzfileWrite (void * context, const char * buffer, int len) {
      int ret;
  
      ret = gzwrite((gzFile) context, (char *) &buffer[0], len);
      if (ret < 0) xmlIOErr(0, "gzwrite()");
      return(ret);
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /**
   * xmlGzfileClose:
   * @context:  the I/O context
   *
   * Close a compressed I/O channel
   */
  static int
  xmlGzfileClose (void * context) {
      int ret;
  
      ret =  (gzclose((gzFile) context) == Z_OK ) ? 0 : -1;
      if (ret < 0) xmlIOErr(0, "gzclose()");
      return(ret);
  }
  #endif /* LIBXML_ZLIB_ENABLED */
  
  #ifdef LIBXML_LZMA_ENABLED
  /************************************************************************
   *									*
   *		I/O for compressed file accesses			*
   *									*
   ************************************************************************/
  #include "xzlib.h"
  /**
   * xmlXzfileMatch:
   * @filename:  the URI for matching
   *
   * input from compressed file test
   *
   * Returns 1 if matches, 0 otherwise
   */
  static int
  xmlXzfileMatch (const char *filename ATTRIBUTE_UNUSED) {
      return(1);
  }
  
  /**
   * xmlXzFileOpen_real:
   * @filename:  the URI for matching
   *
   * input from compressed file open
   * if @filename is " " then the standard input is used
   *
   * Returns an I/O context or NULL in case of error
   */
  static void *
  xmlXzfileOpen_real (const char *filename) {
      const char *path = NULL;
      xzFile fd;
  
      if (!strcmp(filename, "-")) {
          fd = __libxml2_xzdopen(dup(fileno(stdin)), "rb");
  	return((void *) fd);
      }
  
      if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) {
  	path = &filename[16];
      } else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
  	path = &filename[7];
      } else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:/", 6)) {
          /* lots of generators seems to lazy to read RFC 1738 */
  	path = &filename[5];
      } else
  	path = filename;
  
      if (path == NULL)
  	return(NULL);
      if (!xmlCheckFilename(path))
          return(NULL);
  
      fd = __libxml2_xzopen(path, "rb");
      return((void *) fd);
  }
  
  /**
   * xmlXzfileOpen:
   * @filename:  the URI for matching
   *
   * Wrapper around xmlXzfileOpen_real that try it with an unescaped
   * version of @filename, if this fails fallback to @filename
   *
   * Returns a handler or NULL in case or failure
   */
  static void *
  xmlXzfileOpen (const char *filename) {
      char *unescaped;
      void *retval;
  
      retval = xmlXzfileOpen_real(filename);
      if (retval == NULL) {
  	unescaped = xmlURIUnescapeString(filename, 0, NULL);
  	if (unescaped != NULL) {
  	    retval = xmlXzfileOpen_real(unescaped);
  	}
  	xmlFree(unescaped);
      }
  
      return retval;
  }
  
  /**
   * xmlXzfileRead:
   * @context:  the I/O context
   * @buffer:  where to drop data
   * @len:  number of bytes to write
   *
   * Read @len bytes to @buffer from the compressed I/O channel.
   *
   * Returns the number of bytes written
   */
  static int
  xmlXzfileRead (void * context, char * buffer, int len) {
      int ret;
  
      ret = __libxml2_xzread((xzFile) context, &buffer[0], len);
      if (ret < 0) xmlIOErr(0, "xzread()");
      return(ret);
  }
  
  /**
   * xmlXzfileClose:
   * @context:  the I/O context
   *
   * Close a compressed I/O channel
   */
  static int
  xmlXzfileClose (void * context) {
      int ret;
  
      ret =  (__libxml2_xzclose((xzFile) context) == LZMA_OK ) ? 0 : -1;
      if (ret < 0) xmlIOErr(0, "xzclose()");
      return(ret);
  }
  #endif /* LIBXML_LZMA_ENABLED */
  
  #ifdef LIBXML_HTTP_ENABLED
  /************************************************************************
   *									*
   *			I/O for HTTP file accesses			*
   *									*
   ************************************************************************/
  
  #ifdef LIBXML_OUTPUT_ENABLED
  typedef struct xmlIOHTTPWriteCtxt_
  {
      int			compression;
  
      char *		uri;
  
      void *		doc_buff;
  
  } xmlIOHTTPWriteCtxt, *xmlIOHTTPWriteCtxtPtr;
  
  #ifdef LIBXML_ZLIB_ENABLED
  
  #define DFLT_WBITS		( -15 )
  #define DFLT_MEM_LVL		( 8 )
  #define GZ_MAGIC1		( 0x1f )
  #define GZ_MAGIC2		( 0x8b )
  #define LXML_ZLIB_OS_CODE	( 0x03 )
  #define INIT_HTTP_BUFF_SIZE	( 32768 )
  #define DFLT_ZLIB_RATIO		( 5 )
  
  /*
  **  Data structure and functions to work with sending compressed data
  **  via HTTP.
  */
  
  typedef struct xmlZMemBuff_
  {
     unsigned long	size;
     unsigned long	crc;
  
     unsigned char *	zbuff;
     z_stream		zctrl;
  
  } xmlZMemBuff, *xmlZMemBuffPtr;
  
  /**
   * append_reverse_ulong
   * @buff:  Compressed memory buffer
   * @data:  Unsigned long to append
   *
   * Append a unsigned long in reverse byte order to the end of the
   * memory buffer.
   */
  static void
  append_reverse_ulong( xmlZMemBuff * buff, unsigned long data ) {
  
      int		idx;
  
      if ( buff == NULL )
  	return;
  
      /*
      **  This is plagiarized from putLong in gzio.c (zlib source) where
      **  the number "4" is hardcoded.  If zlib is ever patched to
      **  support 64 bit file sizes, this code would need to be patched
      **  as well.
      */
  
      for ( idx = 0; idx < 4; idx++ ) {
  	*buff->zctrl.next_out = ( data & 0xff );
  	data >>= 8;
  	buff->zctrl.next_out++;
      }
  
      return;
  }
  
  /**
   *
   * xmlFreeZMemBuff
   * @buff:  The memory buffer context to clear
   *
   * Release all the resources associated with the compressed memory buffer.
   */
  static void
  xmlFreeZMemBuff( xmlZMemBuffPtr buff ) {
  
  #ifdef DEBUG_HTTP
      int z_err;
  #endif
  
      if ( buff == NULL )
  	return;
  
      xmlFree( buff->zbuff );
  #ifdef DEBUG_HTTP
      z_err = deflateEnd( &buff->zctrl );
      if ( z_err != Z_OK )
  	xmlGenericError( xmlGenericErrorContext,
  			"xmlFreeZMemBuff:  Error releasing zlib context:  %d\n",
  			z_err );
  #else
      deflateEnd( &buff->zctrl );
  #endif
  
      xmlFree( buff );
      return;
  }
  
  /**
   * xmlCreateZMemBuff
   *@compression:	Compression value to use
   *
   * Create a memory buffer to hold the compressed XML document.  The
   * compressed document in memory will end up being identical to what
   * would be created if gzopen/gzwrite/gzclose were being used to
   * write the document to disk.  The code for the header/trailer data to
   * the compression is plagiarized from the zlib source files.
   */
  static void *
  xmlCreateZMemBuff( int compression ) {
  
      int			z_err;
      int			hdr_lgth;
      xmlZMemBuffPtr	buff = NULL;
  
      if ( ( compression < 1 ) || ( compression > 9 ) )
  	return ( NULL );
  
      /*  Create the control and data areas  */
  
      buff = xmlMalloc( sizeof( xmlZMemBuff ) );
      if ( buff == NULL ) {
  	xmlIOErrMemory("creating buffer context");
  	return ( NULL );
      }
  
      (void)memset( buff, 0, sizeof( xmlZMemBuff ) );
      buff->size = INIT_HTTP_BUFF_SIZE;
      buff->zbuff = xmlMalloc( buff->size );
      if ( buff->zbuff == NULL ) {
  	xmlFreeZMemBuff( buff );
  	xmlIOErrMemory("creating buffer");
  	return ( NULL );
      }
  
      z_err = deflateInit2( &buff->zctrl, compression, Z_DEFLATED,
  			    DFLT_WBITS, DFLT_MEM_LVL, Z_DEFAULT_STRATEGY );
      if ( z_err != Z_OK ) {
  	xmlChar msg[500];
  	xmlFreeZMemBuff( buff );
  	buff = NULL;
  	xmlStrPrintf(msg, 500,
  		    "xmlCreateZMemBuff:  %s %d\n",
  		    "Error initializing compression context.  ZLIB error:",
  		    z_err );
  	xmlIOErr(XML_IO_WRITE, (const char *) msg);
  	return ( NULL );
      }
  
      /*  Set the header data.  The CRC will be needed for the trailer  */
      buff->crc = crc32( 0L, NULL, 0 );
      hdr_lgth = snprintf( (char *)buff->zbuff, buff->size,
  			"%c%c%c%c%c%c%c%c%c%c",
  			GZ_MAGIC1, GZ_MAGIC2, Z_DEFLATED,
  			0, 0, 0, 0, 0, 0, LXML_ZLIB_OS_CODE );
      buff->zctrl.next_out  = buff->zbuff + hdr_lgth;
      buff->zctrl.avail_out = buff->size - hdr_lgth;
  
      return ( buff );
  }
  
  /**
   * xmlZMemBuffExtend
   * @buff:  Buffer used to compress and consolidate data.
   * @ext_amt:   Number of bytes to extend the buffer.
   *
   * Extend the internal buffer used to store the compressed data by the
   * specified amount.
   *
   * Returns 0 on success or -1 on failure to extend the buffer.  On failure
   * the original buffer still exists at the original size.
   */
  static int
  xmlZMemBuffExtend( xmlZMemBuffPtr buff, size_t ext_amt ) {
  
      int			rc = -1;
      size_t		new_size;
      size_t		cur_used;
  
      unsigned char *	tmp_ptr = NULL;
  
      if ( buff == NULL )
  	return ( -1 );
  
      else if ( ext_amt == 0 )
  	return ( 0 );
  
      cur_used = buff->zctrl.next_out - buff->zbuff;
      new_size = buff->size + ext_amt;
  
  #ifdef DEBUG_HTTP
      if ( cur_used > new_size )
  	xmlGenericError( xmlGenericErrorContext,
  			"xmlZMemBuffExtend:  %s\n%s %d bytes.\n",
  			"Buffer overwrite detected during compressed memory",
  			"buffer extension.  Overflowed by",
  			(cur_used - new_size ) );
  #endif
  
      tmp_ptr = xmlRealloc( buff->zbuff, new_size );
      if ( tmp_ptr != NULL ) {
  	rc = 0;
  	buff->size  = new_size;
  	buff->zbuff = tmp_ptr;
  	buff->zctrl.next_out  = tmp_ptr + cur_used;
  	buff->zctrl.avail_out = new_size - cur_used;
      }
      else {
  	xmlChar msg[500];
  	xmlStrPrintf(msg, 500,
  		    "xmlZMemBuffExtend:  %s %lu bytes.\n",
  		    "Allocation failure extending output buffer to",
  		    (unsigned long) new_size );
  	xmlIOErr(XML_IO_WRITE, (const char *) msg);
      }
  
      return ( rc );
  }
  
  /**
   * xmlZMemBuffAppend
   * @buff:  Buffer used to compress and consolidate data
   * @src:   Uncompressed source content to append to buffer
   * @len:   Length of source data to append to buffer
   *
   * Compress and append data to the internal buffer.  The data buffer
   * will be expanded if needed to store the additional data.
   *
   * Returns the number of bytes appended to the buffer or -1 on error.
   */
  static int
  xmlZMemBuffAppend( xmlZMemBuffPtr buff, const char * src, int len ) {
  
      int		z_err;
      size_t	min_accept;
  
      if ( ( buff == NULL ) || ( src == NULL ) )
  	return ( -1 );
  
      buff->zctrl.avail_in = len;
      buff->zctrl.next_in  = (unsigned char *)src;
      while ( buff->zctrl.avail_in > 0 ) {
  	/*
  	**  Extend the buffer prior to deflate call if a reasonable amount
  	**  of output buffer space is not available.
  	*/
  	min_accept = buff->zctrl.avail_in / DFLT_ZLIB_RATIO;
  	if ( buff->zctrl.avail_out <= min_accept ) {
  	    if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
  		return ( -1 );
  	}
  
  	z_err = deflate( &buff->zctrl, Z_NO_FLUSH );
  	if ( z_err != Z_OK ) {
  	    xmlChar msg[500];
  	    xmlStrPrintf(msg, 500,
  			"xmlZMemBuffAppend:  %s %d %s - %d",
  			"Compression error while appending",
  			len, "bytes to buffer.  ZLIB error", z_err );
  	    xmlIOErr(XML_IO_WRITE, (const char *) msg);
  	    return ( -1 );
  	}
      }
  
      buff->crc = crc32( buff->crc, (unsigned char *)src, len );
  
      return ( len );
  }
  
  /**
   * xmlZMemBuffGetContent
   * @buff:  Compressed memory content buffer
   * @data_ref:  Pointer reference to point to compressed content
   *
   * Flushes the compression buffers, appends gzip file trailers and
   * returns the compressed content and length of the compressed data.
   * NOTE:  The gzip trailer code here is plagiarized from zlib source.
   *
   * Returns the length of the compressed data or -1 on error.
   */
  static int
  xmlZMemBuffGetContent( xmlZMemBuffPtr buff, char ** data_ref ) {
  
      int		zlgth = -1;
      int		z_err;
  
      if ( ( buff == NULL ) || ( data_ref == NULL ) )
  	return ( -1 );
  
      /*  Need to loop until compression output buffers are flushed  */
  
      do
      {
  	z_err = deflate( &buff->zctrl, Z_FINISH );
  	if ( z_err == Z_OK ) {
  	    /*  In this case Z_OK means more buffer space needed  */
  
  	    if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
  		return ( -1 );
  	}
      }
      while ( z_err == Z_OK );
  
      /*  If the compression state is not Z_STREAM_END, some error occurred  */
  
      if ( z_err == Z_STREAM_END ) {
  
  	/*  Need to append the gzip data trailer  */
  
  	if ( buff->zctrl.avail_out < ( 2 * sizeof( unsigned long ) ) ) {
  	    if ( xmlZMemBuffExtend(buff, (2 * sizeof(unsigned long))) == -1 )
  		return ( -1 );
  	}
  
  	/*
  	**  For whatever reason, the CRC and length data are pushed out
  	**  in reverse byte order.  So a memcpy can't be used here.
  	*/
  
  	append_reverse_ulong( buff, buff->crc );
  	append_reverse_ulong( buff, buff->zctrl.total_in );
  
  	zlgth = buff->zctrl.next_out - buff->zbuff;
  	*data_ref = (char *)buff->zbuff;
      }
  
      else {
  	xmlChar msg[500];
  	xmlStrPrintf(msg, 500,
  		    "xmlZMemBuffGetContent:  %s - %d\n",
  		    "Error flushing zlib buffers.  Error code", z_err );
  	xmlIOErr(XML_IO_WRITE, (const char *) msg);
      }
  
      return ( zlgth );
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  #endif  /*  LIBXML_ZLIB_ENABLED  */
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlFreeHTTPWriteCtxt
   * @ctxt:  Context to cleanup
   *
   * Free allocated memory and reclaim system resources.
   *
   * No return value.
   */
  static void
  xmlFreeHTTPWriteCtxt( xmlIOHTTPWriteCtxtPtr ctxt )
  {
      if ( ctxt->uri != NULL )
  	xmlFree( ctxt->uri );
  
      if ( ctxt->doc_buff != NULL ) {
  
  #ifdef LIBXML_ZLIB_ENABLED
  	if ( ctxt->compression > 0 ) {
  	    xmlFreeZMemBuff( ctxt->doc_buff );
  	}
  	else
  #endif
  	{
  	    xmlOutputBufferClose( ctxt->doc_buff );
  	}
      }
  
      xmlFree( ctxt );
      return;
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  
  /**
   * xmlIOHTTPMatch:
   * @filename:  the URI for matching
   *
   * check if the URI matches an HTTP one
   *
   * Returns 1 if matches, 0 otherwise
   */
  int
  xmlIOHTTPMatch (const char *filename) {
      if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "http://", 7))
  	return(1);
      return(0);
  }
  
  /**
   * xmlIOHTTPOpen:
   * @filename:  the URI for matching
   *
   * open an HTTP I/O channel
   *
   * Returns an I/O context or NULL in case of error
   */
  void *
  xmlIOHTTPOpen (const char *filename) {
      return(xmlNanoHTTPOpen(filename, NULL));
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlIOHTTPOpenW:
   * @post_uri:  The destination URI for the document
   * @compression:  The compression desired for the document.
   *
   * Open a temporary buffer to collect the document for a subsequent HTTP POST
   * request.  Non-static as is called from the output buffer creation routine.
   *
   * Returns an I/O context or NULL in case of error.
   */
  
  void *
  xmlIOHTTPOpenW(const char *post_uri, int compression ATTRIBUTE_UNUSED)
  {
  
      xmlIOHTTPWriteCtxtPtr ctxt = NULL;
  
      if (post_uri == NULL)
          return (NULL);
  
      ctxt = xmlMalloc(sizeof(xmlIOHTTPWriteCtxt));
      if (ctxt == NULL) {
  	xmlIOErrMemory("creating HTTP output context");
          return (NULL);
      }
  
      (void) memset(ctxt, 0, sizeof(xmlIOHTTPWriteCtxt));
  
      ctxt->uri = (char *) xmlStrdup((const xmlChar *)post_uri);
      if (ctxt->uri == NULL) {
  	xmlIOErrMemory("copying URI");
          xmlFreeHTTPWriteCtxt(ctxt);
          return (NULL);
      }
  
      /*
       * **  Since the document length is required for an HTTP post,
       * **  need to put the document into a buffer.  A memory buffer
       * **  is being used to avoid pushing the data to disk and back.
       */
  
  #ifdef LIBXML_ZLIB_ENABLED
      if ((compression > 0) && (compression <= 9)) {
  
          ctxt->compression = compression;
          ctxt->doc_buff = xmlCreateZMemBuff(compression);
      } else
  #endif
      {
          /*  Any character conversions should have been done before this  */
  
          ctxt->doc_buff = xmlAllocOutputBufferInternal(NULL);
      }
  
      if (ctxt->doc_buff == NULL) {
          xmlFreeHTTPWriteCtxt(ctxt);
          ctxt = NULL;
      }
  
      return (ctxt);
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlIOHTTPDfltOpenW
   * @post_uri:  The destination URI for this document.
   *
   * Calls xmlIOHTTPOpenW with no compression to set up for a subsequent
   * HTTP post command.  This function should generally not be used as
   * the open callback is short circuited in xmlOutputBufferCreateFile.
   *
   * Returns a pointer to the new IO context.
   */
  static void *
  xmlIOHTTPDfltOpenW( const char * post_uri ) {
      return ( xmlIOHTTPOpenW( post_uri, 0 ) );
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /**
   * xmlIOHTTPRead:
   * @context:  the I/O context
   * @buffer:  where to drop data
   * @len:  number of bytes to write
   *
   * Read @len bytes to @buffer from the I/O channel.
   *
   * Returns the number of bytes written
   */
  int
  xmlIOHTTPRead(void * context, char * buffer, int len) {
      if ((buffer == NULL) || (len < 0)) return(-1);
      return(xmlNanoHTTPRead(context, &buffer[0], len));
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlIOHTTPWrite
   * @context:  previously opened writing context
   * @buffer:   data to output to temporary buffer
   * @len:      bytes to output
   *
   * Collect data from memory buffer into a temporary file for later
   * processing.
   *
   * Returns number of bytes written.
   */
  
  static int
  xmlIOHTTPWrite( void * context, const char * buffer, int len ) {
  
      xmlIOHTTPWriteCtxtPtr	ctxt = context;
  
      if ( ( ctxt == NULL ) || ( ctxt->doc_buff == NULL ) || ( buffer == NULL ) )
  	return ( -1 );
  
      if ( len > 0 ) {
  
  	/*  Use gzwrite or fwrite as previously setup in the open call  */
  
  #ifdef LIBXML_ZLIB_ENABLED
  	if ( ctxt->compression > 0 )
  	    len = xmlZMemBuffAppend( ctxt->doc_buff, buffer, len );
  
  	else
  #endif
  	    len = xmlOutputBufferWrite( ctxt->doc_buff, len, buffer );
  
  	if ( len < 0 ) {
  	    xmlChar msg[500];
  	    xmlStrPrintf(msg, 500,
  			"xmlIOHTTPWrite:  %s\n%s '%s'.\n",
  			"Error appending to internal buffer.",
  			"Error sending document to URI",
  			ctxt->uri );
  	    xmlIOErr(XML_IO_WRITE, (const char *) msg);
  	}
      }
  
      return ( len );
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  
  /**
   * xmlIOHTTPClose:
   * @context:  the I/O context
   *
   * Close an HTTP I/O channel
   *
   * Returns 0
   */
  int
  xmlIOHTTPClose (void * context) {
      xmlNanoHTTPClose(context);
      return 0;
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlIOHTTCloseWrite
   * @context:  The I/O context
   * @http_mthd: The HTTP method to be used when sending the data
   *
   * Close the transmit HTTP I/O channel and actually send the data.
   */
  static int
  xmlIOHTTPCloseWrite( void * context, const char * http_mthd ) {
  
      int				close_rc = -1;
      int				http_rtn = 0;
      int				content_lgth = 0;
      xmlIOHTTPWriteCtxtPtr	ctxt = context;
  
      char *			http_content = NULL;
      char *			content_encoding = NULL;
      char *			content_type = (char *) "text/xml";
      void *			http_ctxt = NULL;
  
      if ( ( ctxt == NULL ) || ( http_mthd == NULL ) )
  	return ( -1 );
  
      /*  Retrieve the content from the appropriate buffer  */
  
  #ifdef LIBXML_ZLIB_ENABLED
  
      if ( ctxt->compression > 0 ) {
  	content_lgth = xmlZMemBuffGetContent( ctxt->doc_buff, &http_content );
  	content_encoding = (char *) "Content-Encoding: gzip";
      }
      else
  #endif
      {
  	/*  Pull the data out of the memory output buffer  */
  
  	xmlOutputBufferPtr	dctxt = ctxt->doc_buff;
  	http_content = (char *) xmlBufContent(dctxt->buffer);
  	content_lgth = xmlBufUse(dctxt->buffer);
      }
  
      if ( http_content == NULL ) {
  	xmlChar msg[500];
  	xmlStrPrintf(msg, 500,
  		     "xmlIOHTTPCloseWrite:  %s '%s' %s '%s'.\n",
  		     "Error retrieving content.\nUnable to",
  		     http_mthd, "data to URI", ctxt->uri );
  	xmlIOErr(XML_IO_WRITE, (const char *) msg);
      }
  
      else {
  
  	http_ctxt = xmlNanoHTTPMethod( ctxt->uri, http_mthd, http_content,
  					&content_type, content_encoding,
  					content_lgth );
  
  	if ( http_ctxt != NULL ) {
  #ifdef DEBUG_HTTP
  	    /*  If testing/debugging - dump reply with request content  */
  
  	    FILE *	tst_file = NULL;
  	    char	buffer[ 4096 ];
  	    char *	dump_name = NULL;
  	    int		avail;
  
  	    xmlGenericError( xmlGenericErrorContext,
  			"xmlNanoHTTPCloseWrite:  HTTP %s to\n%s returned %d.\n",
  			http_mthd, ctxt->uri,
  			xmlNanoHTTPReturnCode( http_ctxt ) );
  
  	    /*
  	    **  Since either content or reply may be gzipped,
  	    **  dump them to separate files instead of the
  	    **  standard error context.
  	    */
  
  	    dump_name = tempnam( NULL, "lxml" );
  	    if ( dump_name != NULL ) {
  		(void)snprintf( buffer, sizeof(buffer), "%s.content", dump_name );
  
  		tst_file = fopen( buffer, "wb" );
  		if ( tst_file != NULL ) {
  		    xmlGenericError( xmlGenericErrorContext,
  			"Transmitted content saved in file:  %s\n", buffer );
  
  		    fwrite( http_content, sizeof( char ),
  					content_lgth, tst_file );
  		    fclose( tst_file );
  		}
  
  		(void)snprintf( buffer, sizeof(buffer), "%s.reply", dump_name );
  		tst_file = fopen( buffer, "wb" );
  		if ( tst_file != NULL ) {
  		    xmlGenericError( xmlGenericErrorContext,
  			"Reply content saved in file:  %s\n", buffer );
  
  
  		    while ( (avail = xmlNanoHTTPRead( http_ctxt,
  					buffer, sizeof( buffer ) )) > 0 ) {
  
  			fwrite( buffer, sizeof( char ), avail, tst_file );
  		    }
  
  		    fclose( tst_file );
  		}
  
  		free( dump_name );
  	    }
  #endif  /*  DEBUG_HTTP  */
  
  	    http_rtn = xmlNanoHTTPReturnCode( http_ctxt );
  	    if ( ( http_rtn >= 200 ) && ( http_rtn < 300 ) )
  		close_rc = 0;
  	    else {
                  xmlChar msg[500];
                  xmlStrPrintf(msg, 500,
                        "xmlIOHTTPCloseWrite: HTTP '%s' of %d %s\n'%s' %s %d\n",
  			    http_mthd, content_lgth,
  			    "bytes to URI", ctxt->uri,
  			    "failed.  HTTP return code:", http_rtn );
  		xmlIOErr(XML_IO_WRITE, (const char *) msg);
              }
  
  	    xmlNanoHTTPClose( http_ctxt );
  	    xmlFree( content_type );
  	}
      }
  
      /*  Final cleanups  */
  
      xmlFreeHTTPWriteCtxt( ctxt );
  
      return ( close_rc );
  }
  
  /**
   * xmlIOHTTPClosePut
   *
   * @context:  The I/O context
   *
   * Close the transmit HTTP I/O channel and actually send data using a PUT
   * HTTP method.
   */
  static int
  xmlIOHTTPClosePut( void * ctxt ) {
      return ( xmlIOHTTPCloseWrite( ctxt, "PUT" ) );
  }
  
  
  /**
   * xmlIOHTTPClosePost
   *
   * @context:  The I/O context
   *
   * Close the transmit HTTP I/O channel and actually send data using a POST
   * HTTP method.
   */
  static int
  xmlIOHTTPClosePost( void * ctxt ) {
      return ( xmlIOHTTPCloseWrite( ctxt, "POST" ) );
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  #endif /* LIBXML_HTTP_ENABLED */
  
  #ifdef LIBXML_FTP_ENABLED
  /************************************************************************
   *									*
   *			I/O for FTP file accesses			*
   *									*
   ************************************************************************/
  /**
   * xmlIOFTPMatch:
   * @filename:  the URI for matching
   *
   * check if the URI matches an FTP one
   *
   * Returns 1 if matches, 0 otherwise
   */
  int
  xmlIOFTPMatch (const char *filename) {
      if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "ftp://", 6))
  	return(1);
      return(0);
  }
  
  /**
   * xmlIOFTPOpen:
   * @filename:  the URI for matching
   *
   * open an FTP I/O channel
   *
   * Returns an I/O context or NULL in case of error
   */
  void *
  xmlIOFTPOpen (const char *filename) {
      return(xmlNanoFTPOpen(filename));
  }
  
  /**
   * xmlIOFTPRead:
   * @context:  the I/O context
   * @buffer:  where to drop data
   * @len:  number of bytes to write
   *
   * Read @len bytes to @buffer from the I/O channel.
   *
   * Returns the number of bytes written
   */
  int
  xmlIOFTPRead(void * context, char * buffer, int len) {
      if ((buffer == NULL) || (len < 0)) return(-1);
      return(xmlNanoFTPRead(context, &buffer[0], len));
  }
  
  /**
   * xmlIOFTPClose:
   * @context:  the I/O context
   *
   * Close an FTP I/O channel
   *
   * Returns 0
   */
  int
  xmlIOFTPClose (void * context) {
      return ( xmlNanoFTPClose(context) );
  }
  #endif /* LIBXML_FTP_ENABLED */
  
  
  /**
   * xmlRegisterInputCallbacks:
   * @matchFunc:  the xmlInputMatchCallback
   * @openFunc:  the xmlInputOpenCallback
   * @readFunc:  the xmlInputReadCallback
   * @closeFunc:  the xmlInputCloseCallback
   *
   * Register a new set of I/O callback for handling parser input.
   *
   * Returns the registered handler number or -1 in case of error
   */
  int
  xmlRegisterInputCallbacks(xmlInputMatchCallback matchFunc,
  	xmlInputOpenCallback openFunc, xmlInputReadCallback readFunc,
  	xmlInputCloseCallback closeFunc) {
      if (xmlInputCallbackNr >= MAX_INPUT_CALLBACK) {
  	return(-1);
      }
      xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = matchFunc;
      xmlInputCallbackTable[xmlInputCallbackNr].opencallback = openFunc;
      xmlInputCallbackTable[xmlInputCallbackNr].readcallback = readFunc;
      xmlInputCallbackTable[xmlInputCallbackNr].closecallback = closeFunc;
      xmlInputCallbackInitialized = 1;
      return(xmlInputCallbackNr++);
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlRegisterOutputCallbacks:
   * @matchFunc:  the xmlOutputMatchCallback
   * @openFunc:  the xmlOutputOpenCallback
   * @writeFunc:  the xmlOutputWriteCallback
   * @closeFunc:  the xmlOutputCloseCallback
   *
   * Register a new set of I/O callback for handling output.
   *
   * Returns the registered handler number or -1 in case of error
   */
  int
  xmlRegisterOutputCallbacks(xmlOutputMatchCallback matchFunc,
  	xmlOutputOpenCallback openFunc, xmlOutputWriteCallback writeFunc,
  	xmlOutputCloseCallback closeFunc) {
      if (xmlOutputCallbackNr >= MAX_OUTPUT_CALLBACK) {
  	return(-1);
      }
      xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = matchFunc;
      xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = openFunc;
      xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = writeFunc;
      xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = closeFunc;
      xmlOutputCallbackInitialized = 1;
      return(xmlOutputCallbackNr++);
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /**
   * xmlRegisterDefaultInputCallbacks:
   *
   * Registers the default compiled-in I/O handlers.
   */
  void
  xmlRegisterDefaultInputCallbacks(void) {
      if (xmlInputCallbackInitialized)
  	return;
  
      xmlRegisterInputCallbacks(xmlFileMatch, xmlFileOpen,
  	                      xmlFileRead, xmlFileClose);
  #ifdef LIBXML_ZLIB_ENABLED
      xmlRegisterInputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
  	                      xmlGzfileRead, xmlGzfileClose);
  #endif /* LIBXML_ZLIB_ENABLED */
  #ifdef LIBXML_LZMA_ENABLED
      xmlRegisterInputCallbacks(xmlXzfileMatch, xmlXzfileOpen,
  	                      xmlXzfileRead, xmlXzfileClose);
  #endif /* LIBXML_LZMA_ENABLED */
  
  #ifdef LIBXML_HTTP_ENABLED
      xmlRegisterInputCallbacks(xmlIOHTTPMatch, xmlIOHTTPOpen,
  	                      xmlIOHTTPRead, xmlIOHTTPClose);
  #endif /* LIBXML_HTTP_ENABLED */
  
  #ifdef LIBXML_FTP_ENABLED
      xmlRegisterInputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
  	                      xmlIOFTPRead, xmlIOFTPClose);
  #endif /* LIBXML_FTP_ENABLED */
      xmlInputCallbackInitialized = 1;
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlRegisterDefaultOutputCallbacks:
   *
   * Registers the default compiled-in I/O handlers.
   */
  void
  xmlRegisterDefaultOutputCallbacks (void) {
      if (xmlOutputCallbackInitialized)
  	return;
  
      xmlRegisterOutputCallbacks(xmlFileMatch, xmlFileOpenW,
  	                      xmlFileWrite, xmlFileClose);
  
  #ifdef LIBXML_HTTP_ENABLED
      xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
  	                       xmlIOHTTPWrite, xmlIOHTTPClosePut);
  #endif
  
  /*********************************
   No way a-priori to distinguish between gzipped files from
   uncompressed ones except opening if existing then closing
   and saving with same compression ratio ... a pain.
  
  #ifdef LIBXML_ZLIB_ENABLED
      xmlRegisterOutputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
  	                       xmlGzfileWrite, xmlGzfileClose);
  #endif
  
   Nor FTP PUT ....
  #ifdef LIBXML_FTP_ENABLED
      xmlRegisterOutputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
  	                       xmlIOFTPWrite, xmlIOFTPClose);
  #endif
   **********************************/
      xmlOutputCallbackInitialized = 1;
  }
  
  #ifdef LIBXML_HTTP_ENABLED
  /**
   * xmlRegisterHTTPPostCallbacks:
   *
   * By default, libxml submits HTTP output requests using the "PUT" method.
   * Calling this method changes the HTTP output method to use the "POST"
   * method instead.
   *
   */
  void
  xmlRegisterHTTPPostCallbacks( void ) {
  
      /*  Register defaults if not done previously  */
  
      if ( xmlOutputCallbackInitialized == 0 )
  	xmlRegisterDefaultOutputCallbacks( );
  
      xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
  	                       xmlIOHTTPWrite, xmlIOHTTPClosePost);
      return;
  }
  #endif
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /**
   * xmlAllocParserInputBuffer:
   * @enc:  the charset encoding if known
   *
   * Create a buffered parser input for progressive parsing
   *
   * Returns the new parser input or NULL
   */
  xmlParserInputBufferPtr
  xmlAllocParserInputBuffer(xmlCharEncoding enc) {
      xmlParserInputBufferPtr ret;
  
      ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer));
      if (ret == NULL) {
  	xmlIOErrMemory("creating input buffer");
  	return(NULL);
      }
      memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer));
      ret->buffer = xmlBufCreateSize(2 * xmlDefaultBufferSize);
      if (ret->buffer == NULL) {
          xmlFree(ret);
  	return(NULL);
      }
      xmlBufSetAllocationScheme(ret->buffer, XML_BUFFER_ALLOC_DOUBLEIT);
      ret->encoder = xmlGetCharEncodingHandler(enc);
      if (ret->encoder != NULL)
          ret->raw = xmlBufCreateSize(2 * xmlDefaultBufferSize);
      else
          ret->raw = NULL;
      ret->readcallback = NULL;
      ret->closecallback = NULL;
      ret->context = NULL;
      ret->compressed = -1;
      ret->rawconsumed = 0;
  
      return(ret);
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlAllocOutputBuffer:
   * @encoder:  the encoding converter or NULL
   *
   * Create a buffered parser output
   *
   * Returns the new parser output or NULL
   */
  xmlOutputBufferPtr
  xmlAllocOutputBuffer(xmlCharEncodingHandlerPtr encoder) {
      xmlOutputBufferPtr ret;
  
      ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
      if (ret == NULL) {
  	xmlIOErrMemory("creating output buffer");
  	return(NULL);
      }
      memset(ret, 0, (size_t) sizeof(xmlOutputBuffer));
      ret->buffer = xmlBufCreate();
      if (ret->buffer == NULL) {
          xmlFree(ret);
  	return(NULL);
      }
  
      /* try to avoid a performance problem with Windows realloc() */
      if (xmlBufGetAllocationScheme(ret->buffer) == XML_BUFFER_ALLOC_EXACT)
          xmlBufSetAllocationScheme(ret->buffer, XML_BUFFER_ALLOC_DOUBLEIT);
  
      ret->encoder = encoder;
      if (encoder != NULL) {
          ret->conv = xmlBufCreateSize(4000);
  	if (ret->conv == NULL) {
              xmlBufFree(ret->buffer);
  	    xmlFree(ret);
  	    return(NULL);
  	}
  
  	/*
  	 * This call is designed to initiate the encoder state
  	 */
  	xmlCharEncOutput(ret, 1);
      } else
          ret->conv = NULL;
      ret->writecallback = NULL;
      ret->closecallback = NULL;
      ret->context = NULL;
      ret->written = 0;
  
      return(ret);
  }
  
  /**
   * xmlAllocOutputBufferInternal:
   * @encoder:  the encoding converter or NULL
   *
   * Create a buffered parser output
   *
   * Returns the new parser output or NULL
   */
  xmlOutputBufferPtr
  xmlAllocOutputBufferInternal(xmlCharEncodingHandlerPtr encoder) {
      xmlOutputBufferPtr ret;
  
      ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
      if (ret == NULL) {
  	xmlIOErrMemory("creating output buffer");
  	return(NULL);
      }
      memset(ret, 0, (size_t) sizeof(xmlOutputBuffer));
      ret->buffer = xmlBufCreate();
      if (ret->buffer == NULL) {
          xmlFree(ret);
  	return(NULL);
      }
  
  
      /*
       * For conversion buffers we use the special IO handling
       */
      xmlBufSetAllocationScheme(ret->buffer, XML_BUFFER_ALLOC_IO);
  
      ret->encoder = encoder;
      if (encoder != NULL) {
          ret->conv = xmlBufCreateSize(4000);
  	if (ret->conv == NULL) {
              xmlBufFree(ret->buffer);
  	    xmlFree(ret);
  	    return(NULL);
  	}
  
  	/*
  	 * This call is designed to initiate the encoder state
  	 */
          xmlCharEncOutput(ret, 1);
      } else
          ret->conv = NULL;
      ret->writecallback = NULL;
      ret->closecallback = NULL;
      ret->context = NULL;
      ret->written = 0;
  
      return(ret);
  }
  
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /**
   * xmlFreeParserInputBuffer:
   * @in:  a buffered parser input
   *
   * Free up the memory used by a buffered parser input
   */
  void
  xmlFreeParserInputBuffer(xmlParserInputBufferPtr in) {
      if (in == NULL) return;
  
      if (in->raw) {
          xmlBufFree(in->raw);
  	in->raw = NULL;
      }
      if (in->encoder != NULL) {
          xmlCharEncCloseFunc(in->encoder);
      }
      if (in->closecallback != NULL) {
  	in->closecallback(in->context);
      }
      if (in->buffer != NULL) {
          xmlBufFree(in->buffer);
  	in->buffer = NULL;
      }
  
      xmlFree(in);
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlOutputBufferClose:
   * @out:  a buffered output
   *
   * flushes and close the output I/O channel
   * and free up all the associated resources
   *
   * Returns the number of byte written or -1 in case of error.
   */
  int
  xmlOutputBufferClose(xmlOutputBufferPtr out)
  {
      int written;
      int err_rc = 0;
  
      if (out == NULL)
          return (-1);
      if (out->writecallback != NULL)
          xmlOutputBufferFlush(out);
      if (out->closecallback != NULL) {
          err_rc = out->closecallback(out->context);
      }
      written = out->written;
      if (out->conv) {
          xmlBufFree(out->conv);
          out->conv = NULL;
      }
      if (out->encoder != NULL) {
          xmlCharEncCloseFunc(out->encoder);
      }
      if (out->buffer != NULL) {
          xmlBufFree(out->buffer);
          out->buffer = NULL;
      }
  
      if (out->error)
          err_rc = -1;
      xmlFree(out);
      return ((err_rc == 0) ? written : err_rc);
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  xmlParserInputBufferPtr
  __xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) {
      xmlParserInputBufferPtr ret;
      int i = 0;
      void *context = NULL;
  
      if (xmlInputCallbackInitialized == 0)
  	xmlRegisterDefaultInputCallbacks();
  
      if (URI == NULL) return(NULL);
  
      /*
       * Try to find one of the input accept method accepting that scheme
       * Go in reverse to give precedence to user defined handlers.
       */
      if (context == NULL) {
  	for (i = xmlInputCallbackNr - 1;i >= 0;i--) {
  	    if ((xmlInputCallbackTable[i].matchcallback != NULL) &&
  		(xmlInputCallbackTable[i].matchcallback(URI) != 0)) {
  		context = xmlInputCallbackTable[i].opencallback(URI);
  		if (context != NULL) {
  		    break;
  		}
  	    }
  	}
      }
      if (context == NULL) {
  	return(NULL);
      }
  
      /*
       * Allocate the Input buffer front-end.
       */
      ret = xmlAllocParserInputBuffer(enc);
      if (ret != NULL) {
  	ret->context = context;
  	ret->readcallback = xmlInputCallbackTable[i].readcallback;
  	ret->closecallback = xmlInputCallbackTable[i].closecallback;
  #ifdef LIBXML_ZLIB_ENABLED
  	if ((xmlInputCallbackTable[i].opencallback == xmlGzfileOpen) &&
  		(strcmp(URI, "-") != 0)) {
  #if defined(ZLIB_VERNUM) && ZLIB_VERNUM >= 0x1230
              ret->compressed = !gzdirect(context);
  #else
  	    if (((z_stream *)context)->avail_in > 4) {
  	        char *cptr, buff4[4];
  		cptr = (char *) ((z_stream *)context)->next_in;
  		if (gzread(context, buff4, 4) == 4) {
  		    if (strncmp(buff4, cptr, 4) == 0)
  		        ret->compressed = 0;
  		    else
  		        ret->compressed = 1;
  		    gzrewind(context);
  		}
  	    }
  #endif
  	}
  #endif
  #ifdef LIBXML_LZMA_ENABLED
  	if ((xmlInputCallbackTable[i].opencallback == xmlXzfileOpen) &&
  		(strcmp(URI, "-") != 0)) {
              ret->compressed = __libxml2_xzcompressed(context);
  	}
  #endif
      }
      else
        xmlInputCallbackTable[i].closecallback (context);
  
      return(ret);
  }
  
  /**
   * xmlParserInputBufferCreateFilename:
   * @URI:  a C string containing the URI or filename
   * @enc:  the charset encoding if known
   *
   * Create a buffered parser input for the progressive parsing of a file
   * If filename is "-' then we use stdin as the input.
   * Automatic support for ZLIB/Compress compressed document is provided
   * by default if found at compile-time.
   * Do an encoding check if enc == XML_CHAR_ENCODING_NONE
   *
   * Returns the new parser input or NULL
   */
  xmlParserInputBufferPtr
  xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) {
      if ((xmlParserInputBufferCreateFilenameValue)) {
  		return xmlParserInputBufferCreateFilenameValue(URI, enc);
  	}
  	return __xmlParserInputBufferCreateFilename(URI, enc);
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  xmlOutputBufferPtr
  __xmlOutputBufferCreateFilename(const char *URI,
                                xmlCharEncodingHandlerPtr encoder,
                                int compression ATTRIBUTE_UNUSED) {
      xmlOutputBufferPtr ret;
      xmlURIPtr puri;
      int i = 0;
      void *context = NULL;
      char *unescaped = NULL;
  #ifdef LIBXML_ZLIB_ENABLED
      int is_file_uri = 1;
  #endif
  
      if (xmlOutputCallbackInitialized == 0)
  	xmlRegisterDefaultOutputCallbacks();
  
      if (URI == NULL) return(NULL);
  
      puri = xmlParseURI(URI);
      if (puri != NULL) {
  #ifdef LIBXML_ZLIB_ENABLED
          if ((puri->scheme != NULL) &&
  	    (!xmlStrEqual(BAD_CAST puri->scheme, BAD_CAST "file")))
  	    is_file_uri = 0;
  #endif
  	/*
  	 * try to limit the damages of the URI unescaping code.
  	 */
  	if ((puri->scheme == NULL) ||
  	    (xmlStrEqual(BAD_CAST puri->scheme, BAD_CAST "file")))
  	    unescaped = xmlURIUnescapeString(URI, 0, NULL);
  	xmlFreeURI(puri);
      }
  
      /*
       * Try to find one of the output accept method accepting that scheme
       * Go in reverse to give precedence to user defined handlers.
       * try with an unescaped version of the URI
       */
      if (unescaped != NULL) {
  #ifdef LIBXML_ZLIB_ENABLED
  	if ((compression > 0) && (compression <= 9) && (is_file_uri == 1)) {
  	    context = xmlGzfileOpenW(unescaped, compression);
  	    if (context != NULL) {
  		ret = xmlAllocOutputBufferInternal(encoder);
  		if (ret != NULL) {
  		    ret->context = context;
  		    ret->writecallback = xmlGzfileWrite;
  		    ret->closecallback = xmlGzfileClose;
  		}
  		xmlFree(unescaped);
  		return(ret);
  	    }
  	}
  #endif
  	for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
  	    if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
  		(xmlOutputCallbackTable[i].matchcallback(unescaped) != 0)) {
  #if defined(LIBXML_HTTP_ENABLED) && defined(LIBXML_ZLIB_ENABLED)
  		/*  Need to pass compression parameter into HTTP open calls  */
  		if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
  		    context = xmlIOHTTPOpenW(unescaped, compression);
  		else
  #endif
  		    context = xmlOutputCallbackTable[i].opencallback(unescaped);
  		if (context != NULL)
  		    break;
  	    }
  	}
  	xmlFree(unescaped);
      }
  
      /*
       * If this failed try with a non-escaped URI this may be a strange
       * filename
       */
      if (context == NULL) {
  #ifdef LIBXML_ZLIB_ENABLED
  	if ((compression > 0) && (compression <= 9) && (is_file_uri == 1)) {
  	    context = xmlGzfileOpenW(URI, compression);
  	    if (context != NULL) {
  		ret = xmlAllocOutputBufferInternal(encoder);
  		if (ret != NULL) {
  		    ret->context = context;
  		    ret->writecallback = xmlGzfileWrite;
  		    ret->closecallback = xmlGzfileClose;
  		}
  		else
  		    xmlGzfileClose(context);
  		return(ret);
  	    }
  	}
  #endif
  	for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
  	    if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
  		(xmlOutputCallbackTable[i].matchcallback(URI) != 0)) {
  #if defined(LIBXML_HTTP_ENABLED) && defined(LIBXML_ZLIB_ENABLED)
  		/*  Need to pass compression parameter into HTTP open calls  */
  		if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
  		    context = xmlIOHTTPOpenW(URI, compression);
  		else
  #endif
  		    context = xmlOutputCallbackTable[i].opencallback(URI);
  		if (context != NULL)
  		    break;
  	    }
  	}
      }
  
      if (context == NULL) {
  	return(NULL);
      }
  
      /*
       * Allocate the Output buffer front-end.
       */
      ret = xmlAllocOutputBufferInternal(encoder);
      if (ret != NULL) {
  	ret->context = context;
  	ret->writecallback = xmlOutputCallbackTable[i].writecallback;
  	ret->closecallback = xmlOutputCallbackTable[i].closecallback;
      }
      return(ret);
  }
  
  /**
   * xmlOutputBufferCreateFilename:
   * @URI:  a C string containing the URI or filename
   * @encoder:  the encoding converter or NULL
   * @compression:  the compression ration (0 none, 9 max).
   *
   * Create a buffered  output for the progressive saving of a file
   * If filename is "-' then we use stdout as the output.
   * Automatic support for ZLIB/Compress compressed document is provided
   * by default if found at compile-time.
   * TODO: currently if compression is set, the library only support
   *       writing to a local file.
   *
   * Returns the new output or NULL
   */
  xmlOutputBufferPtr
  xmlOutputBufferCreateFilename(const char *URI,
                                xmlCharEncodingHandlerPtr encoder,
                                int compression ATTRIBUTE_UNUSED) {
      if ((xmlOutputBufferCreateFilenameValue)) {
  		return xmlOutputBufferCreateFilenameValue(URI, encoder, compression);
  	}
  	return __xmlOutputBufferCreateFilename(URI, encoder, compression);
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /**
   * xmlParserInputBufferCreateFile:
   * @file:  a FILE*
   * @enc:  the charset encoding if known
   *
   * Create a buffered parser input for the progressive parsing of a FILE *
   * buffered C I/O
   *
   * Returns the new parser input or NULL
   */
  xmlParserInputBufferPtr
  xmlParserInputBufferCreateFile(FILE *file, xmlCharEncoding enc) {
      xmlParserInputBufferPtr ret;
  
      if (xmlInputCallbackInitialized == 0)
  	xmlRegisterDefaultInputCallbacks();
  
      if (file == NULL) return(NULL);
  
      ret = xmlAllocParserInputBuffer(enc);
      if (ret != NULL) {
          ret->context = file;
  	ret->readcallback = xmlFileRead;
  	ret->closecallback = xmlFileFlush;
      }
  
      return(ret);
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlOutputBufferCreateFile:
   * @file:  a FILE*
   * @encoder:  the encoding converter or NULL
   *
   * Create a buffered output for the progressive saving to a FILE *
   * buffered C I/O
   *
   * Returns the new parser output or NULL
   */
  xmlOutputBufferPtr
  xmlOutputBufferCreateFile(FILE *file, xmlCharEncodingHandlerPtr encoder) {
      xmlOutputBufferPtr ret;
  
      if (xmlOutputCallbackInitialized == 0)
  	xmlRegisterDefaultOutputCallbacks();
  
      if (file == NULL) return(NULL);
  
      ret = xmlAllocOutputBufferInternal(encoder);
      if (ret != NULL) {
          ret->context = file;
  	ret->writecallback = xmlFileWrite;
  	ret->closecallback = xmlFileFlush;
      }
  
      return(ret);
  }
  
  /**
   * xmlOutputBufferCreateBuffer:
   * @buffer:  a xmlBufferPtr
   * @encoder:  the encoding converter or NULL
   *
   * Create a buffered output for the progressive saving to a xmlBuffer
   *
   * Returns the new parser output or NULL
   */
  xmlOutputBufferPtr
  xmlOutputBufferCreateBuffer(xmlBufferPtr buffer,
                              xmlCharEncodingHandlerPtr encoder) {
      xmlOutputBufferPtr ret;
  
      if (buffer == NULL) return(NULL);
  
      ret = xmlOutputBufferCreateIO(xmlBufferWrite, NULL, (void *) buffer,
                                    encoder);
  
      return(ret);
  }
  
  /**
   * xmlOutputBufferGetContent:
   * @out:  an xmlOutputBufferPtr
   *
   * Gives a pointer to the data currently held in the output buffer
   *
   * Returns a pointer to the data or NULL in case of error
   */
  const xmlChar *
  xmlOutputBufferGetContent(xmlOutputBufferPtr out) {
      if ((out == NULL) || (out->buffer == NULL))
          return(NULL);
  
      return(xmlBufContent(out->buffer));
  }
  
  /**
   * xmlOutputBufferGetSize:
   * @out:  an xmlOutputBufferPtr
   *
   * Gives the length of the data currently held in the output buffer
   *
   * Returns 0 in case or error or no data is held, the size otherwise
   */
  size_t
  xmlOutputBufferGetSize(xmlOutputBufferPtr out) {
      if ((out == NULL) || (out->buffer == NULL))
          return(0);
  
      return(xmlBufUse(out->buffer));
  }
  
  
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /**
   * xmlParserInputBufferCreateFd:
   * @fd:  a file descriptor number
   * @enc:  the charset encoding if known
   *
   * Create a buffered parser input for the progressive parsing for the input
   * from a file descriptor
   *
   * Returns the new parser input or NULL
   */
  xmlParserInputBufferPtr
  xmlParserInputBufferCreateFd(int fd, xmlCharEncoding enc) {
      xmlParserInputBufferPtr ret;
  
      if (fd < 0) return(NULL);
  
      ret = xmlAllocParserInputBuffer(enc);
      if (ret != NULL) {
          ret->context = (void *) (ptrdiff_t) fd;
  	ret->readcallback = xmlFdRead;
  	ret->closecallback = xmlFdClose;
      }
  
      return(ret);
  }
  
  /**
   * xmlParserInputBufferCreateMem:
   * @mem:  the memory input
   * @size:  the length of the memory block
   * @enc:  the charset encoding if known
   *
   * Create a buffered parser input for the progressive parsing for the input
   * from a memory area.
   *
   * Returns the new parser input or NULL
   */
  xmlParserInputBufferPtr
  xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) {
      xmlParserInputBufferPtr ret;
      int errcode;
  
      if (size < 0) return(NULL);
      if (mem == NULL) return(NULL);
  
      ret = xmlAllocParserInputBuffer(enc);
      if (ret != NULL) {
          ret->context = (void *) mem;
  	ret->readcallback = xmlInputReadCallbackNop;
  	ret->closecallback = NULL;
  	errcode = xmlBufAdd(ret->buffer, (const xmlChar *) mem, size);
  	if (errcode != 0) {
  	    xmlFree(ret);
  	    return(NULL);
  	}
      }
  
      return(ret);
  }
  
  /**
   * xmlParserInputBufferCreateStatic:
   * @mem:  the memory input
   * @size:  the length of the memory block
   * @enc:  the charset encoding if known
   *
   * Create a buffered parser input for the progressive parsing for the input
   * from an immutable memory area. This will not copy the memory area to
   * the buffer, but the memory is expected to be available until the end of
   * the parsing, this is useful for example when using mmap'ed file.
   *
   * Returns the new parser input or NULL
   */
  xmlParserInputBufferPtr
  xmlParserInputBufferCreateStatic(const char *mem, int size,
                                   xmlCharEncoding enc) {
      xmlParserInputBufferPtr ret;
  
      if (size < 0) return(NULL);
      if (mem == NULL) return(NULL);
  
      ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer));
      if (ret == NULL) {
  	xmlIOErrMemory("creating input buffer");
  	return(NULL);
      }
      memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer));
      ret->buffer = xmlBufCreateStatic((void *)mem, (size_t) size);
      if (ret->buffer == NULL) {
          xmlFree(ret);
  	return(NULL);
      }
      ret->encoder = xmlGetCharEncodingHandler(enc);
      if (ret->encoder != NULL)
          ret->raw = xmlBufCreateSize(2 * xmlDefaultBufferSize);
      else
          ret->raw = NULL;
      ret->compressed = -1;
      ret->context = (void *) mem;
      ret->readcallback = NULL;
      ret->closecallback = NULL;
  
      return(ret);
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlOutputBufferCreateFd:
   * @fd:  a file descriptor number
   * @encoder:  the encoding converter or NULL
   *
   * Create a buffered output for the progressive saving
   * to a file descriptor
   *
   * Returns the new parser output or NULL
   */
  xmlOutputBufferPtr
  xmlOutputBufferCreateFd(int fd, xmlCharEncodingHandlerPtr encoder) {
      xmlOutputBufferPtr ret;
  
      if (fd < 0) return(NULL);
  
      ret = xmlAllocOutputBufferInternal(encoder);
      if (ret != NULL) {
          ret->context = (void *) (ptrdiff_t) fd;
  	ret->writecallback = xmlFdWrite;
  	ret->closecallback = NULL;
      }
  
      return(ret);
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /**
   * xmlParserInputBufferCreateIO:
   * @ioread:  an I/O read function
   * @ioclose:  an I/O close function
   * @ioctx:  an I/O handler
   * @enc:  the charset encoding if known
   *
   * Create a buffered parser input for the progressive parsing for the input
   * from an I/O handler
   *
   * Returns the new parser input or NULL
   */
  xmlParserInputBufferPtr
  xmlParserInputBufferCreateIO(xmlInputReadCallback   ioread,
  	 xmlInputCloseCallback  ioclose, void *ioctx, xmlCharEncoding enc) {
      xmlParserInputBufferPtr ret;
  
      if (ioread == NULL) return(NULL);
  
      ret = xmlAllocParserInputBuffer(enc);
      if (ret != NULL) {
          ret->context = (void *) ioctx;
  	ret->readcallback = ioread;
  	ret->closecallback = ioclose;
      }
  
      return(ret);
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlOutputBufferCreateIO:
   * @iowrite:  an I/O write function
   * @ioclose:  an I/O close function
   * @ioctx:  an I/O handler
   * @encoder:  the charset encoding if known
   *
   * Create a buffered output for the progressive saving
   * to an I/O handler
   *
   * Returns the new parser output or NULL
   */
  xmlOutputBufferPtr
  xmlOutputBufferCreateIO(xmlOutputWriteCallback   iowrite,
  	 xmlOutputCloseCallback  ioclose, void *ioctx,
  	 xmlCharEncodingHandlerPtr encoder) {
      xmlOutputBufferPtr ret;
  
      if (iowrite == NULL) return(NULL);
  
      ret = xmlAllocOutputBufferInternal(encoder);
      if (ret != NULL) {
          ret->context = (void *) ioctx;
  	ret->writecallback = iowrite;
  	ret->closecallback = ioclose;
      }
  
      return(ret);
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /**
   * xmlParserInputBufferCreateFilenameDefault:
   * @func: function pointer to the new ParserInputBufferCreateFilenameFunc
   *
   * Registers a callback for URI input file handling
   *
   * Returns the old value of the registration function
   */
  xmlParserInputBufferCreateFilenameFunc
  xmlParserInputBufferCreateFilenameDefault(xmlParserInputBufferCreateFilenameFunc func)
  {
      xmlParserInputBufferCreateFilenameFunc old = xmlParserInputBufferCreateFilenameValue;
      if (old == NULL) {
  		old = __xmlParserInputBufferCreateFilename;
  	}
  
      xmlParserInputBufferCreateFilenameValue = func;
      return(old);
  }
  
  /**
   * xmlOutputBufferCreateFilenameDefault:
   * @func: function pointer to the new OutputBufferCreateFilenameFunc
   *
   * Registers a callback for URI output file handling
   *
   * Returns the old value of the registration function
   */
  xmlOutputBufferCreateFilenameFunc
  xmlOutputBufferCreateFilenameDefault(xmlOutputBufferCreateFilenameFunc func)
  {
      xmlOutputBufferCreateFilenameFunc old = xmlOutputBufferCreateFilenameValue;
  #ifdef LIBXML_OUTPUT_ENABLED
      if (old == NULL) {
  		old = __xmlOutputBufferCreateFilename;
  	}
  #endif
      xmlOutputBufferCreateFilenameValue = func;
      return(old);
  }
  
  /**
   * xmlParserInputBufferPush:
   * @in:  a buffered parser input
   * @len:  the size in bytes of the array.
   * @buf:  an char array
   *
   * Push the content of the arry in the input buffer
   * This routine handle the I18N transcoding to internal UTF-8
   * This is used when operating the parser in progressive (push) mode.
   *
   * Returns the number of chars read and stored in the buffer, or -1
   *         in case of error.
   */
  int
  xmlParserInputBufferPush(xmlParserInputBufferPtr in,
  	                 int len, const char *buf) {
      int nbchars = 0;
      int ret;
  
      if (len < 0) return(0);
      if ((in == NULL) || (in->error)) return(-1);
      if (in->encoder != NULL) {
          unsigned int use;
  
          /*
  	 * Store the data in the incoming raw buffer
  	 */
          if (in->raw == NULL) {
  	    in->raw = xmlBufCreate();
  	}
  	ret = xmlBufAdd(in->raw, (const xmlChar *) buf, len);
  	if (ret != 0)
  	    return(-1);
  
  	/*
  	 * convert as much as possible to the parser reading buffer.
  	 */
  	use = xmlBufUse(in->raw);
  	nbchars = xmlCharEncInput(in, 1);
  	if (nbchars < 0) {
  	    xmlIOErr(XML_IO_ENCODER, NULL);
  	    in->error = XML_IO_ENCODER;
  	    return(-1);
  	}
  	in->rawconsumed += (use - xmlBufUse(in->raw));
      } else {
  	nbchars = len;
          ret = xmlBufAdd(in->buffer, (xmlChar *) buf, nbchars);
  	if (ret != 0)
  	    return(-1);
      }
  #ifdef DEBUG_INPUT
      xmlGenericError(xmlGenericErrorContext,
  	    "I/O: pushed %d chars, buffer %d/%d\n",
              nbchars, xmlBufUse(in->buffer), xmlBufLength(in->buffer));
  #endif
      return(nbchars);
  }
  
  /**
   * endOfInput:
   *
   * When reading from an Input channel indicated end of file or error
   * don't reread from it again.
   */
  static int
  endOfInput (void * context ATTRIBUTE_UNUSED,
  	    char * buffer ATTRIBUTE_UNUSED,
  	    int len ATTRIBUTE_UNUSED) {
      return(0);
  }
  
  /**
   * xmlParserInputBufferGrow:
   * @in:  a buffered parser input
   * @len:  indicative value of the amount of chars to read
   *
   * Grow up the content of the input buffer, the old data are preserved
   * This routine handle the I18N transcoding to internal UTF-8
   * This routine is used when operating the parser in normal (pull) mode
   *
   * TODO: one should be able to remove one extra copy by copying directly
   *       onto in->buffer or in->raw
   *
   * Returns the number of chars read and stored in the buffer, or -1
   *         in case of error.
   */
  int
  xmlParserInputBufferGrow(xmlParserInputBufferPtr in, int len) {
      char *buffer = NULL;
      int res = 0;
      int nbchars = 0;
  
      if ((in == NULL) || (in->error)) return(-1);
      if ((len <= MINLEN) && (len != 4))
          len = MINLEN;
  
      if (xmlBufAvail(in->buffer) <= 0) {
  	xmlIOErr(XML_IO_BUFFER_FULL, NULL);
  	in->error = XML_IO_BUFFER_FULL;
  	return(-1);
      }
  
      if (xmlBufGrow(in->buffer, len + 1) < 0) {
          xmlIOErrMemory("growing input buffer");
          in->error = XML_ERR_NO_MEMORY;
          return(-1);
      }
      buffer = (char *)xmlBufEnd(in->buffer);
  
      /*
       * Call the read method for this I/O type.
       */
      if (in->readcallback != NULL) {
  	res = in->readcallback(in->context, &buffer[0], len);
  	if (res <= 0)
  	    in->readcallback = endOfInput;
      } else {
  	xmlIOErr(XML_IO_NO_INPUT, NULL);
  	in->error = XML_IO_NO_INPUT;
  	return(-1);
      }
      if (res < 0) {
  	return(-1);
      }
  
      /*
       * try to establish compressed status of input if not done already
       */
      if (in->compressed == -1) {
  #ifdef LIBXML_LZMA_ENABLED
  	if (in->readcallback == xmlXzfileRead)
              in->compressed = __libxml2_xzcompressed(in->context);
  #endif
      }
  
      len = res;
      if (in->encoder != NULL) {
          unsigned int use;
  
          /*
  	 * Store the data in the incoming raw buffer
  	 */
          if (in->raw == NULL) {
  	    in->raw = xmlBufCreate();
  	}
  	res = xmlBufAdd(in->raw, (const xmlChar *) buffer, len);
  	if (res != 0)
  	    return(-1);
  
  	/*
  	 * convert as much as possible to the parser reading buffer.
  	 */
  	use = xmlBufUse(in->raw);
  	nbchars = xmlCharEncInput(in, 1);
  	if (nbchars < 0) {
  	    xmlIOErr(XML_IO_ENCODER, NULL);
  	    in->error = XML_IO_ENCODER;
  	    return(-1);
  	}
  	in->rawconsumed += (use - xmlBufUse(in->raw));
      } else {
  	nbchars = len;
          xmlBufAddLen(in->buffer, nbchars);
      }
  #ifdef DEBUG_INPUT
      xmlGenericError(xmlGenericErrorContext,
  	    "I/O: read %d chars, buffer %d\n",
              nbchars, xmlBufUse(in->buffer));
  #endif
      return(nbchars);
  }
  
  /**
   * xmlParserInputBufferRead:
   * @in:  a buffered parser input
   * @len:  indicative value of the amount of chars to read
   *
   * Refresh the content of the input buffer, the old data are considered
   * consumed
   * This routine handle the I18N transcoding to internal UTF-8
   *
   * Returns the number of chars read and stored in the buffer, or -1
   *         in case of error.
   */
  int
  xmlParserInputBufferRead(xmlParserInputBufferPtr in, int len) {
      if ((in == NULL) || (in->error)) return(-1);
      if (in->readcallback != NULL)
  	return(xmlParserInputBufferGrow(in, len));
      else if (xmlBufGetAllocationScheme(in->buffer) == XML_BUFFER_ALLOC_IMMUTABLE)
  	return(0);
      else
          return(-1);
  }
  
  #ifdef LIBXML_OUTPUT_ENABLED
  /**
   * xmlOutputBufferWrite:
   * @out:  a buffered parser output
   * @len:  the size in bytes of the array.
   * @buf:  an char array
   *
   * Write the content of the array in the output I/O buffer
   * This routine handle the I18N transcoding from internal UTF-8
   * The buffer is lossless, i.e. will store in case of partial
   * or delayed writes.
   *
   * Returns the number of chars immediately written, or -1
   *         in case of error.
   */
  int
  xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) {
      int nbchars = 0; /* number of chars to output to I/O */
      int ret;         /* return from function call */
      int written = 0; /* number of char written to I/O so far */
      int chunk;       /* number of byte current processed from buf */
  
      if ((out == NULL) || (out->error)) return(-1);
      if (len < 0) return(0);
      if (out->error) return(-1);
  
      do {
  	chunk = len;
  	if (chunk > 4 * MINLEN)
  	    chunk = 4 * MINLEN;
  
  	/*
  	 * first handle encoding stuff.
  	 */
  	if (out->encoder != NULL) {
  	    /*
  	     * Store the data in the incoming raw buffer
  	     */
  	    if (out->conv == NULL) {
  		out->conv = xmlBufCreate();
  	    }
  	    ret = xmlBufAdd(out->buffer, (const xmlChar *) buf, chunk);
  	    if (ret != 0)
  	        return(-1);
  
  	    if ((xmlBufUse(out->buffer) < MINLEN) && (chunk == len))
  		goto done;
  
  	    /*
  	     * convert as much as possible to the parser reading buffer.
  	     */
  	    ret = xmlCharEncOutput(out, 0);
  	    if ((ret < 0) && (ret != -3)) {
  		xmlIOErr(XML_IO_ENCODER, NULL);
  		out->error = XML_IO_ENCODER;
  		return(-1);
  	    }
              if (out->writecallback)
  	        nbchars = xmlBufUse(out->conv);
              else
                  nbchars = ret >= 0 ? ret : 0;
  	} else {
  	    ret = xmlBufAdd(out->buffer, (const xmlChar *) buf, chunk);
  	    if (ret != 0)
  	        return(-1);
              if (out->writecallback)
  	        nbchars = xmlBufUse(out->buffer);
              else
                  nbchars = chunk;
  	}
  	buf += chunk;
  	len -= chunk;
  
  	if (out->writecallback) {
              if ((nbchars < MINLEN) && (len <= 0))
                  goto done;
  
  	    /*
  	     * second write the stuff to the I/O channel
  	     */
  	    if (out->encoder != NULL) {
  		ret = out->writecallback(out->context,
                             (const char *)xmlBufContent(out->conv), nbchars);
  		if (ret >= 0)
  		    xmlBufShrink(out->conv, ret);
  	    } else {
  		ret = out->writecallback(out->context,
                             (const char *)xmlBufContent(out->buffer), nbchars);
  		if (ret >= 0)
  		    xmlBufShrink(out->buffer, ret);
  	    }
  	    if (ret < 0) {
  		xmlIOErr(XML_IO_WRITE, NULL);
  		out->error = XML_IO_WRITE;
  		return(ret);
  	    }
              if (out->written > INT_MAX - ret)
                  out->written = INT_MAX;
              else
                  out->written += ret;
  	}
  	written += nbchars;
      } while (len > 0);
  
  done:
  #ifdef DEBUG_INPUT
      xmlGenericError(xmlGenericErrorContext,
  	    "I/O: wrote %d chars\n", written);
  #endif
      return(written);
  }
  
  /**
   * xmlEscapeContent:
   * @out:  a pointer to an array of bytes to store the result
   * @outlen:  the length of @out
   * @in:  a pointer to an array of unescaped UTF-8 bytes
   * @inlen:  the length of @in
   *
   * Take a block of UTF-8 chars in and escape them.
   * Returns 0 if success, or -1 otherwise
   * The value of @inlen after return is the number of octets consumed
   *     if the return value is positive, else unpredictable.
   * The value of @outlen after return is the number of octets consumed.
   */
  static int
  xmlEscapeContent(unsigned char* out, int *outlen,
                   const xmlChar* in, int *inlen) {
      unsigned char* outstart = out;
      const unsigned char* base = in;
      unsigned char* outend = out + *outlen;
      const unsigned char* inend;
  
      inend = in + (*inlen);
  
      while ((in < inend) && (out < outend)) {
  	if (*in == '<') {
  	    if (outend - out < 4) break;
  	    *out++ = '&';
  	    *out++ = 'l';
  	    *out++ = 't';
  	    *out++ = ';';
  	} else if (*in == '>') {
  	    if (outend - out < 4) break;
  	    *out++ = '&';
  	    *out++ = 'g';
  	    *out++ = 't';
  	    *out++ = ';';
  	} else if (*in == '&') {
  	    if (outend - out < 5) break;
  	    *out++ = '&';
  	    *out++ = 'a';
  	    *out++ = 'm';
  	    *out++ = 'p';
  	    *out++ = ';';
  	} else if (*in == '\r') {
  	    if (outend - out < 5) break;
  	    *out++ = '&';
  	    *out++ = '#';
  	    *out++ = '1';
  	    *out++ = '3';
  	    *out++ = ';';
  	} else {
  	    *out++ = (unsigned char) *in;
  	}
  	++in;
      }
      *outlen = out - outstart;
      *inlen = in - base;
      return(0);
  }
  
  /**
   * xmlOutputBufferWriteEscape:
   * @out:  a buffered parser output
   * @str:  a zero terminated UTF-8 string
   * @escaping:  an optional escaping function (or NULL)
   *
   * Write the content of the string in the output I/O buffer
   * This routine escapes the characters and then handle the I18N
   * transcoding from internal UTF-8
   * The buffer is lossless, i.e. will store in case of partial
   * or delayed writes.
   *
   * Returns the number of chars immediately written, or -1
   *         in case of error.
   */
  int
  xmlOutputBufferWriteEscape(xmlOutputBufferPtr out, const xmlChar *str,
                             xmlCharEncodingOutputFunc escaping) {
      int nbchars = 0; /* number of chars to output to I/O */
      int ret;         /* return from function call */
      int written = 0; /* number of char written to I/O so far */
      int oldwritten=0;/* loop guard */
      int chunk;       /* number of byte currently processed from str */
      int len;         /* number of bytes in str */
      int cons;        /* byte from str consumed */
  
      if ((out == NULL) || (out->error) || (str == NULL) ||
          (out->buffer == NULL) ||
  	(xmlBufGetAllocationScheme(out->buffer) == XML_BUFFER_ALLOC_IMMUTABLE))
          return(-1);
      len = strlen((const char *)str);
      if (len < 0) return(0);
      if (out->error) return(-1);
      if (escaping == NULL) escaping = xmlEscapeContent;
  
      do {
          oldwritten = written;
  
          /*
  	 * how many bytes to consume and how many bytes to store.
  	 */
  	cons = len;
  	chunk = xmlBufAvail(out->buffer) - 1;
  
          /*
  	 * make sure we have enough room to save first, if this is
  	 * not the case force a flush, but make sure we stay in the loop
  	 */
  	if (chunk < 40) {
  	    if (xmlBufGrow(out->buffer, 100) < 0)
  	        return(-1);
              oldwritten = -1;
  	    continue;
  	}
  
  	/*
  	 * first handle encoding stuff.
  	 */
  	if (out->encoder != NULL) {
  	    /*
  	     * Store the data in the incoming raw buffer
  	     */
  	    if (out->conv == NULL) {
  		out->conv = xmlBufCreate();
  	    }
  	    ret = escaping(xmlBufEnd(out->buffer) ,
  	                   &chunk, str, &cons);
  	    if ((ret < 0) || (chunk == 0)) /* chunk==0 => nothing done */
  	        return(-1);
              xmlBufAddLen(out->buffer, chunk);
  
  	    if ((xmlBufUse(out->buffer) < MINLEN) && (cons == len))
  		goto done;
  
  	    /*
  	     * convert as much as possible to the output buffer.
  	     */
  	    ret = xmlCharEncOutput(out, 0);
  	    if ((ret < 0) && (ret != -3)) {
  		xmlIOErr(XML_IO_ENCODER, NULL);
  		out->error = XML_IO_ENCODER;
  		return(-1);
  	    }
              if (out->writecallback)
  	        nbchars = xmlBufUse(out->conv);
              else
                  nbchars = ret >= 0 ? ret : 0;
  	} else {
  	    ret = escaping(xmlBufEnd(out->buffer), &chunk, str, &cons);
  	    if ((ret < 0) || (chunk == 0)) /* chunk==0 => nothing done */
  	        return(-1);
              xmlBufAddLen(out->buffer, chunk);
              if (out->writecallback)
  	        nbchars = xmlBufUse(out->buffer);
              else
                  nbchars = chunk;
  	}
  	str += cons;
  	len -= cons;
  
  	if (out->writecallback) {
              if ((nbchars < MINLEN) && (len <= 0))
                  goto done;
  
  	    /*
  	     * second write the stuff to the I/O channel
  	     */
  	    if (out->encoder != NULL) {
  		ret = out->writecallback(out->context,
                             (const char *)xmlBufContent(out->conv), nbchars);
  		if (ret >= 0)
  		    xmlBufShrink(out->conv, ret);
  	    } else {
  		ret = out->writecallback(out->context,
                             (const char *)xmlBufContent(out->buffer), nbchars);
  		if (ret >= 0)
  		    xmlBufShrink(out->buffer, ret);
  	    }
  	    if (ret < 0) {
  		xmlIOErr(XML_IO_WRITE, NULL);
  		out->error = XML_IO_WRITE;
  		return(ret);
  	    }
              if (out->written > INT_MAX - ret)
                  out->written = INT_MAX;
              else
                  out->written += ret;
  	} else if (xmlBufAvail(out->buffer) < MINLEN) {
  	    xmlBufGrow(out->buffer, MINLEN);
  	}
  	written += nbchars;
      } while ((len > 0) && (oldwritten != written));
  
  done:
  #ifdef DEBUG_INPUT
      xmlGenericError(xmlGenericErrorContext,
  	    "I/O: wrote %d chars\n", written);
  #endif
      return(written);
  }
  
  /**
   * xmlOutputBufferWriteString:
   * @out:  a buffered parser output
   * @str:  a zero terminated C string
   *
   * Write the content of the string in the output I/O buffer
   * This routine handle the I18N transcoding from internal UTF-8
   * The buffer is lossless, i.e. will store in case of partial
   * or delayed writes.
   *
   * Returns the number of chars immediately written, or -1
   *         in case of error.
   */
  int
  xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str) {
      int len;
  
      if ((out == NULL) || (out->error)) return(-1);
      if (str == NULL)
          return(-1);
      len = strlen(str);
  
      if (len > 0)
  	return(xmlOutputBufferWrite(out, len, str));
      return(len);
  }
  
  /**
   * xmlOutputBufferFlush:
   * @out:  a buffered output
   *
   * flushes the output I/O channel
   *
   * Returns the number of byte written or -1 in case of error.
   */
  int
  xmlOutputBufferFlush(xmlOutputBufferPtr out) {
      int nbchars = 0, ret = 0;
  
      if ((out == NULL) || (out->error)) return(-1);
      /*
       * first handle encoding stuff.
       */
      if ((out->conv != NULL) && (out->encoder != NULL)) {
  	/*
  	 * convert as much as possible to the parser output buffer.
  	 */
  	do {
  	    nbchars = xmlCharEncOutput(out, 0);
  	    if (nbchars < 0) {
  		xmlIOErr(XML_IO_ENCODER, NULL);
  		out->error = XML_IO_ENCODER;
  		return(-1);
  	    }
  	} while (nbchars);
      }
  
      /*
       * second flush the stuff to the I/O channel
       */
      if ((out->conv != NULL) && (out->encoder != NULL) &&
  	(out->writecallback != NULL)) {
  	ret = out->writecallback(out->context,
                                   (const char *)xmlBufContent(out->conv),
                                   xmlBufUse(out->conv));
  	if (ret >= 0)
  	    xmlBufShrink(out->conv, ret);
      } else if (out->writecallback != NULL) {
  	ret = out->writecallback(out->context,
                                   (const char *)xmlBufContent(out->buffer),
                                   xmlBufUse(out->buffer));
  	if (ret >= 0)
  	    xmlBufShrink(out->buffer, ret);
      }
      if (ret < 0) {
  	xmlIOErr(XML_IO_FLUSH, NULL);
  	out->error = XML_IO_FLUSH;
  	return(ret);
      }
      if (out->written > INT_MAX - ret)
          out->written = INT_MAX;
      else
          out->written += ret;
  
  #ifdef DEBUG_INPUT
      xmlGenericError(xmlGenericErrorContext,
  	    "I/O: flushed %d chars\n", ret);
  #endif
      return(ret);
  }
  #endif /* LIBXML_OUTPUT_ENABLED */
  
  /**
   * xmlParserGetDirectory:
   * @filename:  the path to a file
   *
   * lookup the directory for that file
   *
   * Returns a new allocated string containing the directory, or NULL.
   */
  char *
  xmlParserGetDirectory(const char *filename) {
      char *ret = NULL;
      char dir[1024];
      char *cur;
  
      if (xmlInputCallbackInitialized == 0)
  	xmlRegisterDefaultInputCallbacks();
  
      if (filename == NULL) return(NULL);
  
  #if defined(_WIN32)
  #   define IS_XMLPGD_SEP(ch) ((ch=='/')||(ch=='\\'))
  #else
  #   define IS_XMLPGD_SEP(ch) (ch=='/')
  #endif
  
      strncpy(dir, filename, 1023);
      dir[1023] = 0;
      cur = &dir[strlen(dir)];
      while (cur > dir) {
           if (IS_XMLPGD_SEP(*cur)) break;
  	 cur --;
      }
      if (IS_XMLPGD_SEP(*cur)) {
          if (cur == dir) dir[1] = 0;
  	else *cur = 0;
  	ret = xmlMemStrdup(dir);
      } else {
          if (getcwd(dir, 1024) != NULL) {
  	    dir[1023] = 0;
  	    ret = xmlMemStrdup(dir);
  	}
      }
      return(ret);
  #undef IS_XMLPGD_SEP
  }
  
  /****************************************************************
   *								*
   *		External entities loading			*
   *								*
   ****************************************************************/
  
  /**
   * xmlCheckHTTPInput:
   * @ctxt: an XML parser context
   * @ret: an XML parser input
   *
   * Check an input in case it was created from an HTTP stream, in that
   * case it will handle encoding and update of the base URL in case of
   * redirection. It also checks for HTTP errors in which case the input
   * is cleanly freed up and an appropriate error is raised in context
   *
   * Returns the input or NULL in case of HTTP error.
   */
  xmlParserInputPtr
  xmlCheckHTTPInput(xmlParserCtxtPtr ctxt, xmlParserInputPtr ret) {
      /* Avoid unused variable warning if features are disabled. */
      (void) ctxt;
  
  #ifdef LIBXML_HTTP_ENABLED
      if ((ret != NULL) && (ret->buf != NULL) &&
          (ret->buf->readcallback == xmlIOHTTPRead) &&
          (ret->buf->context != NULL)) {
          const char *encoding;
          const char *redir;
          const char *mime;
          int code;
  
          code = xmlNanoHTTPReturnCode(ret->buf->context);
          if (code >= 400) {
              /* fatal error */
  	    if (ret->filename != NULL)
  		__xmlLoaderErr(ctxt, "failed to load HTTP resource \"%s\"\n",
                           (const char *) ret->filename);
  	    else
  		__xmlLoaderErr(ctxt, "failed to load HTTP resource\n", NULL);
              xmlFreeInputStream(ret);
              ret = NULL;
          } else {
  
              mime = xmlNanoHTTPMimeType(ret->buf->context);
              if ((xmlStrstr(BAD_CAST mime, BAD_CAST "/xml")) ||
                  (xmlStrstr(BAD_CAST mime, BAD_CAST "+xml"))) {
                  encoding = xmlNanoHTTPEncoding(ret->buf->context);
                  if (encoding != NULL) {
                      xmlCharEncodingHandlerPtr handler;
  
                      handler = xmlFindCharEncodingHandler(encoding);
                      if (handler != NULL) {
                          xmlSwitchInputEncoding(ctxt, ret, handler);
                      } else {
                          __xmlErrEncoding(ctxt, XML_ERR_UNKNOWN_ENCODING,
                                           "Unknown encoding %s",
                                           BAD_CAST encoding, NULL);
                      }
                      if (ret->encoding == NULL)
                          ret->encoding = xmlStrdup(BAD_CAST encoding);
                  }
  #if 0
              } else if (xmlStrstr(BAD_CAST mime, BAD_CAST "html")) {
  #endif
              }
              redir = xmlNanoHTTPRedir(ret->buf->context);
              if (redir != NULL) {
                  if (ret->filename != NULL)
                      xmlFree((xmlChar *) ret->filename);
                  if (ret->directory != NULL) {
                      xmlFree((xmlChar *) ret->directory);
                      ret->directory = NULL;
                  }
                  ret->filename =
                      (char *) xmlStrdup((const xmlChar *) redir);
              }
          }
      }
  #endif
      return(ret);
  }
  
  static int xmlNoNetExists(const char *URL) {
      const char *path;
  
      if (URL == NULL)
  	return(0);
  
      if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
  #if defined (_WIN32)
  	path = &URL[17];
  #else
  	path = &URL[16];
  #endif
      else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
  #if defined (_WIN32)
  	path = &URL[8];
  #else
  	path = &URL[7];
  #endif
      } else
  	path = URL;
  
      return xmlCheckFilename(path);
  }
  
  #ifdef LIBXML_CATALOG_ENABLED
  
  /**
   * xmlResolveResourceFromCatalog:
   * @URL:  the URL for the entity to load
   * @ID:  the System ID for the entity to load
   * @ctxt:  the context in which the entity is called or NULL
   *
   * Resolves the URL and ID against the appropriate catalog.
   * This function is used by xmlDefaultExternalEntityLoader and
   * xmlNoNetExternalEntityLoader.
   *
   * Returns a new allocated URL, or NULL.
   */
  static xmlChar *
  xmlResolveResourceFromCatalog(const char *URL, const char *ID,
                                xmlParserCtxtPtr ctxt) {
      xmlChar *resource = NULL;
      xmlCatalogAllow pref;
  
      /*
       * If the resource doesn't exists as a file,
       * try to load it from the resource pointed in the catalogs
       */
      pref = xmlCatalogGetDefaults();
  
      if ((pref != XML_CATA_ALLOW_NONE) && (!xmlNoNetExists(URL))) {
  	/*
  	 * Do a local lookup
  	 */
  	if ((ctxt != NULL) && (ctxt->catalogs != NULL) &&
  	    ((pref == XML_CATA_ALLOW_ALL) ||
  	     (pref == XML_CATA_ALLOW_DOCUMENT))) {
  	    resource = xmlCatalogLocalResolve(ctxt->catalogs,
  					      (const xmlChar *)ID,
  					      (const xmlChar *)URL);
          }
  	/*
  	 * Try a global lookup
  	 */
  	if ((resource == NULL) &&
  	    ((pref == XML_CATA_ALLOW_ALL) ||
  	     (pref == XML_CATA_ALLOW_GLOBAL))) {
  	    resource = xmlCatalogResolve((const xmlChar *)ID,
  					 (const xmlChar *)URL);
  	}
  	if ((resource == NULL) && (URL != NULL))
  	    resource = xmlStrdup((const xmlChar *) URL);
  
  	/*
  	 * TODO: do an URI lookup on the reference
  	 */
  	if ((resource != NULL) && (!xmlNoNetExists((const char *)resource))) {
  	    xmlChar *tmp = NULL;
  
  	    if ((ctxt != NULL) && (ctxt->catalogs != NULL) &&
  		((pref == XML_CATA_ALLOW_ALL) ||
  		 (pref == XML_CATA_ALLOW_DOCUMENT))) {
  		tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
  	    }
  	    if ((tmp == NULL) &&
  		((pref == XML_CATA_ALLOW_ALL) ||
  	         (pref == XML_CATA_ALLOW_GLOBAL))) {
  		tmp = xmlCatalogResolveURI(resource);
  	    }
  
  	    if (tmp != NULL) {
  		xmlFree(resource);
  		resource = tmp;
  	    }
  	}
      }
  
      return resource;
  }
  
  #endif
  
  /**
   * xmlDefaultExternalEntityLoader:
   * @URL:  the URL for the entity to load
   * @ID:  the System ID for the entity to load
   * @ctxt:  the context in which the entity is called or NULL
   *
   * By default we don't load external entities, yet.
   *
   * Returns a new allocated xmlParserInputPtr, or NULL.
   */
  static xmlParserInputPtr
  xmlDefaultExternalEntityLoader(const char *URL, const char *ID,
                                 xmlParserCtxtPtr ctxt)
  {
      xmlParserInputPtr ret = NULL;
      xmlChar *resource = NULL;
  
  #ifdef DEBUG_EXTERNAL_ENTITIES
      xmlGenericError(xmlGenericErrorContext,
                      "xmlDefaultExternalEntityLoader(%s, xxx)\n", URL);
  #endif
      if ((ctxt != NULL) && (ctxt->options & XML_PARSE_NONET)) {
          int options = ctxt->options;
  
  	ctxt->options -= XML_PARSE_NONET;
          ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
  	ctxt->options = options;
  	return(ret);
      }
  #ifdef LIBXML_CATALOG_ENABLED
      resource = xmlResolveResourceFromCatalog(URL, ID, ctxt);
  #endif
  
      if (resource == NULL)
          resource = (xmlChar *) URL;
  
      if (resource == NULL) {
          if (ID == NULL)
              ID = "NULL";
          __xmlLoaderErr(ctxt, "failed to load external entity \"%s\"\n", ID);
          return (NULL);
      }
      ret = xmlNewInputFromFile(ctxt, (const char *) resource);
      if ((resource != NULL) && (resource != (xmlChar *) URL))
          xmlFree(resource);
      return (ret);
  }
  
  static xmlExternalEntityLoader xmlCurrentExternalEntityLoader =
         xmlDefaultExternalEntityLoader;
  
  /**
   * xmlSetExternalEntityLoader:
   * @f:  the new entity resolver function
   *
   * Changes the defaultexternal entity resolver function for the application
   */
  void
  xmlSetExternalEntityLoader(xmlExternalEntityLoader f) {
      xmlCurrentExternalEntityLoader = f;
  }
  
  /**
   * xmlGetExternalEntityLoader:
   *
   * Get the default external entity resolver function for the application
   *
   * Returns the xmlExternalEntityLoader function pointer
   */
  xmlExternalEntityLoader
  xmlGetExternalEntityLoader(void) {
      return(xmlCurrentExternalEntityLoader);
  }
  
  /**
   * xmlLoadExternalEntity:
   * @URL:  the URL for the entity to load
   * @ID:  the Public ID for the entity to load
   * @ctxt:  the context in which the entity is called or NULL
   *
   * Load an external entity, note that the use of this function for
   * unparsed entities may generate problems
   *
   * Returns the xmlParserInputPtr or NULL
   */
  xmlParserInputPtr
  xmlLoadExternalEntity(const char *URL, const char *ID,
                        xmlParserCtxtPtr ctxt) {
      if ((URL != NULL) && (xmlNoNetExists(URL) == 0)) {
  	char *canonicFilename;
  	xmlParserInputPtr ret;
  
  	canonicFilename = (char *) xmlCanonicPath((const xmlChar *) URL);
  	if (canonicFilename == NULL) {
              xmlIOErrMemory("building canonical path\n");
  	    return(NULL);
  	}
  
  	ret = xmlCurrentExternalEntityLoader(canonicFilename, ID, ctxt);
  	xmlFree(canonicFilename);
  	return(ret);
      }
      return(xmlCurrentExternalEntityLoader(URL, ID, ctxt));
  }
  
  /************************************************************************
   *									*
   *		Disabling Network access				*
   *									*
   ************************************************************************/
  
  /**
   * xmlNoNetExternalEntityLoader:
   * @URL:  the URL for the entity to load
   * @ID:  the System ID for the entity to load
   * @ctxt:  the context in which the entity is called or NULL
   *
   * A specific entity loader disabling network accesses, though still
   * allowing local catalog accesses for resolution.
   *
   * Returns a new allocated xmlParserInputPtr, or NULL.
   */
  xmlParserInputPtr
  xmlNoNetExternalEntityLoader(const char *URL, const char *ID,
                               xmlParserCtxtPtr ctxt) {
      xmlParserInputPtr input = NULL;
      xmlChar *resource = NULL;
  
  #ifdef LIBXML_CATALOG_ENABLED
      resource = xmlResolveResourceFromCatalog(URL, ID, ctxt);
  #endif
  
      if (resource == NULL)
  	resource = (xmlChar *) URL;
  
      if (resource != NULL) {
          if ((!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "ftp://", 6)) ||
              (!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "http://", 7))) {
              xmlIOErr(XML_IO_NETWORK_ATTEMPT, (const char *) resource);
  	    if (resource != (xmlChar *) URL)
  		xmlFree(resource);
  	    return(NULL);
  	}
      }
      input = xmlDefaultExternalEntityLoader((const char *) resource, ID, ctxt);
      if (resource != (xmlChar *) URL)
  	xmlFree(resource);
      return(input);
  }
  
  