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