/* * xmlIO.c : implementation of the I/O interfaces used by the parser * * See Copyright for the status of this software. * * daniel@veillard.com */ #define IN_LIBXML #include "libxml.h" #include #include #include #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef LIBXML_ZLIB_ENABLED #include #endif #ifdef LIBXML_LZMA_ENABLED #include #endif #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #include #include #include #endif #include #include #include #include #include #include #ifdef LIBXML_CATALOG_ENABLED #include #endif #include "private/buf.h" #include "private/enc.h" #include "private/error.h" #include "private/io.h" #include "private/parser.h" /* #define VERBOSE_FAILURE */ #define MINLEN 4000 #ifndef STDOUT_FILENO #define STDOUT_FILENO 1 #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 /* * Input I/O callback sets */ typedef struct _xmlInputCallback { xmlInputMatchCallback matchcallback; xmlInputOpenCallback opencallback; xmlInputReadCallback readcallback; xmlInputCloseCallback closecallback; } xmlInputCallback; /* This dummy function only marks default IO in the callback table */ static int xmlIODefaultMatch(const char *filename); #define MAX_INPUT_CALLBACK 10 static xmlInputCallback xmlInputCallbackTable[MAX_INPUT_CALLBACK]; static int xmlInputCallbackNr; #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 10 static xmlOutputCallback xmlOutputCallbackTable[MAX_OUTPUT_CALLBACK]; static int xmlOutputCallbackNr; #endif /* LIBXML_OUTPUT_ENABLED */ /************************************************************************ * * * Error handling * * * ************************************************************************/ /** * xmlIOErrMemory: * @extra: extra information * * Handle an out of memory condition */ static void xmlIOErrMemory(void) { xmlRaiseMemoryError(NULL, NULL, NULL, XML_FROM_IO, NULL); } /** * __xmlIOErr: * @code: the error number * @ * @extra: extra information * * Handle an I/O error */ int __xmlIOErr(int domain, int code, const char *extra) { xmlStructuredErrorFunc schannel = NULL; xmlGenericErrorFunc channel = NULL; void *data = NULL; const char *fmt, *arg; int res; if (code == 0) { if (errno == 0) code = XML_IO_UNKNOWN; #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; } if (xmlStructuredError) { schannel = xmlStructuredError; data = xmlStructuredErrorContext; } else { channel = xmlGenericError; data = xmlGenericErrorContext; } if (code == XML_IO_NETWORK_ATTEMPT) { fmt = "Attempt to load network entity %s"; arg = extra; } else { fmt = "%s"; arg = xmlErrString(code); } res = __xmlRaiseError(schannel, channel, data, NULL, NULL, domain, code, XML_ERR_ERROR, NULL, 0, extra, NULL, NULL, 0, 0, fmt, arg); if (res < 0) { xmlIOErrMemory(); return(XML_ERR_NO_MEMORY); } return(code); } /** * xmlIOErr: * @code: the error number * @extra: extra information * * Handle an I/O error */ static int xmlIOErr(int code, const char *extra) { return(__xmlIOErr(XML_FROM_IO, code, extra)); } /************************************************************************ * * * Standard I/O for file accesses * * * ************************************************************************/ #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; int i; 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; } } /* * Convert to backward slash */ for (i = 0; wString[i] != 0; i++) { if (wString[i] == '/') wString[i] = '\\'; } } } return wString; } #endif /** * xmlNormalizeWindowsPath: * @path: the input file path * * DEPRECATED: This never really worked. * * Returns a copy of path. */ xmlChar * xmlNormalizeWindowsPath(const xmlChar *path) { return xmlStrdup(path); } /** * xmlCheckFilename: * @path: the path to check * * DEPRECATED: Internal function, don't use. * * 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 int res; #endif if (path == NULL) return(0); #ifdef HAVE_STAT #if defined(_WIN32) { wchar_t *wpath; /* * 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; wpath = __xmlIOWin32UTF8ToWChar(path); if (wpath == NULL) return(0); res = _wstat(wpath, &stat_buffer); xmlFree(wpath); } #else res = stat(path, &stat_buffer); #endif if (res < 0) return 0; #ifdef S_ISDIR if (S_ISDIR(stat_buffer.st_mode)) return 2; #endif #endif /* HAVE_STAT */ return 1; } static int xmlConvertUriToPath(const char *uri, char **out) { const char *escaped; char *unescaped; *out = NULL; if (!xmlStrncasecmp(BAD_CAST uri, BAD_CAST "file://localhost/", 17)) { escaped = &uri[16]; } else if (!xmlStrncasecmp(BAD_CAST uri, BAD_CAST "file:///", 8)) { escaped = &uri[7]; } else if (!xmlStrncasecmp(BAD_CAST uri, BAD_CAST "file:/", 6)) { /* lots of generators seems to lazy to read RFC 1738 */ escaped = &uri[5]; } else { return(1); } #ifdef _WIN32 /* Ignore slash like in file:///C:/file.txt */ escaped += 1; #endif unescaped = xmlURIUnescapeString(escaped, 0, NULL); if (unescaped == NULL) return(-1); *out = unescaped; return(0); } /** * xmlFdOpen: * @filename: the URI for matching * @out: pointer to resulting context * * Returns an xmlParserErrors code */ static int xmlFdOpen(const char *filename, int write, int *out) { char *fromUri = NULL; int flags; int fd; int ret; *out = -1; if (filename == NULL) return(XML_ERR_ARGUMENT); if (xmlConvertUriToPath(filename, &fromUri) < 0) return(XML_ERR_NO_MEMORY); if (fromUri != NULL) filename = fromUri; #if defined(_WIN32) { wchar_t *wpath; wpath = __xmlIOWin32UTF8ToWChar(filename); if (wpath == NULL) { xmlFree(fromUri); return(XML_ERR_NO_MEMORY); } if (write) flags = _O_WRONLY | _O_CREAT | _O_TRUNC; else flags = _O_RDONLY; fd = _wopen(wpath, flags | _O_BINARY, 0777); xmlFree(wpath); } #else if (write) flags = O_WRONLY | O_CREAT | O_TRUNC; else flags = O_RDONLY; fd = open(filename, flags, 0777); #endif /* WIN32 */ if (fd < 0) { /* * Windows and possibly other platforms return EINVAL * for invalid filenames. */ if ((errno == ENOENT) || (errno == EINVAL)) { ret = XML_IO_ENOENT; } else { /* * This error won't be forwarded to the parser context * which will report it a second time. */ ret = xmlIOErr(0, filename); } } else { *out = fd; ret = XML_ERR_OK; } xmlFree(fromUri); return(ret); } /** * 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 read */ static int xmlFdRead(void *context, char *buffer, int len) { int fd = (int) (ptrdiff_t) context; int ret = 0; int bytes; while (len > 0) { bytes = read(fd, buffer, len); if (bytes < 0) { /* * If we already got some bytes, return them without * raising an error. */ if (ret > 0) break; return(-xmlIOErr(0, "read()")); } if (bytes == 0) break; ret += bytes; buffer += bytes; len -= bytes; } 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 fd = (int) (ptrdiff_t) context; int ret = 0; int bytes; while (len > 0) { bytes = write(fd, buffer, len); if (bytes < 0) return(-xmlIOErr(0, "write()")); ret += bytes; buffer += bytes; len -= bytes; } 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) return(xmlIOErr(0, "close()")); return(XML_ERR_OK); } /** * xmlFileMatch: * @filename: the URI for matching * * DEPRECATED: Internal function, don't use. * * Returns 1 if matches, 0 otherwise */ int xmlFileMatch (const char *filename ATTRIBUTE_UNUSED) { return(1); } /** * xmlFileOpenSafe: * @filename: the URI for matching * @out: pointer to resulting context * * input from FILE * * * Returns an I/O context or NULL in case of error */ static int xmlFileOpenSafe(const char *filename, int write, void **out) { char *fromUri = NULL; FILE *fd; int ret = XML_ERR_OK; *out = NULL; if (filename == NULL) return(XML_ERR_ARGUMENT); if (xmlConvertUriToPath(filename, &fromUri) < 0) return(XML_ERR_NO_MEMORY); if (fromUri != NULL) filename = fromUri; #if defined(_WIN32) { wchar_t *wpath; wpath = __xmlIOWin32UTF8ToWChar(filename); if (wpath == NULL) { xmlFree(fromUri); return(XML_ERR_NO_MEMORY); } fd = _wfopen(wpath, write ? L"wb" : L"rb"); xmlFree(wpath); } #else fd = fopen(filename, write ? "wb" : "rb"); #endif /* WIN32 */ if (fd == NULL) { /* * Windows and possibly other platforms return EINVAL * for invalid filenames. */ if ((errno == ENOENT) || (errno == EINVAL)) { ret = XML_IO_ENOENT; } else { /* * This error won't be forwarded to the parser context * which will report it a second time. */ ret = xmlIOErr(0, filename); } } *out = fd; xmlFree(fromUri); return(ret); } /** * xmlFileOpen: * @filename: the URI for matching * * DEPRECATED: Internal function, don't use. * * Returns an IO context or NULL in case or failure */ void * xmlFileOpen(const char *filename) { void *context; xmlFileOpenSafe(filename, 0, &context); return(context); } /** * xmlFileRead: * @context: the I/O context * @buffer: where to drop data * @len: number of bytes to write * * DEPRECATED: Internal function, don't use. * * Returns the number of bytes read or < 0 in case of failure */ int xmlFileRead(void * context, char * buffer, int len) { FILE *file = context; size_t bytes; if ((context == NULL) || (buffer == NULL)) return(-1); /* * The C standard doesn't mandate that fread sets errno, only * POSIX does. The Windows documentation isn't really clear. * Set errno to zero which will be reported as unknown error * if fread fails without setting errno. */ errno = 0; bytes = fread(buffer, 1, len, file); if ((bytes < (size_t) len) && (ferror(file))) return(-xmlIOErr(0, "fread()")); return(len); } #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) { FILE *file = context; size_t bytes; if ((context == NULL) || (buffer == NULL)) return(-1); errno = 0; bytes = fwrite(buffer, 1, len, file); if (bytes < (size_t) len) return(-xmlIOErr(0, "fwrite()")); return(len); } #endif /* LIBXML_OUTPUT_ENABLED */ /** * xmlFileFlush: * @context: the I/O context * * Flush an I/O channel */ static int xmlFileFlush (void * context) { FILE *file = context; if (file == NULL) return(-1); if (fflush(file) != 0) return(xmlIOErr(0, "fflush()")); return(XML_ERR_OK); } /** * xmlFileClose: * @context: the I/O context * * DEPRECATED: Internal function, don't use. * * Returns 0 or -1 an error code case of error */ int xmlFileClose (void * context) { FILE *file = context; if (context == NULL) return(-1); if (file == stdin) return(0); if ((file == stdout) || (file == stderr)) return(xmlFileFlush(file)); if (fclose(file) != 0) return(xmlIOErr(0, "fclose()")); return(0); } #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 or a negative xmlParserErrors * value. */ static int xmlBufferWrite (void * context, const char * buffer, int len) { int ret; ret = xmlBufferAdd((xmlBufferPtr) context, (const xmlChar *) buffer, len); if (ret != 0) return(-XML_ERR_NO_MEMORY); return(len); } #endif #ifdef LIBXML_ZLIB_ENABLED /************************************************************************ * * * I/O for compressed file accesses * * * ************************************************************************/ /** * 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 */ /************************************************************************ * * * I/O for compressed file accesses * * * ************************************************************************/ #ifdef LIBXML_LZMA_ENABLED #include "private/xzlib.h" /** * 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 */ /************************************************************************ * * * I/O for HTTP file accesses * * * ************************************************************************/ #ifdef LIBXML_HTTP_ENABLED /** * xmlIOHTTPMatch: * @filename: the URI for matching * * DEPRECATED: Internal function, don't use. * * 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 * * DEPRECATED: Internal function, don't use. * * 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. * * DEPRECATED: Support for HTTP POST has been removed. * * Returns NULL. */ void * xmlIOHTTPOpenW(const char *post_uri ATTRIBUTE_UNUSED, int compression ATTRIBUTE_UNUSED) { return(NULL); } #endif /* LIBXML_OUTPUT_ENABLED */ /** * xmlIOHTTPRead: * @context: the I/O context * @buffer: where to drop data * @len: number of bytes to write * * DEPRECATED: Internal function, don't use. * * 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)); } /** * xmlIOHTTPClose: * @context: the I/O context * * DEPRECATED: Internal function, don't use. * * Close an HTTP I/O channel * * Returns 0 */ int xmlIOHTTPClose (void * context) { xmlNanoHTTPClose(context); return 0; } #endif /* LIBXML_HTTP_ENABLED */ #ifdef LIBXML_FTP_ENABLED /************************************************************************ * * * I/O for FTP file accesses * * * ************************************************************************/ /** * xmlIOFTPMatch: * @filename: the URI for matching * * DEPRECATED: Internal function, don't use. * * 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 * * DEPRECATED: Internal function, don't use. * * 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 * * DEPRECATED: Internal function, don't use. * * 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 * * DEPRECATED: Internal function, don't use. * * Close an FTP I/O channel * * Returns 0 */ int xmlIOFTPClose (void * context) { return ( xmlNanoFTPClose(context) ); } #endif /* LIBXML_FTP_ENABLED */ /************************************************************************ * * * Input/output buffers * * * ************************************************************************/ static int xmlIODefaultMatch(const char *filename ATTRIBUTE_UNUSED) { return(1); } /** * xmlInputDefaultOpen: * @buf: input buffer to be filled * @filename: filename or URI * * Returns an xmlParserErrors code. */ static int xmlInputDefaultOpen(xmlParserInputBufferPtr buf, const char *filename) { int ret; int fd; #ifdef LIBXML_FTP_ENABLED if (xmlIOFTPMatch(filename)) { buf->context = xmlIOFTPOpen(filename); if (buf->context != NULL) { buf->readcallback = xmlIOFTPRead; buf->closecallback = xmlIOFTPClose; return(XML_ERR_OK); } } #endif /* LIBXML_FTP_ENABLED */ #ifdef LIBXML_HTTP_ENABLED if (xmlIOHTTPMatch(filename)) { buf->context = xmlIOHTTPOpen(filename); if (buf->context != NULL) { buf->readcallback = xmlIOHTTPRead; buf->closecallback = xmlIOHTTPClose; return(XML_ERR_OK); } } #endif /* LIBXML_HTTP_ENABLED */ if (!xmlFileMatch(filename)) return(XML_IO_ENOENT); #ifdef LIBXML_LZMA_ENABLED { xzFile xzStream; ret = xmlFdOpen(filename, 0, &fd); if (ret != XML_ERR_OK) return(ret); xzStream = __libxml2_xzdopen(filename, fd, "rb"); if (xzStream == NULL) { close(fd); } else { if (__libxml2_xzcompressed(xzStream) > 0) { buf->context = xzStream; buf->readcallback = xmlXzfileRead; buf->closecallback = xmlXzfileClose; buf->compressed = 1; return(XML_ERR_OK); } xmlXzfileClose(xzStream); } } #endif /* LIBXML_LZMA_ENABLED */ #ifdef LIBXML_ZLIB_ENABLED { gzFile gzStream; ret = xmlFdOpen(filename, 0, &fd); if (ret != XML_ERR_OK) return(ret); gzStream = gzdopen(fd, "rb"); if (gzStream == NULL) { close(fd); } else { char buff4[4]; if ((gzread(gzStream, buff4, 4) > 0) && (gzdirect(gzStream) == 0)) { gzrewind(gzStream); buf->context = gzStream; buf->readcallback = xmlGzfileRead; buf->closecallback = xmlGzfileClose; buf->compressed = 1; return(XML_ERR_OK); } xmlGzfileClose(gzStream); } } #endif /* LIBXML_ZLIB_ENABLED */ ret = xmlFdOpen(filename, 0, &fd); if (ret != XML_ERR_OK) return(ret); buf->context = (void *) (ptrdiff_t) fd; buf->readcallback = xmlFdRead; buf->closecallback = xmlFdClose; return(XML_ERR_OK); } #ifdef LIBXML_OUTPUT_ENABLED /** * xmlOutputDefaultOpen: * @buf: input buffer to be filled * @filename: filename or URI * @compression: compression level or 0 * @is_file_uri: whether filename is a file URI * * Returns an xmlParserErrors code. */ static int xmlOutputDefaultOpen(xmlOutputBufferPtr buf, const char *filename, int compression) { int fd; (void) compression; if (!strcmp(filename, "-")) { fd = dup(STDOUT_FILENO); if (fd < 0) return(xmlIOErr(0, "dup()")); } else { int ret; ret = xmlFdOpen(filename, /* write */ 1, &fd); if (ret != XML_ERR_OK) return(ret); } #ifdef LIBXML_ZLIB_ENABLED if ((compression > 0) && (compression <= 9)) { gzFile gzStream; char mode[15]; snprintf(mode, sizeof(mode), "wb%d", compression); gzStream = gzdopen(fd, mode); if (gzStream == NULL) { close(fd); return(xmlIOErr(XML_IO_UNKNOWN, "gzdopen()")); } buf->context = gzStream; buf->writecallback = xmlGzfileWrite; buf->closecallback = xmlGzfileClose; return(XML_ERR_OK); } #endif /* LIBXML_ZLIB_ENABLED */ buf->context = (void *) (ptrdiff_t) fd; buf->writecallback = xmlFdWrite; buf->closecallback = xmlFdClose; return(XML_ERR_OK); } #endif /** * xmlAllocParserInputBuffer: * @enc: the charset encoding if known (deprecated) * * Create a buffered parser input for progressive parsing. * * The encoding argument is deprecated and should be set to * XML_CHAR_ENCODING_NONE. The encoding can be changed with * xmlSwitchEncoding or xmlSwitchEncodingName later on. * * Returns the new parser input or NULL */ xmlParserInputBufferPtr xmlAllocParserInputBuffer(xmlCharEncoding enc) { xmlParserInputBufferPtr ret; ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer)); if (ret == NULL) { 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); if (enc != XML_CHAR_ENCODING_NONE) { if (xmlLookupCharEncodingHandler(enc, &ret->encoder) != 0) { /* We can't handle errors properly here. */ xmlFreeParserInputBuffer(ret); return(NULL); } } 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) { 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) { 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 ret; if (out == NULL) return (-1); if (out->writecallback != NULL) xmlOutputBufferFlush(out); if (out->closecallback != NULL) { int code = out->closecallback(out->context); if ((code != XML_ERR_OK) && (out->error == XML_ERR_OK)) { if (code < 0) out->error = XML_IO_UNKNOWN; else out->error = code; } } /* * TODO: Report the error code */ if (out->error != XML_ERR_OK) ret = -1; else ret = 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; } xmlFree(out); return(ret); } #endif /* LIBXML_OUTPUT_ENABLED */ /** * xmlParserInputBufferCreateFilenameInt: * @URI: the filename or URI * @enc: encoding enum (deprecated) * @out: pointer to resulting input buffer * * Returns an xmlParserErrors code. */ static int xmlParserInputBufferCreateFilenameInt(const char *URI, xmlCharEncoding enc, xmlParserInputBufferPtr *out) { xmlParserInputBufferPtr buf; int ret; int i; *out = NULL; if (URI == NULL) return(XML_ERR_ARGUMENT); /* * Allocate the Input buffer front-end. */ buf = xmlAllocParserInputBuffer(enc); if (buf == NULL) return(XML_ERR_NO_MEMORY); /* * Try to find one of the input accept method accepting that scheme * Go in reverse to give precedence to user defined handlers. */ ret = XML_IO_ENOENT; for (i = xmlInputCallbackNr - 1; i >= 0; i--) { xmlInputCallback *cb = &xmlInputCallbackTable[i]; if (cb->matchcallback == xmlIODefaultMatch) { ret = xmlInputDefaultOpen(buf, URI); if ((ret == XML_ERR_OK) || (ret != XML_IO_ENOENT)) break; } else if ((cb->matchcallback != NULL) && (cb->matchcallback(URI) != 0)) { buf->context = cb->opencallback(URI); if (buf->context != NULL) { buf->readcallback = cb->readcallback; buf->closecallback = cb->closecallback; ret = XML_ERR_OK; break; } } } if (ret != XML_ERR_OK) { xmlFreeParserInputBuffer(buf); *out = NULL; return(ret); } *out = buf; return(ret); } xmlParserInputBufferPtr __xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) { xmlParserInputBufferPtr ret; xmlParserInputBufferCreateFilenameInt(URI, enc, &ret); 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 * 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 != NULL) return(xmlParserInputBufferCreateFilenameValue(URI, enc)); return(__xmlParserInputBufferCreateFilename(URI, enc)); } /** * xmlParserInputBufferCreateFilenameSafe: * @URI: the filename or URI * @enc: encoding enum (deprecated) * @out: pointer to resulting input buffer * * Returns an xmlParserErrors code. */ int xmlParserInputBufferCreateFilenameSafe(const char *URI, xmlCharEncoding enc, xmlParserInputBufferPtr *out) { if (xmlParserInputBufferCreateFilenameValue != NULL) { *out = xmlParserInputBufferCreateFilenameValue(URI, enc); if (*out == NULL) return(XML_IO_ENOENT); return(XML_ERR_OK); } return(xmlParserInputBufferCreateFilenameInt(URI, enc, out)); } #ifdef LIBXML_OUTPUT_ENABLED xmlOutputBufferPtr __xmlOutputBufferCreateFilename(const char *URI, xmlCharEncodingHandlerPtr encoder, int compression) { xmlOutputBufferPtr ret; xmlURIPtr puri; int i = 0; char *unescaped = NULL; if (URI == NULL) return(NULL); puri = xmlParseURI(URI); if (puri != NULL) { /* * try to limit the damages of the URI unescaping code. */ if (puri->scheme == NULL) { unescaped = xmlURIUnescapeString(URI, 0, NULL); if (unescaped == NULL) { xmlFreeURI(puri); return(NULL); } URI = unescaped; } xmlFreeURI(puri); } /* * Allocate the Output buffer front-end. */ ret = xmlAllocOutputBufferInternal(encoder); if (ret == NULL) { xmlFree(unescaped); return(NULL); } /* * Try to find one of the output accept method accepting that scheme * Go in reverse to give precedence to user defined handlers. */ for (i = xmlOutputCallbackNr - 1; i >= 0; i--) { xmlOutputCallback *cb = &xmlOutputCallbackTable[i]; int code; if (cb->matchcallback == xmlIODefaultMatch) { code = xmlOutputDefaultOpen(ret, URI, compression); /* TODO: Handle other errors */ if (code == XML_ERR_OK) break; } else if ((cb->matchcallback != NULL) && (cb->matchcallback(URI) != 0)) { ret->context = cb->opencallback(URI); if (ret->context != NULL) { ret->writecallback = cb->writecallback; ret->closecallback = cb->closecallback; break; } } } if (ret->context == NULL) { xmlOutputBufferClose(ret); ret = NULL; } xmlFree(unescaped); 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 (deprecated) * * Create a buffered parser input for the progressive parsing of a FILE * * buffered C I/O * * The encoding argument is deprecated and should be set to * XML_CHAR_ENCODING_NONE. The encoding can be changed with * xmlSwitchEncoding or xmlSwitchEncodingName later on. * * Returns the new parser input or NULL */ xmlParserInputBufferPtr xmlParserInputBufferCreateFile(FILE *file, xmlCharEncoding enc) { xmlParserInputBufferPtr ret; if (file == NULL) return(NULL); ret = xmlAllocParserInputBuffer(enc); if (ret != NULL) { ret->context = file; ret->readcallback = xmlFileRead; ret->closecallback = NULL; } 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 (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) || (out->error != 0)) 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) || (out->error != 0)) return(0); return(xmlBufUse(out->buffer)); } #endif /* LIBXML_OUTPUT_ENABLED */ /** * xmlParserInputBufferCreateFd: * @fd: a file descriptor number * @enc: the charset encoding if known (deprecated) * * Create a buffered parser input for the progressive parsing for the input * from a file descriptor * * The encoding argument is deprecated and should be set to * XML_CHAR_ENCODING_NONE. The encoding can be changed with * xmlSwitchEncoding or xmlSwitchEncodingName later on. * * 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); } typedef struct { char *mem; const char *cur; size_t size; } xmlMemIOCtxt; static int xmlMemRead(void *vctxt, char *buf, int size) { xmlMemIOCtxt *ctxt = vctxt; if ((size_t) size > ctxt->size) size = ctxt->size; memcpy(buf, ctxt->cur, size); ctxt->cur += size; ctxt->size -= size; return size; } static int xmlMemClose(void *vctxt) { xmlMemIOCtxt *ctxt = vctxt; if (ctxt->mem != NULL) xmlFree(ctxt->mem); xmlFree(ctxt); return(0); } xmlParserInputBufferPtr xmlNewInputBufferMemory(const void *mem, size_t size, int flags, xmlCharEncoding enc) { xmlParserInputBufferPtr ret; xmlMemIOCtxt *ctxt; char *copy = NULL; if ((flags & XML_INPUT_BUF_STATIC) == 0) { if (size + 1 == 0) return(NULL); copy = xmlMalloc(size + 1); if (copy == NULL) return(NULL); memcpy(copy, mem, size); copy[size] = 0; mem = copy; } ret = xmlAllocParserInputBuffer(enc); if (ret == NULL) { xmlFree(copy); return(NULL); } ctxt = xmlMalloc(sizeof(*ctxt)); if (ctxt == NULL) { xmlFreeParserInputBuffer(ret); xmlFree(copy); return(NULL); } ctxt->mem = copy; ctxt->cur = mem; ctxt->size = size; ret->context = ctxt; ret->readcallback = xmlMemRead; ret->closecallback = xmlMemClose; return(ret); } /** * xmlParserInputBufferCreateMem: * @mem: the memory input * @size: the length of the memory block * @enc: the charset encoding if known (deprecated) * * Create a parser input buffer for parsing from a memory area. * * This function makes a copy of the whole input buffer. If you are sure * that the contents of the buffer will remain valid until the document * was parsed, you can avoid the copy by using * xmlParserInputBufferCreateStatic. * * The encoding argument is deprecated and should be set to * XML_CHAR_ENCODING_NONE. The encoding can be changed with * xmlSwitchEncoding or xmlSwitchEncodingName later on. * * Returns the new parser input or NULL in case of error. */ xmlParserInputBufferPtr xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) { if ((mem == NULL) || (size < 0)) return(NULL); return(xmlNewInputBufferMemory(mem, size, 0, enc)); } /** * xmlParserInputBufferCreateStatic: * @mem: the memory input * @size: the length of the memory block * @enc: the charset encoding if known * * Create a parser input buffer for parsing from a memory area. * * This functions assumes that the contents of the input buffer remain * valid until the document was parsed. Use xmlParserInputBufferCreateMem * otherwise. * * The encoding argument is deprecated and should be set to * XML_CHAR_ENCODING_NONE. The encoding can be changed with * xmlSwitchEncoding or xmlSwitchEncodingName later on. * * Returns the new parser input or NULL in case of error. */ xmlParserInputBufferPtr xmlParserInputBufferCreateStatic(const char *mem, int size, xmlCharEncoding enc) { if ((mem == NULL) || (size < 0)) return(NULL); return(xmlNewInputBufferMemory(mem, size, XML_INPUT_BUF_STATIC, enc)); } typedef struct { const char *str; } xmlStringIOCtxt; static int xmlStringRead(void *vctxt, char *buf, int size) { xmlStringIOCtxt *ctxt = vctxt; const char *zero; size_t len; zero = memchr(ctxt->str, 0, size); len = zero ? zero - ctxt->str : size; memcpy(buf, ctxt->str, len); ctxt->str += len; return(len); } static int xmlStringClose(void *vctxt) { xmlFree(vctxt); return(0); } xmlParserInputBufferPtr xmlNewInputBufferString(const char *str, int flags) { xmlParserInputBufferPtr ret; xmlStringIOCtxt *ctxt; if ((flags & XML_INPUT_BUF_STATIC) == 0) return(xmlNewInputBufferMemory(str, strlen(str), flags, XML_CHAR_ENCODING_NONE)); ret = xmlAllocParserInputBuffer(XML_CHAR_ENCODING_NONE); if (ret == NULL) return(NULL); ctxt = xmlMalloc(sizeof(*ctxt)); if (ctxt == NULL) { xmlFreeParserInputBuffer(ret); return(NULL); } ctxt->str = str; ret->context = ctxt; ret->readcallback = xmlStringRead; ret->closecallback = xmlStringClose; return(ret); } #ifdef LIBXML_OUTPUT_ENABLED /** * xmlOutputBufferCreateFd: * @fd: a file descriptor number * @encoder: the encoding converter or NULL * * Create a buffered output for the progressive saving * to a file descriptor * * Returns the new parser output or NULL */ xmlOutputBufferPtr xmlOutputBufferCreateFd(int fd, xmlCharEncodingHandlerPtr encoder) { xmlOutputBufferPtr ret; if (fd < 0) return(NULL); ret = xmlAllocOutputBufferInternal(encoder); if (ret != NULL) { ret->context = (void *) (ptrdiff_t) fd; ret->writecallback = xmlFdWrite; ret->closecallback = NULL; } return(ret); } #endif /* LIBXML_OUTPUT_ENABLED */ /** * xmlParserInputBufferCreateIO: * @ioread: an I/O read function * @ioclose: an I/O close function * @ioctx: an I/O handler * @enc: the charset encoding if known (deprecated) * * Create a buffered parser input for the progressive parsing for the input * from an I/O handler * * The encoding argument is deprecated and should be set to * XML_CHAR_ENCODING_NONE. The encoding can be changed with * xmlSwitchEncoding or xmlSwitchEncodingName later on. * * 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) { /* * Store the data in the incoming raw buffer */ if (in->raw == NULL) { in->raw = xmlBufCreate(); if (in->raw == NULL) { in->error = XML_ERR_NO_MEMORY; return(-1); } } ret = xmlBufAdd(in->raw, (const xmlChar *) buf, len); if (ret != 0) { in->error = XML_ERR_NO_MEMORY; return(-1); } /* * convert as much as possible to the parser reading buffer. */ nbchars = xmlCharEncInput(in); if (nbchars < 0) return(-1); } else { nbchars = len; ret = xmlBufAdd(in->buffer, (xmlChar *) buf, nbchars); if (ret != 0) { in->error = XML_ERR_NO_MEMORY; return(-1); } } 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) { 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) { if (res == -1) in->error = XML_IO_UNKNOWN; else in->error = -res; return(-1); } if (xmlBufAddLen(buf, res) < 0) { in->error = XML_ERR_NO_MEMORY; 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) { res = xmlCharEncInput(in); if (res < 0) return(-1); } 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(); if (out->conv == NULL) { out->error = XML_ERR_NO_MEMORY; return(-1); } } 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) 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) { int errNo = (ret == -1) ? XML_IO_WRITE : -ret; xmlIOErr(errNo, NULL); out->error = errNo; return(ret); } if (out->written > INT_MAX - ret) out->written = INT_MAX; else out->written += ret; } written += nbchars; } while (len > 0); done: 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) 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) { int errNo = (ret == -1) ? XML_IO_WRITE : -ret; xmlIOErr(errNo, NULL); out->error = errNo; 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: 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) 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) { int errNo = (ret == -1) ? XML_IO_WRITE : -ret; xmlIOErr(errNo, NULL); out->error = errNo; return(ret); } if (out->written > INT_MAX - ret) out->written = INT_MAX; else out->written += ret; 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 (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 } /** * xmlNoNetExists: * @filename: the path to check * * DEPRECATED: Internal function, don't use. * * Like xmlCheckFilename but handles file URIs. */ int xmlNoNetExists(const char *filename) { char *fromUri; int ret; if (filename == NULL) return(0); if (xmlConvertUriToPath(filename, &fromUri) < 0) return(0); if (fromUri != NULL) filename = fromUri; ret = xmlCheckFilename(filename); xmlFree(fromUri); return(ret); } /************************************************************************ * * * Input/output callbacks * * * ************************************************************************/ /** * xmlInitIOCallbacks: * * Initialize callback tables. */ void xmlInitIOCallbacks(void) { xmlInputCallbackNr = 1; xmlInputCallbackTable[0].matchcallback = xmlIODefaultMatch; #ifdef LIBXML_OUTPUT_ENABLED xmlOutputCallbackNr = 1; xmlOutputCallbackTable[0].matchcallback = xmlIODefaultMatch; #endif } /** * 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; return(xmlInputCallbackNr++); } /** * xmlRegisterDefaultInputCallbacks: * * Registers the default compiled-in I/O handlers. */ void xmlRegisterDefaultInputCallbacks(void) { xmlRegisterInputCallbacks(xmlIODefaultMatch, NULL, NULL, NULL); } /** * 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 (xmlInputCallbackNr <= 0) return(-1); xmlInputCallbackNr--; return(xmlInputCallbackNr); } /** * xmlCleanupInputCallbacks: * * clears the entire input callback table. this includes the * compiled-in I/O. */ void xmlCleanupInputCallbacks(void) { xmlInputCallbackNr = 0; } #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; return(xmlOutputCallbackNr++); } /** * xmlRegisterDefaultOutputCallbacks: * * Registers the default compiled-in I/O handlers. */ void xmlRegisterDefaultOutputCallbacks (void) { xmlRegisterOutputCallbacks(xmlIODefaultMatch, NULL, NULL, NULL); } /** * 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 (xmlOutputCallbackNr <= 0) return(-1); xmlOutputCallbackNr--; return(xmlOutputCallbackNr); } /** * xmlCleanupOutputCallbacks: * * clears the entire output callback table. this includes the * compiled-in I/O callbacks. */ void xmlCleanupOutputCallbacks(void) { xmlOutputCallbackNr = 0; } #ifdef LIBXML_HTTP_ENABLED /** * xmlRegisterHTTPPostCallbacks: * * DEPRECATED: Support for HTTP POST has been removed. */ void xmlRegisterHTTPPostCallbacks(void) { xmlRegisterDefaultOutputCallbacks(); } #endif #endif /* LIBXML_OUTPUT_ENABLED */