diff options
Diffstat (limited to 'libxml2/xmlIO.c')
-rw-r--r-- | libxml2/xmlIO.c | 4186 |
1 files changed, 4186 insertions, 0 deletions
diff --git a/libxml2/xmlIO.c b/libxml2/xmlIO.c new file mode 100644 index 0000000..1a79c09 --- /dev/null +++ b/libxml2/xmlIO.c @@ -0,0 +1,4186 @@ +/* + * 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 +#ifdef HAVE_ZLIB_H +#include <zlib.h> +#endif +#ifdef HAVE_LZMA_H +#include <lzma.h> +#endif + +#if defined(WIN32) || defined(_WIN32) +#include <windows.h> +#endif + +#if defined(_WIN32_WCE) +#include <winnls.h> /* for CP_UTF8 */ +#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> + +#include "buf.h" +#include "enc.h" + +/* #define VERBOSE_FAILURE */ +/* #define DEBUG_EXTERNAL_ENTITIES */ +/* #define DEBUG_INPUT */ + +#ifdef DEBUG_INPUT +#define MINLEN 40 +#else +#define MINLEN 4000 +#endif + +/* + * Input I/O callback sets + */ +typedef struct _xmlInputCallback { + xmlInputMatchCallback matchcallback; + xmlInputOpenCallback opencallback; + xmlInputReadCallback readcallback; + xmlInputCloseCallback closecallback; +} xmlInputCallback; + +#define MAX_INPUT_CALLBACK 15 + +static xmlInputCallback xmlInputCallbackTable[MAX_INPUT_CALLBACK]; +static int xmlInputCallbackNr = 0; +static int xmlInputCallbackInitialized = 0; + +#ifdef LIBXML_OUTPUT_ENABLED +/* + * Output I/O callback sets + */ +typedef struct _xmlOutputCallback { + xmlOutputMatchCallback matchcallback; + xmlOutputOpenCallback opencallback; + xmlOutputWriteCallback writecallback; + xmlOutputCloseCallback closecallback; +} xmlOutputCallback; + +#define MAX_OUTPUT_CALLBACK 15 + +static xmlOutputCallback xmlOutputCallbackTable[MAX_OUTPUT_CALLBACK]; +static int xmlOutputCallbackNr = 0; +static int xmlOutputCallbackInitialized = 0; + +xmlOutputBufferPtr +xmlAllocOutputBufferInternal(xmlCharEncodingHandlerPtr encoder); +#endif /* LIBXML_OUTPUT_ENABLED */ + +/************************************************************************ + * * + * Tree memory error handler * + * * + ************************************************************************/ + +static const char *IOerr[] = { + "Unknown IO error", /* UNKNOWN */ + "Permission denied", /* EACCES */ + "Resource temporarily unavailable",/* EAGAIN */ + "Bad file descriptor", /* EBADF */ + "Bad message", /* EBADMSG */ + "Resource busy", /* EBUSY */ + "Operation canceled", /* ECANCELED */ + "No child processes", /* ECHILD */ + "Resource deadlock avoided",/* EDEADLK */ + "Domain error", /* EDOM */ + "File exists", /* EEXIST */ + "Bad address", /* EFAULT */ + "File too large", /* EFBIG */ + "Operation in progress", /* EINPROGRESS */ + "Interrupted function call",/* EINTR */ + "Invalid argument", /* EINVAL */ + "Input/output error", /* EIO */ + "Is a directory", /* EISDIR */ + "Too many open files", /* EMFILE */ + "Too many links", /* EMLINK */ + "Inappropriate message buffer length",/* EMSGSIZE */ + "Filename too long", /* ENAMETOOLONG */ + "Too many open files in system",/* ENFILE */ + "No such device", /* ENODEV */ + "No such file or directory",/* ENOENT */ + "Exec format error", /* ENOEXEC */ + "No locks available", /* ENOLCK */ + "Not enough space", /* ENOMEM */ + "No space left on device", /* ENOSPC */ + "Function not implemented", /* ENOSYS */ + "Not a directory", /* ENOTDIR */ + "Directory not empty", /* ENOTEMPTY */ + "Not supported", /* ENOTSUP */ + "Inappropriate I/O control operation",/* ENOTTY */ + "No such device or address",/* ENXIO */ + "Operation not permitted", /* EPERM */ + "Broken pipe", /* EPIPE */ + "Result too large", /* ERANGE */ + "Read-only file system", /* EROFS */ + "Invalid seek", /* ESPIPE */ + "No such process", /* ESRCH */ + "Operation timed out", /* ETIMEDOUT */ + "Improper link", /* EXDEV */ + "Attempt to load network entity %s", /* XML_IO_NETWORK_ATTEMPT */ + "encoder error", /* XML_IO_ENCODER */ + "flush error", + "write error", + "no input", + "buffer full", + "loading error", + "not a socket", /* ENOTSOCK */ + "already connected", /* EISCONN */ + "connection refused", /* ECONNREFUSED */ + "unreachable network", /* ENETUNREACH */ + "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; +} + +#ifdef HAVE_ZLIB_H +static gzFile +xmlWrapGzOpenUtf8(const char *path, const char *mode) +{ + gzFile fd; + wchar_t *wPath; + + fd = gzopen (path, mode); + if (fd) + return fd; + + wPath = __xmlIOWin32UTF8ToWChar(path); + if(wPath) + { + int d, m = (strstr(mode, "r") ? O_RDONLY : O_RDWR); +#ifdef _O_BINARY + m |= (strstr(mode, "b") ? _O_BINARY : 0); +#endif + d = _wopen(wPath, m); + if (d >= 0) + fd = gzdopen(d, mode); + xmlFree(wPath); + } + + return fd; +} +#endif + +/** + * xmlWrapStatUtf8: + * @path: the path in utf-8 encoding + * @info: structure that stores results + * + * function obtains information about the file or directory + * + */ +static int +xmlWrapStatUtf8(const char *path,struct stat *info) +{ +#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; +#ifdef HAVE_ZLIB_H +typedef gzFile (* xmlWrapGzOpenFunc) (const char *f, const char *mode); +static xmlWrapGzOpenFunc xmlWrapGzOpen = gzopen; +#endif +/** + * 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; +#ifdef HAVE_ZLIB_H + xmlWrapGzOpen = xmlWrapGzOpenUtf8; +#endif + } else { + xmlWrapStat = xmlWrapStatNative; + xmlWrapOpen = xmlWrapOpenNative; +#ifdef HAVE_ZLIB_H + xmlWrapGzOpen = gzopen; +#endif + } + + 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__) + /* + * On Windows stat and wstat do not work with long pathname, + * which start with '\\?\' + */ + if ((path[0] == '\\') && (path[1] == '\\') && (path[2] == '?') && + (path[3] == '\\') ) + return 1; + + if (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; +} + +/** + * xmlNop: + * + * No Operation function, does nothing, no input + * + * Returns zero + */ +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 = filename; + FILE *fd; + + if (filename == NULL) + return(NULL); + + if (!strcmp(filename, "-")) { + fd = stdin; + return((void *) fd); + } + + if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) { +#if defined (_WIN32) || 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 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:/", 6)) { + /* lots of generators seems to lazy to read RFC 1738 */ +#if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__) + path = &filename[6]; +#else + path = &filename[5]; +#endif + } + + 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; + + retval = xmlFileOpen_real(filename); + if (retval == NULL) { + unescaped = xmlURIUnescapeString(filename, 0, NULL); + if (unescaped != NULL) { + retval = xmlFileOpen_real(unescaped); + xmlFree(unescaped); + } + } + + return retval; +} + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlFileOpenW: + * @filename: the URI for matching + * + * output to from FILE *, + * if @filename is "-" then the standard output is used + * + * Returns an I/O context or NULL in case of error + */ +static void * +xmlFileOpenW (const char *filename) { + const char *path = NULL; + FILE *fd; + + if (!strcmp(filename, "-")) { + fd = stdout; + return((void *) fd); + } + + if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) +#if defined (_WIN32) || 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, "-")) { + int duped_fd = dup(fileno(stdin)); + fd = gzdopen(duped_fd, "rb"); + if (fd == Z_NULL && duped_fd >= 0) { + close(duped_fd); /* gzdOpen() does not close on failure */ + } + + return((void *) fd); + } + + if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) +#if defined (_WIN32) || 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 = xmlWrapGzOpen(path, "rb"); +#else + fd = gzopen(path, "rb"); +#endif + return((void *) fd); +} + +/** + * xmlGzfileOpen: + * @filename: the URI for matching + * + * Wrapper around xmlGzfileOpen if the open fais, it will + * try to unescape @filename + */ +static void * +xmlGzfileOpen (const char *filename) { + char *unescaped; + void *retval; + + retval = xmlGzfileOpen_real(filename); + if (retval == NULL) { + unescaped = xmlURIUnescapeString(filename, 0, NULL); + if (unescaped != NULL) { + retval = xmlGzfileOpen_real(unescaped); + } + xmlFree(unescaped); + } + return retval; +} + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlGzfileOpenW: + * @filename: the URI for matching + * @compression: the compression factor (0 - 9 included) + * + * input from compressed file open + * if @filename is " " then the standard input is used + * + * Returns an I/O context or NULL in case of error + */ +static void * +xmlGzfileOpenW (const char *filename, int compression) { + const char *path = NULL; + char mode[15]; + gzFile fd; + + snprintf(mode, sizeof(mode), "wb%d", compression); + if (!strcmp(filename, "-")) { + int duped_fd = dup(fileno(stdout)); + fd = gzdopen(duped_fd, "rb"); + if (fd == Z_NULL && duped_fd >= 0) { + close(duped_fd); /* gzdOpen() does not close on failure */ + } + + return((void *) fd); + } + + if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) +#if defined (_WIN32) || 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 = xmlWrapGzOpen(path, mode); +#else + fd = gzopen(path, mode); +#endif + return((void *) fd); +} +#endif /* LIBXML_OUTPUT_ENABLED */ + +/** + * xmlGzfileRead: + * @context: the I/O context + * @buffer: where to drop data + * @len: number of bytes to write + * + * Read @len bytes to @buffer from the compressed I/O channel. + * + * Returns the number of bytes 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_LZMA_ENABLED +/************************************************************************ + * * + * I/O for compressed file accesses * + * * + ************************************************************************/ +#include "xzlib.h" +/** + * xmlXzfileMatch: + * @filename: the URI for matching + * + * input from compressed file test + * + * Returns 1 if matches, 0 otherwise + */ +static int +xmlXzfileMatch (const char *filename ATTRIBUTE_UNUSED) { + return(1); +} + +/** + * xmlXzFileOpen_real: + * @filename: the URI for matching + * + * input from compressed file open + * if @filename is " " then the standard input is used + * + * Returns an I/O context or NULL in case of error + */ +static void * +xmlXzfileOpen_real (const char *filename) { + const char *path = NULL; + xzFile fd; + + if (!strcmp(filename, "-")) { + fd = __libxml2_xzdopen(dup(fileno(stdin)), "rb"); + return((void *) fd); + } + + if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) { + path = &filename[16]; + } else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) { + path = &filename[7]; + } else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:/", 6)) { + /* lots of generators seems to lazy to read RFC 1738 */ + path = &filename[5]; + } else + path = filename; + + if (path == NULL) + return(NULL); + if (!xmlCheckFilename(path)) + return(NULL); + + fd = __libxml2_xzopen(path, "rb"); + return((void *) fd); +} + +/** + * xmlXzfileOpen: + * @filename: the URI for matching + * + * Wrapper around xmlXzfileOpen_real that try it with an unescaped + * version of @filename, if this fails fallback to @filename + * + * Returns a handler or NULL in case or failure + */ +static void * +xmlXzfileOpen (const char *filename) { + char *unescaped; + void *retval; + + retval = xmlXzfileOpen_real(filename); + if (retval == NULL) { + unescaped = xmlURIUnescapeString(filename, 0, NULL); + if (unescaped != NULL) { + retval = xmlXzfileOpen_real(unescaped); + } + xmlFree(unescaped); + } + + return retval; +} + +/** + * xmlXzfileRead: + * @context: the I/O context + * @buffer: where to drop data + * @len: number of bytes to write + * + * Read @len bytes to @buffer from the compressed I/O channel. + * + * Returns the number of bytes written + */ +static int +xmlXzfileRead (void * context, char * buffer, int len) { + int ret; + + ret = __libxml2_xzread((xzFile) context, &buffer[0], len); + if (ret < 0) xmlIOErr(0, "xzread()"); + return(ret); +} + +/** + * xmlXzfileClose: + * @context: the I/O context + * + * Close a compressed I/O channel + */ +static int +xmlXzfileClose (void * context) { + int ret; + + ret = (__libxml2_xzclose((xzFile) context) == LZMA_OK ) ? 0 : -1; + if (ret < 0) xmlIOErr(0, "xzclose()"); + return(ret); +} +#endif /* LIBXML_LZMA_ENABLED */ + +#ifdef LIBXML_HTTP_ENABLED +/************************************************************************ + * * + * I/O for HTTP file accesses * + * * + ************************************************************************/ + +#ifdef LIBXML_OUTPUT_ENABLED +typedef struct xmlIOHTTPWriteCtxt_ +{ + int compression; + + char * uri; + + void * doc_buff; + +} xmlIOHTTPWriteCtxt, *xmlIOHTTPWriteCtxtPtr; + +#ifdef 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, + "xmlCreateZMemBuff: %s %d\n", + "Error initializing compression context. ZLIB error:", + z_err ); + xmlIOErr(XML_IO_WRITE, (const char *) msg); + return ( NULL ); + } + + /* Set the header data. The CRC will be needed for the trailer */ + buff->crc = crc32( 0L, NULL, 0 ); + hdr_lgth = snprintf( (char *)buff->zbuff, buff->size, + "%c%c%c%c%c%c%c%c%c%c", + GZ_MAGIC1, GZ_MAGIC2, Z_DEFLATED, + 0, 0, 0, 0, 0, 0, LXML_ZLIB_OS_CODE ); + buff->zctrl.next_out = buff->zbuff + hdr_lgth; + buff->zctrl.avail_out = buff->size - hdr_lgth; + + return ( buff ); +} + +/** + * xmlZMemBuffExtend + * @buff: Buffer used to compress and consolidate data. + * @ext_amt: Number of bytes to extend the buffer. + * + * Extend the internal buffer used to store the compressed data by the + * specified amount. + * + * Returns 0 on success or -1 on failure to extend the buffer. On failure + * the original buffer still exists at the original size. + */ +static int +xmlZMemBuffExtend( xmlZMemBuffPtr buff, size_t ext_amt ) { + + int rc = -1; + size_t new_size; + size_t cur_used; + + unsigned char * tmp_ptr = NULL; + + if ( buff == NULL ) + return ( -1 ); + + else if ( ext_amt == 0 ) + return ( 0 ); + + cur_used = buff->zctrl.next_out - buff->zbuff; + new_size = buff->size + ext_amt; + +#ifdef DEBUG_HTTP + if ( cur_used > new_size ) + xmlGenericError( xmlGenericErrorContext, + "xmlZMemBuffExtend: %s\n%s %d bytes.\n", + "Buffer overwrite detected during compressed memory", + "buffer extension. Overflowed by", + (cur_used - new_size ) ); +#endif + + tmp_ptr = xmlRealloc( buff->zbuff, new_size ); + if ( tmp_ptr != NULL ) { + rc = 0; + buff->size = new_size; + buff->zbuff = tmp_ptr; + buff->zctrl.next_out = tmp_ptr + cur_used; + buff->zctrl.avail_out = new_size - cur_used; + } + else { + xmlChar msg[500]; + xmlStrPrintf(msg, 500, + "xmlZMemBuffExtend: %s %lu bytes.\n", + "Allocation failure extending output buffer to", + new_size ); + xmlIOErr(XML_IO_WRITE, (const char *) msg); + } + + return ( rc ); +} + +/** + * xmlZMemBuffAppend + * @buff: Buffer used to compress and consolidate data + * @src: Uncompressed source content to append to buffer + * @len: Length of source data to append to buffer + * + * Compress and append data to the internal buffer. The data buffer + * will be expanded if needed to store the additional data. + * + * Returns the number of bytes appended to the buffer or -1 on error. + */ +static int +xmlZMemBuffAppend( xmlZMemBuffPtr buff, const char * src, int len ) { + + int z_err; + size_t min_accept; + + if ( ( buff == NULL ) || ( src == NULL ) ) + return ( -1 ); + + buff->zctrl.avail_in = len; + buff->zctrl.next_in = (unsigned char *)src; + while ( buff->zctrl.avail_in > 0 ) { + /* + ** Extend the buffer prior to deflate call if a reasonable amount + ** of output buffer space is not available. + */ + min_accept = buff->zctrl.avail_in / DFLT_ZLIB_RATIO; + if ( buff->zctrl.avail_out <= min_accept ) { + if ( xmlZMemBuffExtend( buff, buff->size ) == -1 ) + return ( -1 ); + } + + z_err = deflate( &buff->zctrl, Z_NO_FLUSH ); + if ( z_err != Z_OK ) { + xmlChar msg[500]; + xmlStrPrintf(msg, 500, + "xmlZMemBuffAppend: %s %d %s - %d", + "Compression error while appending", + len, "bytes to buffer. ZLIB error", z_err ); + xmlIOErr(XML_IO_WRITE, (const char *) msg); + return ( -1 ); + } + } + + buff->crc = crc32( buff->crc, (unsigned char *)src, len ); + + return ( len ); +} + +/** + * xmlZMemBuffGetContent + * @buff: Compressed memory content buffer + * @data_ref: Pointer reference to point to compressed content + * + * Flushes the compression buffers, appends gzip file trailers and + * returns the compressed content and length of the compressed data. + * NOTE: The gzip trailer code here is plagiarized from zlib source. + * + * Returns the length of the compressed data or -1 on error. + */ +static int +xmlZMemBuffGetContent( xmlZMemBuffPtr buff, char ** data_ref ) { + + int zlgth = -1; + int z_err; + + if ( ( buff == NULL ) || ( data_ref == NULL ) ) + return ( -1 ); + + /* Need to loop until compression output buffers are flushed */ + + do + { + z_err = deflate( &buff->zctrl, Z_FINISH ); + if ( z_err == Z_OK ) { + /* In this case Z_OK means more buffer space needed */ + + if ( xmlZMemBuffExtend( buff, buff->size ) == -1 ) + return ( -1 ); + } + } + while ( z_err == Z_OK ); + + /* If the compression state is not Z_STREAM_END, some error occurred */ + + if ( z_err == Z_STREAM_END ) { + + /* Need to append the gzip data trailer */ + + if ( buff->zctrl.avail_out < ( 2 * sizeof( unsigned long ) ) ) { + if ( xmlZMemBuffExtend(buff, (2 * sizeof(unsigned long))) == -1 ) + return ( -1 ); + } + + /* + ** For whatever reason, the CRC and length data are pushed out + ** in reverse byte order. So a memcpy can't be used here. + */ + + append_reverse_ulong( buff, buff->crc ); + append_reverse_ulong( buff, buff->zctrl.total_in ); + + zlgth = buff->zctrl.next_out - buff->zbuff; + *data_ref = (char *)buff->zbuff; + } + + else { + xmlChar msg[500]; + xmlStrPrintf(msg, 500, + "xmlZMemBuffGetContent: %s - %d\n", + "Error flushing zlib buffers. Error code", z_err ); + xmlIOErr(XML_IO_WRITE, (const char *) msg); + } + + return ( zlgth ); +} +#endif /* LIBXML_OUTPUT_ENABLED */ +#endif /* 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 = xmlAllocOutputBufferInternal(NULL); + } + + if (ctxt->doc_buff == NULL) { + xmlFreeHTTPWriteCtxt(ctxt); + ctxt = NULL; + } + + return (ctxt); +} +#endif /* LIBXML_OUTPUT_ENABLED */ + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlIOHTTPDfltOpenW + * @post_uri: The destination URI for this document. + * + * Calls xmlIOHTTPOpenW with no compression to set up for a subsequent + * HTTP post command. This function should generally not be used as + * the open callback is short circuited in xmlOutputBufferCreateFile. + * + * Returns a pointer to the new IO context. + */ +static void * +xmlIOHTTPDfltOpenW( const char * post_uri ) { + return ( xmlIOHTTPOpenW( post_uri, 0 ) ); +} +#endif /* LIBXML_OUTPUT_ENABLED */ + +/** + * xmlIOHTTPRead: + * @context: the I/O context + * @buffer: where to drop data + * @len: number of bytes to write + * + * Read @len bytes to @buffer from the I/O channel. + * + * Returns the number of bytes written + */ +int +xmlIOHTTPRead(void * context, char * buffer, int len) { + if ((buffer == NULL) || (len < 0)) return(-1); + return(xmlNanoHTTPRead(context, &buffer[0], len)); +} + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlIOHTTPWrite + * @context: previously opened writing context + * @buffer: data to output to temporary buffer + * @len: bytes to output + * + * Collect data from memory buffer into a temporary file for later + * processing. + * + * Returns number of bytes written. + */ + +static int +xmlIOHTTPWrite( void * context, const char * buffer, int len ) { + + xmlIOHTTPWriteCtxtPtr ctxt = context; + + if ( ( ctxt == NULL ) || ( ctxt->doc_buff == NULL ) || ( buffer == NULL ) ) + return ( -1 ); + + if ( len > 0 ) { + + /* Use gzwrite or fwrite as previously setup in the open call */ + +#ifdef 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, + "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 *) xmlBufContent(dctxt->buffer); + content_lgth = xmlBufUse(dctxt->buffer); + } + + if ( http_content == NULL ) { + xmlChar msg[500]; + xmlStrPrintf(msg, 500, + "xmlIOHTTPCloseWrite: %s '%s' %s '%s'.\n", + "Error retrieving content.\nUnable to", + http_mthd, "data to URI", ctxt->uri ); + xmlIOErr(XML_IO_WRITE, (const char *) msg); + } + + else { + + http_ctxt = xmlNanoHTTPMethod( ctxt->uri, http_mthd, http_content, + &content_type, content_encoding, + content_lgth ); + + if ( http_ctxt != NULL ) { +#ifdef DEBUG_HTTP + /* If testing/debugging - dump reply with request content */ + + FILE * tst_file = NULL; + char buffer[ 4096 ]; + char * dump_name = NULL; + int avail; + + xmlGenericError( xmlGenericErrorContext, + "xmlNanoHTTPCloseWrite: HTTP %s to\n%s returned %d.\n", + http_mthd, ctxt->uri, + xmlNanoHTTPReturnCode( http_ctxt ) ); + + /* + ** Since either content or reply may be gzipped, + ** dump them to separate files instead of the + ** standard error context. + */ + + dump_name = tempnam( NULL, "lxml" ); + if ( dump_name != NULL ) { + (void)snprintf( buffer, sizeof(buffer), "%s.content", dump_name ); + + tst_file = fopen( buffer, "wb" ); + if ( tst_file != NULL ) { + xmlGenericError( xmlGenericErrorContext, + "Transmitted content saved in file: %s\n", buffer ); + + fwrite( http_content, sizeof( char ), + content_lgth, tst_file ); + fclose( tst_file ); + } + + (void)snprintf( buffer, sizeof(buffer), "%s.reply", dump_name ); + tst_file = fopen( buffer, "wb" ); + if ( tst_file != NULL ) { + xmlGenericError( xmlGenericErrorContext, + "Reply content saved in file: %s\n", buffer ); + + + while ( (avail = xmlNanoHTTPRead( http_ctxt, + buffer, sizeof( buffer ) )) > 0 ) { + + fwrite( buffer, sizeof( char ), avail, tst_file ); + } + + fclose( tst_file ); + } + + free( dump_name ); + } +#endif /* DEBUG_HTTP */ + + http_rtn = xmlNanoHTTPReturnCode( http_ctxt ); + if ( ( http_rtn >= 200 ) && ( http_rtn < 300 ) ) + close_rc = 0; + else { + xmlChar msg[500]; + xmlStrPrintf(msg, 500, + "xmlIOHTTPCloseWrite: HTTP '%s' of %d %s\n'%s' %s %d\n", + http_mthd, content_lgth, + "bytes to URI", ctxt->uri, + "failed. HTTP return code:", http_rtn ); + xmlIOErr(XML_IO_WRITE, (const char *) msg); + } + + xmlNanoHTTPClose( http_ctxt ); + xmlFree( content_type ); + } + } + + /* Final cleanups */ + + xmlFreeHTTPWriteCtxt( ctxt ); + + return ( close_rc ); +} + +/** + * xmlIOHTTPClosePut + * + * @context: The I/O context + * + * Close the transmit HTTP I/O channel and actually send data using a PUT + * HTTP method. + */ +static int +xmlIOHTTPClosePut( void * ctxt ) { + return ( xmlIOHTTPCloseWrite( ctxt, "PUT" ) ); +} + + +/** + * xmlIOHTTPClosePost + * + * @context: The I/O context + * + * Close the transmit HTTP I/O channel and actually send data using a POST + * HTTP method. + */ +static int +xmlIOHTTPClosePost( void * ctxt ) { + return ( xmlIOHTTPCloseWrite( ctxt, "POST" ) ); +} +#endif /* LIBXML_OUTPUT_ENABLED */ + +#endif /* LIBXML_HTTP_ENABLED */ + +#ifdef LIBXML_FTP_ENABLED +/************************************************************************ + * * + * I/O for FTP file accesses * + * * + ************************************************************************/ +/** + * xmlIOFTPMatch: + * @filename: the URI for matching + * + * check if the URI matches an FTP one + * + * Returns 1 if matches, 0 otherwise + */ +int +xmlIOFTPMatch (const char *filename) { + if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "ftp://", 6)) + return(1); + return(0); +} + +/** + * xmlIOFTPOpen: + * @filename: the URI for matching + * + * open an FTP I/O channel + * + * Returns an I/O context or NULL in case of error + */ +void * +xmlIOFTPOpen (const char *filename) { + return(xmlNanoFTPOpen(filename)); +} + +/** + * xmlIOFTPRead: + * @context: the I/O context + * @buffer: where to drop data + * @len: number of bytes to write + * + * Read @len bytes to @buffer from the I/O channel. + * + * Returns the number of bytes written + */ +int +xmlIOFTPRead(void * context, char * buffer, int len) { + if ((buffer == NULL) || (len < 0)) return(-1); + return(xmlNanoFTPRead(context, &buffer[0], len)); +} + +/** + * xmlIOFTPClose: + * @context: the I/O context + * + * Close an FTP I/O channel + * + * Returns 0 + */ +int +xmlIOFTPClose (void * context) { + return ( xmlNanoFTPClose(context) ); +} +#endif /* LIBXML_FTP_ENABLED */ + + +/** + * xmlRegisterInputCallbacks: + * @matchFunc: the xmlInputMatchCallback + * @openFunc: the xmlInputOpenCallback + * @readFunc: the xmlInputReadCallback + * @closeFunc: the xmlInputCloseCallback + * + * Register a new set of I/O callback for handling parser input. + * + * Returns the registered handler number or -1 in case of error + */ +int +xmlRegisterInputCallbacks(xmlInputMatchCallback matchFunc, + xmlInputOpenCallback openFunc, xmlInputReadCallback readFunc, + xmlInputCloseCallback closeFunc) { + if (xmlInputCallbackNr >= MAX_INPUT_CALLBACK) { + return(-1); + } + xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = matchFunc; + xmlInputCallbackTable[xmlInputCallbackNr].opencallback = openFunc; + xmlInputCallbackTable[xmlInputCallbackNr].readcallback = readFunc; + xmlInputCallbackTable[xmlInputCallbackNr].closecallback = closeFunc; + xmlInputCallbackInitialized = 1; + return(xmlInputCallbackNr++); +} + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlRegisterOutputCallbacks: + * @matchFunc: the xmlOutputMatchCallback + * @openFunc: the xmlOutputOpenCallback + * @writeFunc: the xmlOutputWriteCallback + * @closeFunc: the xmlOutputCloseCallback + * + * Register a new set of I/O callback for handling output. + * + * Returns the registered handler number or -1 in case of error + */ +int +xmlRegisterOutputCallbacks(xmlOutputMatchCallback matchFunc, + xmlOutputOpenCallback openFunc, xmlOutputWriteCallback writeFunc, + xmlOutputCloseCallback closeFunc) { + if (xmlOutputCallbackNr >= MAX_OUTPUT_CALLBACK) { + return(-1); + } + xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = matchFunc; + xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = openFunc; + xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = writeFunc; + xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = closeFunc; + xmlOutputCallbackInitialized = 1; + return(xmlOutputCallbackNr++); +} +#endif /* LIBXML_OUTPUT_ENABLED */ + +/** + * xmlRegisterDefaultInputCallbacks: + * + * Registers the default compiled-in I/O handlers. + */ +void +xmlRegisterDefaultInputCallbacks(void) { + if (xmlInputCallbackInitialized) + return; + +#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_LZMA_ENABLED + xmlRegisterInputCallbacks(xmlXzfileMatch, xmlXzfileOpen, + xmlXzfileRead, xmlXzfileClose); +#endif /* LIBXML_LZMA_ENABLED */ + +#ifdef LIBXML_HTTP_ENABLED + xmlRegisterInputCallbacks(xmlIOHTTPMatch, xmlIOHTTPOpen, + xmlIOHTTPRead, xmlIOHTTPClose); +#endif /* LIBXML_HTTP_ENABLED */ + +#ifdef LIBXML_FTP_ENABLED + xmlRegisterInputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen, + xmlIOFTPRead, xmlIOFTPClose); +#endif /* LIBXML_FTP_ENABLED */ + xmlInputCallbackInitialized = 1; +} + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlRegisterDefaultOutputCallbacks: + * + * Registers the default compiled-in I/O handlers. + */ +void +xmlRegisterDefaultOutputCallbacks (void) { + if (xmlOutputCallbackInitialized) + return; + +#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 = xmlBufCreateSize(2 * xmlDefaultBufferSize); + if (ret->buffer == NULL) { + xmlFree(ret); + return(NULL); + } + xmlBufSetAllocationScheme(ret->buffer, XML_BUFFER_ALLOC_DOUBLEIT); + ret->encoder = xmlGetCharEncodingHandler(enc); + if (ret->encoder != NULL) + ret->raw = xmlBufCreateSize(2 * xmlDefaultBufferSize); + else + ret->raw = NULL; + ret->readcallback = NULL; + ret->closecallback = NULL; + ret->context = NULL; + ret->compressed = -1; + ret->rawconsumed = 0; + + return(ret); +} + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlAllocOutputBuffer: + * @encoder: the encoding converter or NULL + * + * Create a buffered parser output + * + * Returns the new parser output or NULL + */ +xmlOutputBufferPtr +xmlAllocOutputBuffer(xmlCharEncodingHandlerPtr encoder) { + xmlOutputBufferPtr ret; + + ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer)); + if (ret == NULL) { + xmlIOErrMemory("creating output buffer"); + return(NULL); + } + memset(ret, 0, (size_t) sizeof(xmlOutputBuffer)); + ret->buffer = xmlBufCreate(); + if (ret->buffer == NULL) { + xmlFree(ret); + return(NULL); + } + + /* try to avoid a performance problem with Windows realloc() */ + if (xmlBufGetAllocationScheme(ret->buffer) == XML_BUFFER_ALLOC_EXACT) + xmlBufSetAllocationScheme(ret->buffer, XML_BUFFER_ALLOC_DOUBLEIT); + + ret->encoder = encoder; + if (encoder != NULL) { + ret->conv = xmlBufCreateSize(4000); + if (ret->conv == NULL) { + xmlFree(ret); + return(NULL); + } + + /* + * This call is designed to initiate the encoder state + */ + xmlCharEncOutput(ret, 1); + } else + ret->conv = NULL; + ret->writecallback = NULL; + ret->closecallback = NULL; + ret->context = NULL; + ret->written = 0; + + return(ret); +} + +/** + * xmlAllocOutputBufferInternal: + * @encoder: the encoding converter or NULL + * + * Create a buffered parser output + * + * Returns the new parser output or NULL + */ +xmlOutputBufferPtr +xmlAllocOutputBufferInternal(xmlCharEncodingHandlerPtr encoder) { + xmlOutputBufferPtr ret; + + ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer)); + if (ret == NULL) { + xmlIOErrMemory("creating output buffer"); + return(NULL); + } + memset(ret, 0, (size_t) sizeof(xmlOutputBuffer)); + ret->buffer = xmlBufCreate(); + if (ret->buffer == NULL) { + xmlFree(ret); + return(NULL); + } + + + /* + * For conversion buffers we use the special IO handling + */ + xmlBufSetAllocationScheme(ret->buffer, XML_BUFFER_ALLOC_IO); + + ret->encoder = encoder; + if (encoder != NULL) { + ret->conv = xmlBufCreateSize(4000); + if (ret->conv == NULL) { + xmlFree(ret); + return(NULL); + } + + /* + * This call is designed to initiate the encoder state + */ + xmlCharEncOutput(ret, 1); + } else + ret->conv = NULL; + ret->writecallback = NULL; + ret->closecallback = NULL; + ret->context = NULL; + ret->written = 0; + + return(ret); +} + +#endif /* LIBXML_OUTPUT_ENABLED */ + +/** + * xmlFreeParserInputBuffer: + * @in: a buffered parser input + * + * Free up the memory used by a buffered parser input + */ +void +xmlFreeParserInputBuffer(xmlParserInputBufferPtr in) { + if (in == NULL) return; + + if (in->raw) { + xmlBufFree(in->raw); + in->raw = NULL; + } + if (in->encoder != NULL) { + xmlCharEncCloseFunc(in->encoder); + } + if (in->closecallback != NULL) { + in->closecallback(in->context); + } + if (in->buffer != NULL) { + xmlBufFree(in->buffer); + in->buffer = NULL; + } + + xmlFree(in); +} + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlOutputBufferClose: + * @out: a buffered output + * + * flushes and close the output I/O channel + * and free up all the associated resources + * + * Returns the number of byte written or -1 in case of error. + */ +int +xmlOutputBufferClose(xmlOutputBufferPtr out) +{ + int written; + int err_rc = 0; + + if (out == NULL) + return (-1); + if (out->writecallback != NULL) + xmlOutputBufferFlush(out); + if (out->closecallback != NULL) { + err_rc = out->closecallback(out->context); + } + written = out->written; + if (out->conv) { + xmlBufFree(out->conv); + out->conv = NULL; + } + if (out->encoder != NULL) { + xmlCharEncCloseFunc(out->encoder); + } + if (out->buffer != NULL) { + xmlBufFree(out->buffer); + out->buffer = NULL; + } + + if (out->error) + err_rc = -1; + xmlFree(out); + return ((err_rc == 0) ? written : err_rc); +} +#endif /* LIBXML_OUTPUT_ENABLED */ + +xmlParserInputBufferPtr +__xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) { + xmlParserInputBufferPtr ret; + int i = 0; + void *context = NULL; + + if (xmlInputCallbackInitialized == 0) + xmlRegisterDefaultInputCallbacks(); + + if (URI == NULL) return(NULL); + + /* + * Try to find one of the input accept method accepting that scheme + * Go in reverse to give precedence to user defined handlers. + */ + if (context == NULL) { + for (i = xmlInputCallbackNr - 1;i >= 0;i--) { + if ((xmlInputCallbackTable[i].matchcallback != NULL) && + (xmlInputCallbackTable[i].matchcallback(URI) != 0)) { + context = xmlInputCallbackTable[i].opencallback(URI); + if (context != NULL) { + break; + } + } + } + } + if (context == NULL) { + return(NULL); + } + + /* + * Allocate the Input buffer front-end. + */ + ret = xmlAllocParserInputBuffer(enc); + if (ret != NULL) { + ret->context = context; + ret->readcallback = xmlInputCallbackTable[i].readcallback; + ret->closecallback = xmlInputCallbackTable[i].closecallback; +#ifdef 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 +#ifdef LIBXML_LZMA_ENABLED + if ((xmlInputCallbackTable[i].opencallback == xmlXzfileOpen) && + (strcmp(URI, "-") != 0)) { + ret->compressed = __libxml2_xzcompressed(context); + } +#endif + } + else + xmlInputCallbackTable[i].closecallback (context); + + return(ret); +} + +/** + * xmlParserInputBufferCreateFilename: + * @URI: a C string containing the URI or filename + * @enc: the charset encoding if known + * + * Create a buffered parser input for the progressive parsing of a file + * If filename is "-' then we use stdin as the input. + * Automatic support for ZLIB/Compress compressed document is provided + * by default if found at compile-time. + * Do an encoding check if enc == XML_CHAR_ENCODING_NONE + * + * Returns the new parser input or NULL + */ +xmlParserInputBufferPtr +xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) { + if ((xmlParserInputBufferCreateFilenameValue)) { + return xmlParserInputBufferCreateFilenameValue(URI, enc); + } + return __xmlParserInputBufferCreateFilename(URI, enc); +} + +#ifdef LIBXML_OUTPUT_ENABLED +xmlOutputBufferPtr +__xmlOutputBufferCreateFilename(const char *URI, + xmlCharEncodingHandlerPtr encoder, + int compression ATTRIBUTE_UNUSED) { + xmlOutputBufferPtr ret; + xmlURIPtr puri; + int i = 0; + void *context = NULL; + char *unescaped = NULL; +#ifdef 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 = xmlAllocOutputBufferInternal(encoder); + if (ret != NULL) { + ret->context = context; + ret->writecallback = xmlGzfileWrite; + ret->closecallback = xmlGzfileClose; + } + xmlFree(unescaped); + return(ret); + } + } +#endif + for (i = xmlOutputCallbackNr - 1;i >= 0;i--) { + if ((xmlOutputCallbackTable[i].matchcallback != NULL) && + (xmlOutputCallbackTable[i].matchcallback(unescaped) != 0)) { +#if defined(LIBXML_HTTP_ENABLED) && defined(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 = xmlAllocOutputBufferInternal(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 = xmlAllocOutputBufferInternal(encoder); + if (ret != NULL) { + ret->context = context; + ret->writecallback = xmlOutputCallbackTable[i].writecallback; + ret->closecallback = xmlOutputCallbackTable[i].closecallback; + } + return(ret); +} + +/** + * xmlOutputBufferCreateFilename: + * @URI: a C string containing the URI or filename + * @encoder: the encoding converter or NULL + * @compression: the compression ration (0 none, 9 max). + * + * Create a buffered output for the progressive saving of a file + * If filename is "-' then we use stdout as the output. + * Automatic support for ZLIB/Compress compressed document is provided + * by default if found at compile-time. + * TODO: currently if compression is set, the library only support + * writing to a local file. + * + * Returns the new output or NULL + */ +xmlOutputBufferPtr +xmlOutputBufferCreateFilename(const char *URI, + xmlCharEncodingHandlerPtr encoder, + int compression ATTRIBUTE_UNUSED) { + if ((xmlOutputBufferCreateFilenameValue)) { + return xmlOutputBufferCreateFilenameValue(URI, encoder, compression); + } + return __xmlOutputBufferCreateFilename(URI, encoder, compression); +} +#endif /* LIBXML_OUTPUT_ENABLED */ + +/** + * xmlParserInputBufferCreateFile: + * @file: a FILE* + * @enc: the charset encoding if known + * + * Create a buffered parser input for the progressive parsing of a FILE * + * buffered C I/O + * + * Returns the new parser input or NULL + */ +xmlParserInputBufferPtr +xmlParserInputBufferCreateFile(FILE *file, xmlCharEncoding enc) { + xmlParserInputBufferPtr ret; + + if (xmlInputCallbackInitialized == 0) + xmlRegisterDefaultInputCallbacks(); + + if (file == NULL) return(NULL); + + ret = xmlAllocParserInputBuffer(enc); + if (ret != NULL) { + ret->context = file; + ret->readcallback = xmlFileRead; + ret->closecallback = xmlFileFlush; + } + + return(ret); +} + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlOutputBufferCreateFile: + * @file: a FILE* + * @encoder: the encoding converter or NULL + * + * Create a buffered output for the progressive saving to a FILE * + * buffered C I/O + * + * Returns the new parser output or NULL + */ +xmlOutputBufferPtr +xmlOutputBufferCreateFile(FILE *file, xmlCharEncodingHandlerPtr encoder) { + xmlOutputBufferPtr ret; + + if (xmlOutputCallbackInitialized == 0) + xmlRegisterDefaultOutputCallbacks(); + + if (file == NULL) return(NULL); + + ret = xmlAllocOutputBufferInternal(encoder); + if (ret != NULL) { + ret->context = file; + ret->writecallback = xmlFileWrite; + ret->closecallback = xmlFileFlush; + } + + return(ret); +} + +/** + * xmlOutputBufferCreateBuffer: + * @buffer: a xmlBufferPtr + * @encoder: the encoding converter or NULL + * + * Create a buffered output for the progressive saving to a xmlBuffer + * + * Returns the new parser output or NULL + */ +xmlOutputBufferPtr +xmlOutputBufferCreateBuffer(xmlBufferPtr buffer, + xmlCharEncodingHandlerPtr encoder) { + xmlOutputBufferPtr ret; + + if (buffer == NULL) return(NULL); + + ret = xmlOutputBufferCreateIO((xmlOutputWriteCallback) + xmlBufferWrite, + (xmlOutputCloseCallback) + NULL, (void *) buffer, encoder); + + return(ret); +} + +/** + * xmlOutputBufferGetContent: + * @out: an xmlOutputBufferPtr + * + * Gives a pointer to the data currently held in the output buffer + * + * Returns a pointer to the data or NULL in case of error + */ +const xmlChar * +xmlOutputBufferGetContent(xmlOutputBufferPtr out) { + if ((out == NULL) || (out->buffer == NULL)) + return(NULL); + + return(xmlBufContent(out->buffer)); +} + +/** + * xmlOutputBufferGetSize: + * @out: an xmlOutputBufferPtr + * + * Gives the length of the data currently held in the output buffer + * + * Returns 0 in case or error or no data is held, the size otherwise + */ +size_t +xmlOutputBufferGetSize(xmlOutputBufferPtr out) { + if ((out == NULL) || (out->buffer == NULL)) + return(0); + + return(xmlBufUse(out->buffer)); +} + + +#endif /* LIBXML_OUTPUT_ENABLED */ + +/** + * xmlParserInputBufferCreateFd: + * @fd: a file descriptor number + * @enc: the charset encoding if known + * + * Create a buffered parser input for the progressive parsing for the input + * from a file descriptor + * + * Returns the new parser input or NULL + */ +xmlParserInputBufferPtr +xmlParserInputBufferCreateFd(int fd, xmlCharEncoding enc) { + xmlParserInputBufferPtr ret; + + if (fd < 0) return(NULL); + + ret = xmlAllocParserInputBuffer(enc); + if (ret != NULL) { + ret->context = (void *) (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 = xmlBufAdd(ret->buffer, (const xmlChar *) mem, size); + if (errcode != 0) { + xmlFree(ret); + return(NULL); + } + } + + return(ret); +} + +/** + * xmlParserInputBufferCreateStatic: + * @mem: the memory input + * @size: the length of the memory block + * @enc: the charset encoding if known + * + * Create a buffered parser input for the progressive parsing for the input + * from an immutable memory area. This will not copy the memory area to + * the buffer, but the memory is expected to be available until the end of + * the parsing, this is useful for example when using mmap'ed file. + * + * Returns the new parser input or NULL + */ +xmlParserInputBufferPtr +xmlParserInputBufferCreateStatic(const char *mem, int size, + xmlCharEncoding enc) { + xmlParserInputBufferPtr ret; + + if (size <= 0) return(NULL); + if (mem == NULL) return(NULL); + + ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer)); + if (ret == NULL) { + xmlIOErrMemory("creating input buffer"); + return(NULL); + } + memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer)); + ret->buffer = xmlBufCreateStatic((void *)mem, (size_t) size); + if (ret->buffer == NULL) { + xmlFree(ret); + return(NULL); + } + ret->encoder = xmlGetCharEncodingHandler(enc); + if (ret->encoder != NULL) + ret->raw = xmlBufCreateSize(2 * xmlDefaultBufferSize); + else + ret->raw = NULL; + ret->compressed = -1; + ret->context = (void *) mem; + ret->readcallback = NULL; + ret->closecallback = NULL; + + return(ret); +} + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlOutputBufferCreateFd: + * @fd: a file descriptor number + * @encoder: the encoding converter or NULL + * + * Create a buffered output for the progressive saving + * to a file descriptor + * + * Returns the new parser output or NULL + */ +xmlOutputBufferPtr +xmlOutputBufferCreateFd(int fd, xmlCharEncodingHandlerPtr encoder) { + xmlOutputBufferPtr ret; + + if (fd < 0) return(NULL); + + ret = xmlAllocOutputBufferInternal(encoder); + if (ret != NULL) { + ret->context = (void *) (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 = xmlAllocOutputBufferInternal(encoder); + if (ret != NULL) { + ret->context = (void *) ioctx; + ret->writecallback = iowrite; + ret->closecallback = ioclose; + } + + return(ret); +} +#endif /* LIBXML_OUTPUT_ENABLED */ + +/** + * xmlParserInputBufferCreateFilenameDefault: + * @func: function pointer to the new ParserInputBufferCreateFilenameFunc + * + * Registers a callback for URI input file handling + * + * Returns the old value of the registration function + */ +xmlParserInputBufferCreateFilenameFunc +xmlParserInputBufferCreateFilenameDefault(xmlParserInputBufferCreateFilenameFunc func) +{ + xmlParserInputBufferCreateFilenameFunc old = xmlParserInputBufferCreateFilenameValue; + if (old == NULL) { + old = __xmlParserInputBufferCreateFilename; + } + + xmlParserInputBufferCreateFilenameValue = func; + return(old); +} + +/** + * xmlOutputBufferCreateFilenameDefault: + * @func: function pointer to the new OutputBufferCreateFilenameFunc + * + * Registers a callback for URI output file handling + * + * Returns the old value of the registration function + */ +xmlOutputBufferCreateFilenameFunc +xmlOutputBufferCreateFilenameDefault(xmlOutputBufferCreateFilenameFunc func) +{ + xmlOutputBufferCreateFilenameFunc old = xmlOutputBufferCreateFilenameValue; +#ifdef LIBXML_OUTPUT_ENABLED + if (old == NULL) { + old = __xmlOutputBufferCreateFilename; + } +#endif + xmlOutputBufferCreateFilenameValue = func; + return(old); +} + +/** + * xmlParserInputBufferPush: + * @in: a buffered parser input + * @len: the size in bytes of the array. + * @buf: an char array + * + * Push the content of the arry in the input buffer + * This routine handle the I18N transcoding to internal UTF-8 + * This is used when operating the parser in progressive (push) mode. + * + * Returns the number of chars read and stored in the buffer, or -1 + * in case of error. + */ +int +xmlParserInputBufferPush(xmlParserInputBufferPtr in, + int len, const char *buf) { + int nbchars = 0; + int ret; + + if (len < 0) return(0); + if ((in == NULL) || (in->error)) return(-1); + if (in->encoder != NULL) { + unsigned int use; + + /* + * Store the data in the incoming raw buffer + */ + if (in->raw == NULL) { + in->raw = xmlBufCreate(); + } + ret = xmlBufAdd(in->raw, (const xmlChar *) buf, len); + if (ret != 0) + return(-1); + + /* + * convert as much as possible to the parser reading buffer. + */ + use = xmlBufUse(in->raw); + nbchars = xmlCharEncInput(in, 1); + if (nbchars < 0) { + xmlIOErr(XML_IO_ENCODER, NULL); + in->error = XML_IO_ENCODER; + return(-1); + } + in->rawconsumed += (use - xmlBufUse(in->raw)); + } else { + nbchars = len; + ret = xmlBufAdd(in->buffer, (xmlChar *) buf, nbchars); + if (ret != 0) + return(-1); + } +#ifdef DEBUG_INPUT + xmlGenericError(xmlGenericErrorContext, + "I/O: pushed %d chars, buffer %d/%d\n", + nbchars, xmlBufUse(in->buffer), xmlBufLength(in->buffer)); +#endif + return(nbchars); +} + +/** + * endOfInput: + * + * When reading from an Input channel indicated end of file or error + * don't reread from it again. + */ +static int +endOfInput (void * context ATTRIBUTE_UNUSED, + char * buffer ATTRIBUTE_UNUSED, + int len ATTRIBUTE_UNUSED) { + return(0); +} + +/** + * xmlParserInputBufferGrow: + * @in: a buffered parser input + * @len: indicative value of the amount of chars to read + * + * Grow up the content of the input buffer, the old data are preserved + * This routine handle the I18N transcoding to internal UTF-8 + * This routine is used when operating the parser in normal (pull) mode + * + * TODO: one should be able to remove one extra copy by copying directly + * onto in->buffer or in->raw + * + * Returns the number of chars read and stored in the buffer, or -1 + * in case of error. + */ +int +xmlParserInputBufferGrow(xmlParserInputBufferPtr in, int len) { + char *buffer = NULL; + int res = 0; + int nbchars = 0; + + if ((in == NULL) || (in->error)) return(-1); + if ((len <= MINLEN) && (len != 4)) + len = MINLEN; + + if (xmlBufAvail(in->buffer) <= 0) { + xmlIOErr(XML_IO_BUFFER_FULL, NULL); + in->error = XML_IO_BUFFER_FULL; + return(-1); + } + + if (xmlBufGrow(in->buffer, len + 1) < 0) { + xmlIOErrMemory("growing input buffer"); + in->error = XML_ERR_NO_MEMORY; + return(-1); + } + buffer = (char *)xmlBufEnd(in->buffer); + + /* + * Call the read method for this I/O type. + */ + if (in->readcallback != NULL) { + res = in->readcallback(in->context, &buffer[0], len); + if (res <= 0) + in->readcallback = endOfInput; + } else { + xmlIOErr(XML_IO_NO_INPUT, NULL); + in->error = XML_IO_NO_INPUT; + return(-1); + } + if (res < 0) { + return(-1); + } + + /* + * try to establish compressed status of input if not done already + */ + if (in->compressed == -1) { +#ifdef LIBXML_LZMA_ENABLED + if (in->readcallback == xmlXzfileRead) + in->compressed = __libxml2_xzcompressed(in->context); +#endif + } + + len = res; + if (in->encoder != NULL) { + unsigned int use; + + /* + * Store the data in the incoming raw buffer + */ + if (in->raw == NULL) { + in->raw = xmlBufCreate(); + } + res = xmlBufAdd(in->raw, (const xmlChar *) buffer, len); + if (res != 0) + return(-1); + + /* + * convert as much as possible to the parser reading buffer. + */ + use = xmlBufUse(in->raw); + nbchars = xmlCharEncInput(in, 1); + if (nbchars < 0) { + xmlIOErr(XML_IO_ENCODER, NULL); + in->error = XML_IO_ENCODER; + return(-1); + } + in->rawconsumed += (use - xmlBufUse(in->raw)); + } else { + nbchars = len; + xmlBufAddLen(in->buffer, nbchars); + } +#ifdef DEBUG_INPUT + xmlGenericError(xmlGenericErrorContext, + "I/O: read %d chars, buffer %d\n", + nbchars, xmlBufUse(in->buffer)); +#endif + return(nbchars); +} + +/** + * xmlParserInputBufferRead: + * @in: a buffered parser input + * @len: indicative value of the amount of chars to read + * + * Refresh the content of the input buffer, the old data are considered + * consumed + * This routine handle the I18N transcoding to internal UTF-8 + * + * Returns the number of chars read and stored in the buffer, or -1 + * in case of error. + */ +int +xmlParserInputBufferRead(xmlParserInputBufferPtr in, int len) { + if ((in == NULL) || (in->error)) return(-1); + if (in->readcallback != NULL) + return(xmlParserInputBufferGrow(in, len)); + else if (xmlBufGetAllocationScheme(in->buffer) == XML_BUFFER_ALLOC_IMMUTABLE) + return(0); + else + return(-1); +} + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlOutputBufferWrite: + * @out: a buffered parser output + * @len: the size in bytes of the array. + * @buf: an char array + * + * Write the content of the array in the output I/O buffer + * This routine handle the I18N transcoding from internal UTF-8 + * The buffer is lossless, i.e. will store in case of partial + * or delayed writes. + * + * Returns the number of chars immediately written, or -1 + * in case of error. + */ +int +xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) { + int nbchars = 0; /* number of chars to output to I/O */ + int ret; /* return from function call */ + int written = 0; /* number of char written to I/O so far */ + int chunk; /* number of byte 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 = xmlBufCreate(); + } + ret = xmlBufAdd(out->buffer, (const xmlChar *) buf, chunk); + if (ret != 0) + return(-1); + + if ((xmlBufUse(out->buffer) < MINLEN) && (chunk == len)) + goto done; + + /* + * convert as much as possible to the parser reading buffer. + */ + ret = xmlCharEncOutput(out, 0); + if ((ret < 0) && (ret != -3)) { + xmlIOErr(XML_IO_ENCODER, NULL); + out->error = XML_IO_ENCODER; + return(-1); + } + nbchars = xmlBufUse(out->conv); + } else { + ret = xmlBufAdd(out->buffer, (const xmlChar *) buf, chunk); + if (ret != 0) + return(-1); + nbchars = xmlBufUse(out->buffer); + } + 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 *)xmlBufContent(out->conv), nbchars); + if (ret >= 0) + xmlBufShrink(out->conv, ret); + } else { + ret = out->writecallback(out->context, + (const char *)xmlBufContent(out->buffer), nbchars); + if (ret >= 0) + xmlBufShrink(out->buffer, ret); + } + if (ret < 0) { + xmlIOErr(XML_IO_WRITE, NULL); + out->error = XML_IO_WRITE; + return(ret); + } + 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) || + (xmlBufGetAllocationScheme(out->buffer) == XML_BUFFER_ALLOC_IMMUTABLE)) + return(-1); + len = strlen((const char *)str); + if (len < 0) return(0); + if (out->error) return(-1); + if (escaping == NULL) escaping = xmlEscapeContent; + + do { + oldwritten = written; + + /* + * how many bytes to consume and how many bytes to store. + */ + cons = len; + chunk = xmlBufAvail(out->buffer) - 1; + + /* + * make sure we have enough room to save first, if this is + * not the case force a flush, but make sure we stay in the loop + */ + if (chunk < 40) { + if (xmlBufGrow(out->buffer, 100) < 0) + return(-1); + oldwritten = -1; + continue; + } + + /* + * first handle encoding stuff. + */ + if (out->encoder != NULL) { + /* + * Store the data in the incoming raw buffer + */ + if (out->conv == NULL) { + out->conv = xmlBufCreate(); + } + ret = escaping(xmlBufEnd(out->buffer) , + &chunk, str, &cons); + if ((ret < 0) || (chunk == 0)) /* chunk==0 => nothing done */ + return(-1); + xmlBufAddLen(out->buffer, chunk); + + if ((xmlBufUse(out->buffer) < MINLEN) && (cons == len)) + goto done; + + /* + * convert as much as possible to the output buffer. + */ + ret = xmlCharEncOutput(out, 0); + if ((ret < 0) && (ret != -3)) { + xmlIOErr(XML_IO_ENCODER, NULL); + out->error = XML_IO_ENCODER; + return(-1); + } + nbchars = xmlBufUse(out->conv); + } else { + ret = escaping(xmlBufEnd(out->buffer), &chunk, str, &cons); + if ((ret < 0) || (chunk == 0)) /* chunk==0 => nothing done */ + return(-1); + xmlBufAddLen(out->buffer, chunk); + nbchars = xmlBufUse(out->buffer); + } + 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 *)xmlBufContent(out->conv), nbchars); + if (ret >= 0) + xmlBufShrink(out->conv, ret); + } else { + ret = out->writecallback(out->context, + (const char *)xmlBufContent(out->buffer), nbchars); + if (ret >= 0) + xmlBufShrink(out->buffer, ret); + } + if (ret < 0) { + xmlIOErr(XML_IO_WRITE, NULL); + out->error = XML_IO_WRITE; + return(ret); + } + out->written += ret; + } else if (xmlBufAvail(out->buffer) < MINLEN) { + xmlBufGrow(out->buffer, MINLEN); + } + written += nbchars; + } while ((len > 0) && (oldwritten != written)); + +done: +#ifdef DEBUG_INPUT + xmlGenericError(xmlGenericErrorContext, + "I/O: wrote %d chars\n", written); +#endif + return(written); +} + +/** + * xmlOutputBufferWriteString: + * @out: a buffered parser output + * @str: a zero terminated C string + * + * Write the content of the string in the output I/O buffer + * This routine handle the I18N transcoding from internal UTF-8 + * The buffer is lossless, i.e. will store in case of partial + * or delayed writes. + * + * Returns the number of chars immediately written, or -1 + * in case of error. + */ +int +xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str) { + int len; + + if ((out == NULL) || (out->error)) return(-1); + if (str == NULL) + return(-1); + len = strlen(str); + + if (len > 0) + return(xmlOutputBufferWrite(out, len, str)); + return(len); +} + +/** + * xmlOutputBufferFlush: + * @out: a buffered output + * + * flushes the output I/O channel + * + * Returns the number of byte written or -1 in case of error. + */ +int +xmlOutputBufferFlush(xmlOutputBufferPtr out) { + int nbchars = 0, ret = 0; + + if ((out == NULL) || (out->error)) return(-1); + /* + * first handle encoding stuff. + */ + if ((out->conv != NULL) && (out->encoder != NULL)) { + /* + * convert as much as possible to the parser output buffer. + */ + do { + nbchars = xmlCharEncOutput(out, 0); + if (nbchars < 0) { + xmlIOErr(XML_IO_ENCODER, NULL); + out->error = XML_IO_ENCODER; + return(-1); + } + } while (nbchars); + } + + /* + * second flush the stuff to the I/O channel + */ + if ((out->conv != NULL) && (out->encoder != NULL) && + (out->writecallback != NULL)) { + ret = out->writecallback(out->context, + (const char *)xmlBufContent(out->conv), + xmlBufUse(out->conv)); + if (ret >= 0) + xmlBufShrink(out->conv, ret); + } else if (out->writecallback != NULL) { + ret = out->writecallback(out->context, + (const char *)xmlBufContent(out->buffer), + xmlBufUse(out->buffer)); + if (ret >= 0) + xmlBufShrink(out->buffer, ret); + } + if (ret < 0) { + xmlIOErr(XML_IO_FLUSH, NULL); + out->error = XML_IO_FLUSH; + return(ret); + } + 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; + +#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__) +# define IS_XMLPGD_SEP(ch) ((ch=='/')||(ch=='\\')) +#else +# define IS_XMLPGD_SEP(ch) (ch=='/') +#endif + + strncpy(dir, filename, 1023); + dir[1023] = 0; + cur = &dir[strlen(dir)]; + while (cur > dir) { + if (IS_XMLPGD_SEP(*cur)) break; + cur --; + } + if (IS_XMLPGD_SEP(*cur)) { + if (cur == dir) dir[1] = 0; + else *cur = 0; + ret = xmlMemStrdup(dir); + } else { + if (getcwd(dir, 1024) != NULL) { + dir[1023] = 0; + ret = xmlMemStrdup(dir); + } + } + return(ret); +#undef IS_XMLPGD_SEP +} + +/**************************************************************** + * * + * External entities loading * + * * + ****************************************************************/ + +/** + * xmlCheckHTTPInput: + * @ctxt: an XML parser context + * @ret: an XML parser input + * + * Check an input in case it was created from an HTTP stream, in that + * case it will handle encoding and update of the base URL in case of + * redirection. It also checks for HTTP errors in which case the input + * is cleanly freed up and an appropriate error is raised in context + * + * Returns the input or NULL in case of HTTP error. + */ +xmlParserInputPtr +xmlCheckHTTPInput(xmlParserCtxtPtr ctxt, xmlParserInputPtr ret) { +#ifdef LIBXML_HTTP_ENABLED + if ((ret != NULL) && (ret->buf != NULL) && + (ret->buf->readcallback == xmlIOHTTPRead) && + (ret->buf->context != NULL)) { + const char *encoding; + const char *redir; + const char *mime; + int code; + + code = xmlNanoHTTPReturnCode(ret->buf->context); + if (code >= 400) { + /* fatal error */ + if (ret->filename != NULL) + __xmlLoaderErr(ctxt, "failed to load HTTP resource \"%s\"\n", + (const char *) ret->filename); + else + __xmlLoaderErr(ctxt, "failed to load HTTP resource\n", NULL); + xmlFreeInputStream(ret); + ret = NULL; + } else { + + mime = xmlNanoHTTPMimeType(ret->buf->context); + if ((xmlStrstr(BAD_CAST mime, BAD_CAST "/xml")) || + (xmlStrstr(BAD_CAST mime, BAD_CAST "+xml"))) { + encoding = xmlNanoHTTPEncoding(ret->buf->context); + if (encoding != NULL) { + xmlCharEncodingHandlerPtr handler; + + handler = xmlFindCharEncodingHandler(encoding); + if (handler != NULL) { + xmlSwitchInputEncoding(ctxt, ret, handler); + } else { + __xmlErrEncoding(ctxt, XML_ERR_UNKNOWN_ENCODING, + "Unknown encoding %s", + BAD_CAST encoding, NULL); + } + if (ret->encoding == NULL) + ret->encoding = xmlStrdup(BAD_CAST encoding); + } +#if 0 + } else if (xmlStrstr(BAD_CAST mime, BAD_CAST "html")) { +#endif + } + redir = xmlNanoHTTPRedir(ret->buf->context); + if (redir != NULL) { + if (ret->filename != NULL) + xmlFree((xmlChar *) ret->filename); + if (ret->directory != NULL) { + xmlFree((xmlChar *) ret->directory); + ret->directory = NULL; + } + ret->filename = + (char *) xmlStrdup((const xmlChar *) redir); + } + } + } +#endif + return(ret); +} + +static int xmlNoNetExists(const char *URL) { + const char *path; + + if (URL == NULL) + return(0); + + if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17)) +#if defined (_WIN32) || 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. + */ +static xmlChar * +xmlResolveResourceFromCatalog(const char *URL, const char *ID, + xmlParserCtxtPtr ctxt) { + xmlChar *resource = NULL; + xmlCatalogAllow pref; + + /* + * If the resource doesn't exists as a file, + * try to load it from the resource pointed in the catalogs + */ + pref = xmlCatalogGetDefaults(); + + if ((pref != XML_CATA_ALLOW_NONE) && (!xmlNoNetExists(URL))) { + /* + * Do a local lookup + */ + if ((ctxt != NULL) && (ctxt->catalogs != NULL) && + ((pref == XML_CATA_ALLOW_ALL) || + (pref == XML_CATA_ALLOW_DOCUMENT))) { + resource = xmlCatalogLocalResolve(ctxt->catalogs, + (const xmlChar *)ID, + (const xmlChar *)URL); + } + /* + * Try a global lookup + */ + if ((resource == NULL) && + ((pref == XML_CATA_ALLOW_ALL) || + (pref == XML_CATA_ALLOW_GLOBAL))) { + resource = xmlCatalogResolve((const xmlChar *)ID, + (const xmlChar *)URL); + } + if ((resource == NULL) && (URL != NULL)) + resource = xmlStrdup((const xmlChar *) URL); + + /* + * TODO: do an URI lookup on the reference + */ + if ((resource != NULL) && (!xmlNoNetExists((const char *)resource))) { + xmlChar *tmp = NULL; + + if ((ctxt != NULL) && (ctxt->catalogs != NULL) && + ((pref == XML_CATA_ALLOW_ALL) || + (pref == XML_CATA_ALLOW_DOCUMENT))) { + tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource); + } + if ((tmp == NULL) && + ((pref == XML_CATA_ALLOW_ALL) || + (pref == XML_CATA_ALLOW_GLOBAL))) { + tmp = xmlCatalogResolveURI(resource); + } + + if (tmp != NULL) { + xmlFree(resource); + resource = tmp; + } + } + } + + return resource; +} + +#endif + +/** + * xmlDefaultExternalEntityLoader: + * @URL: the URL for the entity to load + * @ID: the System ID for the entity to load + * @ctxt: the context in which the entity is called or NULL + * + * By default we don't load external 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" |