/*
 * 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>
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif


#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 HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include "vtk_libxml2_zlib.h"

#ifdef WIN32
#include <windows.h>
#endif

/*--------------------------------------------------------------------------*/
/*
  VTK MODIFICATION: When building on cygwin with -mwin32 we get _WIN32
  defined.  If -mno-cygwin is not given then __CYGWIN__ is still
  defined.  This is the case when VTK builds in cygwin.  The cygwin
  API does not provide _wfopen or _wstat, and libxml2 does not seem to
  have accounted for this upstream.  We work around the unresolved
  symbols by providing them here.  We simply return failure so that
  the routines below fall back to the single-width API provided by
  cygwin.
*/
#if defined(_WIN32) && defined(__CYGWIN__)
static FILE* _wfopen(const wchar_t* path, const wchar_t* mode)
{
  (void)path;
  (void)mode;
  return 0;
}
static int _wstat(const wchar_t* path, struct _stat* info)
{
  (void)path;
  (void)info;
  return -1;
}
#endif
/*--------------------------------------------------------------------------*/

/* Figure a portable way to know if a file is a directory. */
#ifndef HAVE_STAT
#  ifdef HAVE__STAT
     /* MS C library seems to define stat and _stat. The definition
        is identical. Still, mapping them to each other causes a warning. */
#    ifndef _MSC_VER
#      define stat(x,y) _stat(x,y)
#    endif
#    define HAVE_STAT
#  endif
#else
#  ifdef HAVE__STAT
#    if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
#      define stat _stat
#    endif
#  endif
#endif
#ifdef HAVE_STAT
#  ifndef S_ISDIR
#    ifdef _S_ISDIR
#      define S_ISDIR(x) _S_ISDIR(x)
#    else
#      ifdef S_IFDIR
#        ifndef S_IFMT
#          ifdef _S_IFMT
#            define S_IFMT _S_IFMT
#          endif
#        endif
#        ifdef S_IFMT
#          define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#        endif
#      endif
#    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>

/* #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;
#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 */
    "adddress in use",          /* EADDRINUSE */
    "already in use",           /* EALREADY */
    "unknown address familly",  /* EAFNOSUPPORT */
};

#if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
/**
 * __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 informations
 *
 * 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 informations
 *
 * Handle an I/O error
 */
void
__xmlIOErr(int domain, int code, const char *extra)
{
    unsigned int idx;

    if (code == 0) {
#ifdef HAVE_ERRNO_H
        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;
#endif /* HAVE_ERRNO_H */
    }
    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 informations
 *
 * 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 informations
 *
 * 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;
}
#endif /* LIBXML_OUTPUT_ENABLED */

/************************************************************************
 *                                                                      *
 *              Standard I/O for file accesses                          *
 *                                                                      *
 ************************************************************************/

#if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)

/**
 *  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;
}

/**
 *  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)
{
#ifdef HAVE_STAT
    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;
#else
    return -1;
#endif
}

/**
 *  xmlWrapOpenNative:
 * @path:  the path
 * @mode:  type of access (0 - read, 1 - write)
 *
 * function opens the file specified by @path
 *
 */
static FILE*
xmlWrapOpenNative(const char *path,int mode)
{
    return fopen(path,mode ? "wb" : "rb");
}

/**
 *  xmlWrapStatNative:
 * @path:  the path
 * @info:  structure that stores results
 *
 * function obtains information about the file or directory
 *
 */
static int
xmlWrapStatNative(const char *path,struct stat *info)
{
#ifdef HAVE_STAT
    return stat(path,info);
#else
    return -1;
#endif
}

typedef int (* xmlWrapStatFunc) (const char *f, struct stat *s);
static xmlWrapStatFunc xmlWrapStat = xmlWrapStatNative;
typedef FILE* (* xmlWrapOpenFunc)(const char *f,int mode);
static xmlWrapOpenFunc xmlWrapOpen = xmlWrapOpenNative;

/**
 * xmlInitPlatformSpecificIo:
 *
 * Initialize platform specific features.
 */
static void
xmlInitPlatformSpecificIo(void)
{
    static int xmlPlatformIoInitialized = 0;
    OSVERSIONINFO osvi;

    if(xmlPlatformIoInitialized)
      return;

    osvi.dwOSVersionInfoSize = sizeof(osvi);

    if(GetVersionEx(&osvi) && (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)) {
      xmlWrapStat = xmlWrapStatUtf8;
      xmlWrapOpen = xmlWrapOpenUtf8;
    } else {
      xmlWrapStat = xmlWrapStatNative;
      xmlWrapOpen = xmlWrapOpenNative;
    }

    xmlPlatformIoInitialized = 1;
    return;
}

#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
        struct stat stat_buffer;
#endif
        if (path == NULL)
                return(0);

#ifdef HAVE_STAT
#if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
    if (xmlWrapStat(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;
}

static int
xmlNop(void) {
    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) (long) 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) (long) 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) (long) 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 = NULL;
    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) || defined (__DJGPP__) && !defined(__CYGWIN__)
        path = &filename[17];
#else
        path = &filename[16];
#endif
    else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
#if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
        path = &filename[8];
#else
        path = &filename[7];
#endif
    } else
        path = filename;

    if (path == NULL)
        return(NULL);
    if (!xmlCheckFilename(path))
        return(NULL);

#if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
    fd = xmlWrapOpen(path, 0);
#else
    fd = fopen(path, "r");
#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;

    unescaped = xmlURIUnescapeString(filename, 0, NULL);
    if (unescaped != NULL) {
        retval = xmlFileOpen_real(unescaped);
        xmlFree(unescaped);
    } else {
        retval = xmlFileOpen_real(filename);
    }
    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) || defined (__DJGPP__) && !defined(__CYGWIN__)
        path = &filename[17];
#else
        path = &filename[16];
#endif
    else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
#if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
        path = &filename[8];
#else
        path = &filename[7];
#endif
    } else
        path = filename;

    if (path == NULL)
        return(NULL);

#if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
    fd = xmlWrapOpen(path, 1);
#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 HAVE_ZLIB_H
/************************************************************************
 *                                                                      *
 *              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, "-")) {
        fd = gzdopen(dup(0), "rb");
        return((void *) fd);
    }

    if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
#if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
        path = &filename[17];
#else
        path = &filename[16];
#endif
    else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
#if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
        path = &filename[8];
#else
        path = &filename[7];
#endif
    } else
        path = filename;

    if (path == NULL)
        return(NULL);
    if (!xmlCheckFilename(path))
        return(NULL);

    fd = gzopen(path, "rb");
    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, "-")) {
        fd = gzdopen(dup(1), mode);
        return((void *) fd);
    }

    if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
#if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
        path = &filename[17];
#else
        path = &filename[16];
#endif
    else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
#if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
        path = &filename[8];
#else
        path = &filename[7];
#endif
    } else
        path = filename;

    if (path == NULL)
        return(NULL);

    fd = gzopen(path, mode);
    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 written
 */
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 /* HAVE_ZLIB_H */

#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 HAVE_ZLIB_H

#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,
                    (const xmlChar *) "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,
                    (const xmlChar *) "xmlZMemBuffExtend:  %s %lu bytes.\n",
                    "Allocation failure extending output buffer to",
                    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,
                        (const xmlChar *) "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,
                    (const xmlChar *) "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  /*  HAVE_ZLIB_H  */

#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 HAVE_ZLIB_H
        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)
{

    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 HAVE_ZLIB_H
    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 = xmlAllocOutputBuffer(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 HAVE_ZLIB_H
        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,
                        (const xmlChar *) "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 HAVE_ZLIB_H

    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 *)dctxt->buffer->content;
        content_lgth = dctxt->buffer->use;
    }

    if ( http_content == NULL ) {
        xmlChar msg[500];
        xmlStrPrintf(msg, 500,
                     (const xmlChar *) "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,
    (const xmlChar *) "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_INPUT_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;

#if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
    xmlInitPlatformSpecificIo();
#endif

    xmlRegisterInputCallbacks(xmlFileMatch, xmlFileOpen,
                              xmlFileRead, xmlFileClose);
#ifdef HAVE_ZLIB_H
    xmlRegisterInputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
                              xmlGzfileRead, xmlGzfileClose);
#endif /* HAVE_ZLIB_H */

#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;

#if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
    xmlInitPlatformSpecificIo();
#endif

    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 HAVE_ZLIB_H
    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 = xmlBufferCreateSize(2 * xmlDefaultBufferSize);
    if (ret->buffer == NULL) {
        xmlFree(ret);
        return(NULL);
    }
    ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
    ret->encoder = xmlGetCharEncodingHandler(enc);
    if (ret->encoder != NULL)
        ret->raw = xmlBufferCreateSize(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 = xmlBufferCreate();
    if (ret->buffer == NULL) {
        xmlFree(ret);
        return(NULL);
    }
    ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
    ret->encoder = encoder;
    if (encoder != NULL) {
        ret->conv = xmlBufferCreateSize(4000);
        /*
         * This call is designed to initiate the encoder state
         */
        xmlCharEncOutFunc(encoder, ret->conv, NULL);
    } 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) {
        xmlBufferFree(in->raw);
        in->raw = NULL;
    }
    if (in->encoder != NULL) {
        xmlCharEncCloseFunc(in->encoder);
    }
    if (in->closecallback != NULL) {
        in->closecallback(in->context);
    }
    if (in->buffer != NULL) {
        xmlBufferFree(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) {
        xmlBufferFree(out->conv);
        out->conv = NULL;
    }
    if (out->encoder != NULL) {
        xmlCharEncCloseFunc(out->encoder);
    }
    if (out->buffer != NULL) {
        xmlBufferFree(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 HAVE_ZLIB_H
  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
    }
    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 HAVE_ZLIB_H
    int is_file_uri = 1;
#endif

    if (xmlOutputCallbackInitialized == 0)
        xmlRegisterDefaultOutputCallbacks();

    if (URI == NULL) return(NULL);

    puri = xmlParseURI(URI);
    if (puri != NULL) {
#ifdef HAVE_ZLIB_H
        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 HAVE_ZLIB_H
        if ((compression > 0) && (compression <= 9) && (is_file_uri == 1)) {
            context = xmlGzfileOpenW(unescaped, compression);
            if (context != NULL) {
                ret = xmlAllocOutputBuffer(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(HAVE_ZLIB_H)
                /*  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 HAVE_ZLIB_H
        if ((compression > 0) && (compression <= 9) && (is_file_uri == 1)) {
            context = xmlGzfileOpenW(URI, compression);
            if (context != NULL) {
                ret = xmlAllocOutputBuffer(encoder);
                if (ret != NULL) {
                    ret->context = context;
                    ret->writecallback = xmlGzfileWrite;
                    ret->closecallback = xmlGzfileClose;
                }
                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(HAVE_ZLIB_H)
                /*  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 = xmlAllocOutputBuffer(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 = xmlAllocOutputBuffer(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((xmlOutputWriteCallback)
                                  xmlBufferWrite,
                                  (xmlOutputCloseCallback)
                                  NULL, (void *) buffer, encoder);

    return(ret);
}

#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 *) (long) 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 = (xmlInputReadCallback) xmlNop;
        ret->closecallback = NULL;
        errcode = xmlBufferAdd(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 = xmlBufferCreateStatic((void *)mem, (size_t) size);
    if (ret->buffer == NULL) {
        xmlFree(ret);
        return(NULL);
    }
    ret->encoder = xmlGetCharEncodingHandler(enc);
    if (ret->encoder != NULL)
        ret->raw = xmlBufferCreateSize(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 = xmlAllocOutputBuffer(encoder);
    if (ret != NULL) {
        ret->context = (void *) (long) 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 = xmlAllocOutputBuffer(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 = xmlBufferCreate();
        }
        ret = xmlBufferAdd(in->raw, (const xmlChar *) buf, len);
        if (ret != 0)
            return(-1);

        /*
         * convert as much as possible to the parser reading buffer.
         */
        use = in->raw->use;
        nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
        if (nbchars < 0) {
            xmlIOErr(XML_IO_ENCODER, NULL);
            in->error = XML_IO_ENCODER;
            return(-1);
        }
        in->rawconsumed += (use - in->raw->use);
    } else {
        nbchars = len;
        ret = xmlBufferAdd(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, in->buffer->use, in->buffer->size);
#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;
    int buffree;
    unsigned int needSize;

    if ((in == NULL) || (in->error)) return(-1);
    if ((len <= MINLEN) && (len != 4))
        len = MINLEN;

    buffree = in->buffer->size - in->buffer->use;
    if (buffree <= 0) {
        xmlIOErr(XML_IO_BUFFER_FULL, NULL);
        in->error = XML_IO_BUFFER_FULL;
        return(-1);
    }

    needSize = in->buffer->use + len + 1;
    if (needSize > in->buffer->size){
        if (!xmlBufferResize(in->buffer, needSize)){
            xmlIOErrMemory("growing input buffer");
            in->error = XML_ERR_NO_MEMORY;
            return(-1);
        }
    }
    buffer = (char *)&in->buffer->content[in->buffer->use];

    /*
     * 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);
    }
    len = res;
    if (in->encoder != NULL) {
        unsigned int use;

        /*
         * Store the data in the incoming raw buffer
         */
        if (in->raw == NULL) {
            in->raw = xmlBufferCreate();
        }
        res = xmlBufferAdd(in->raw, (const xmlChar *) buffer, len);
        if (res != 0)
            return(-1);

        /*
         * convert as much as possible to the parser reading buffer.
         */
        use = in->raw->use;
        nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
        if (nbchars < 0) {
            xmlIOErr(XML_IO_ENCODER, NULL);
            in->error = XML_IO_ENCODER;
            return(-1);
        }
        in->rawconsumed += (use - in->raw->use);
    } else {
        nbchars = len;
        in->buffer->use += nbchars;
        buffer[nbchars] = 0;
    }
#ifdef DEBUG_INPUT
    xmlGenericError(xmlGenericErrorContext,
            "I/O: read %d chars, buffer %d/%d\n",
            nbchars, in->buffer->use, in->buffer->size);
#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 ((in->buffer != NULL) &&
             (in->buffer->alloc == 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 curreent 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 = xmlBufferCreate();
            }
            ret = xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
            if (ret != 0)
                return(-1);

            if ((out->buffer->use < MINLEN) && (chunk == len))
                goto done;

            /*
             * convert as much as possible to the parser reading buffer.
             */
            ret = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
            if ((ret < 0) && (ret != -3)) {
                xmlIOErr(XML_IO_ENCODER, NULL);
                out->error = XML_IO_ENCODER;
                return(-1);
            }
            nbchars = out->conv->use;
        } else {
            ret = xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
            if (ret != 0)
                return(-1);
            nbchars = out->buffer->use;
        }
        buf += chunk;
        len -= chunk;

        if ((nbchars < MINLEN) && (len <= 0))
            goto done;

        if (out->writecallback) {
            /*
             * second write the stuff to the I/O channel
             */
            if (out->encoder != NULL) {
                ret = out->writecallback(out->context,
                                 (const char *)out->conv->content, nbchars);
                if (ret >= 0)
                    xmlBufferShrink(out->conv, ret);
            } else {
                ret = out->writecallback(out->context,
                                 (const char *)out->buffer->content, nbchars);
                if (ret >= 0)
                    xmlBufferShrink(out->buffer, ret);
            }
            if (ret < 0) {
                xmlIOErr(XML_IO_WRITE, NULL);
                out->error = XML_IO_WRITE;
                return(ret);
            }
            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 caracters 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) ||
        (out->buffer->alloc == 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 = (out->buffer->size - out->buffer->use) - 1;

        /*
         * first handle encoding stuff.
         */
        if (out->encoder != NULL) {
            /*
             * Store the data in the incoming raw buffer
             */
            if (out->conv == NULL) {
                out->conv = xmlBufferCreate();
            }
            ret = escaping(out->buffer->content + out->buffer->use ,
                           &chunk, str, &cons);
            if ((ret < 0) || (chunk == 0)) /* chunk==0 => nothing done */
                return(-1);
            out->buffer->use += chunk;
            out->buffer->content[out->buffer->use] = 0;

            if ((out->buffer->use < MINLEN) && (cons == len))
                goto done;

            /*
             * convert as much as possible to the output buffer.
             */
            ret = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
            if ((ret < 0) && (ret != -3)) {
                xmlIOErr(XML_IO_ENCODER, NULL);
                out->error = XML_IO_ENCODER;
                return(-1);
            }
            nbchars = out->conv->use;
        } else {
            ret = escaping(out->buffer->content + out->buffer->use ,
                           &chunk, str, &cons);
            if ((ret < 0) || (chunk == 0)) /* chunk==0 => nothing done */
                return(-1);
            out->buffer->use += chunk;
            out->buffer->content[out->buffer->use] = 0;
            nbchars = out->buffer->use;
        }
        str += cons;
        len -= cons;

        if ((nbchars < MINLEN) && (len <= 0))
            goto done;

        if (out->writecallback) {
            /*
             * second write the stuff to the I/O channel
             */
            if (out->encoder != NULL) {
                ret = out->writecallback(out->context,
                                 (const char *)out->conv->content, nbchars);
                if (ret >= 0)
                    xmlBufferShrink(out->conv, ret);
            } else {
                ret = out->writecallback(out->context,
                                 (const char *)out->buffer->content, nbchars);
                if (ret >= 0)
                    xmlBufferShrink(out->buffer, ret);
            }
            if (ret < 0) {
                xmlIOErr(XML_IO_WRITE, NULL);
                out->error = XML_IO_WRITE;
                return(ret);
            }
            out->written += ret;
        } else if (out->buffer->size - out->buffer->use < MINLEN) {
            xmlBufferResize(out->buffer, out->buffer->size + 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 reading buffer.
         */
        nbchars = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
        if (nbchars < 0) {
            xmlIOErr(XML_IO_ENCODER, NULL);
            out->error = XML_IO_ENCODER;
            return(-1);
        }
    }

    /*
     * 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 *)out->conv->content, out->conv->use);
        if (ret >= 0)
            xmlBufferShrink(out->conv, ret);
    } else if (out->writecallback != NULL) {
        ret = out->writecallback(out->context,
                   (const char *)out->buffer->content, out->buffer->use);
        if (ret >= 0)
            xmlBufferShrink(out->buffer, ret);
    }
    if (ret < 0) {
        xmlIOErr(XML_IO_FLUSH, NULL);
        out->error = XML_IO_FLUSH;
        return(ret);
    }
    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;
    char sep = '/';

#ifdef _WIN32_WCE  /* easy way by now ... wince does not have dirs! */
    return NULL;
#endif

    if (xmlInputCallbackInitialized == 0)
        xmlRegisterDefaultInputCallbacks();

    if (filename == NULL) return(NULL);
#if defined(WIN32) && !defined(__CYGWIN__)
    sep = '\\';
#endif

    strncpy(dir, filename, 1023);
    dir[1023] = 0;
    cur = &dir[strlen(dir)];
    while (cur > dir) {
         if (*cur == sep) break;
         cur --;
    }
    if (*cur == sep) {
        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);
}

/****************************************************************
 *                                                              *
 *              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) {
#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);
            }
        }
    }
#else
    (void)ctxt;
#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) || defined (__DJGPP__) && !defined(__CYGWIN__)
        path = &URL[17];
#else
        path = &URL[16];
#endif
    else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
#if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
        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.
 */
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 entitites, 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);
}

#define bottom_xmlIO
#include "elfgcchack.h"
