/* * xmlIO.c : implementation of the I/O interfaces used by the parser * * See Copyright for the status of this software. * * Author: Daniel Veillard */ #define IN_LIBXML #include "libxml.h" #include #include #include #include #include #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #include #include #include #else #include #endif #ifdef LIBXML_ZLIB_ENABLED #include #endif #ifdef LIBXML_LZMA_ENABLED #include #endif #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" #ifndef SIZE_MAX #define SIZE_MAX ((size_t) -1) #endif /* #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 */ /************************************************************************ * * * Special escaping routines * * * ************************************************************************/ /* * @param buf a char buffer * @param val a codepoint * * Serializes a hex char ref like ` `. * * Writes at most 9 bytes. Does not include a terminating zero byte. * * @returns the number of bytes written. */ static int xmlSerializeHexCharRef(char *buf, int val) { char *out = buf; int shift = 0, bits; *out++ = '&'; *out++ = '#'; *out++ = 'x'; bits = val; if (bits & 0xFF0000) { shift = 16; bits &= 0xFF0000; } else if (bits & 0x00FF00) { shift = 8; bits &= 0x00FF00; } if (bits & 0xF0F0F0) { shift += 4; } do { int d = (val >> shift) & 0x0F; if (d < 10) *out++ = '0' + d; else *out++ = 'A' + (d - 10); shift -= 4; } while (shift >= 0); *out++ = ';'; return(out - buf); } #include "codegen/escape.inc" /* * @param text input text * @param flags XML_ESCAPE flags * * Escapes certain characters with char refs. * * - XML_ESCAPE_ATTR: for attribute content. * - XML_ESCAPE_NON_ASCII: escape non-ASCII chars. * - XML_ESCAPE_HTML: for HTML content. * - XML_ESCAPE_QUOT: escape double quotes. * * @returns an escaped string or NULL if a memory allocation failed. */ xmlChar * xmlEscapeText(const xmlChar *string, int flags) { const xmlChar *cur; xmlChar *buffer; xmlChar *out; const signed char *tab; size_t size = 50; #ifdef LIBXML_HTML_ENABLED if (flags & XML_ESCAPE_HTML) { if (flags & XML_ESCAPE_ATTR) tab = htmlEscapeTabAttr; else tab = htmlEscapeTab; } else #endif { if (flags & XML_ESCAPE_QUOT) tab = xmlEscapeTabQuot; else if (flags & XML_ESCAPE_ATTR) tab = xmlEscapeTabAttr; else tab = xmlEscapeTab; } buffer = xmlMalloc(size + 1); if (buffer == NULL) return(NULL); out = buffer; cur = string; while (*cur != 0) { char tempBuf[12]; const xmlChar *base; const char *repl; size_t used; size_t replSize; size_t unescapedSize; size_t totalSize; int c; int offset; base = cur; offset = -1; while (1) { c = *cur; if (c < 0x80) { offset = tab[c]; if (offset >= 0) break; } else if (flags & XML_ESCAPE_NON_ASCII) { break; } cur += 1; } unescapedSize = cur - base; if (offset >= 0) { if (c == 0) { replSize = 0; repl = ""; } else { replSize = xmlEscapeContent[offset], repl = &xmlEscapeContent[offset+1]; cur += 1; } } else { int val = 0, len = 4; val = xmlGetUTF8Char(cur, &len); if (val < 0) { val = 0xFFFD; cur += 1; } else { if ((val == 0xFFFE) || (val == 0xFFFF)) val = 0xFFFD; cur += len; } replSize = xmlSerializeHexCharRef(tempBuf, val); repl = tempBuf; } used = out - buffer; totalSize = unescapedSize + replSize; if (totalSize > size - used) { xmlChar *tmp; int newSize; if ((size > (SIZE_MAX - 1) / 2) || (totalSize > (SIZE_MAX - 1) / 2 - size)) { xmlFree(buffer); return(NULL); } newSize = size + totalSize; if (*cur != 0) newSize *= 2; tmp = xmlRealloc(buffer, newSize + 1); if (tmp == NULL) { xmlFree(buffer); return(NULL); } buffer = tmp; size = newSize; out = buffer + used; } memcpy(out, base, unescapedSize); out += unescapedSize; memcpy(out, repl, replSize); out += replSize; if (c == 0) break; base = cur; } *out = 0; return(buffer); } #ifdef LIBXML_OUTPUT_ENABLED void xmlSerializeText(xmlOutputBuffer *buf, const xmlChar *string, size_t maxSize, unsigned flags) { const xmlChar *cur; const signed char *tab; if (string == NULL) return; #ifdef LIBXML_HTML_ENABLED if (flags & XML_ESCAPE_HTML) { if (flags & XML_ESCAPE_ATTR) tab = htmlEscapeTabAttr; else tab = htmlEscapeTab; } else #endif { if (flags & XML_ESCAPE_QUOT) tab = xmlEscapeTabQuot; else if (flags & XML_ESCAPE_ATTR) tab = xmlEscapeTabAttr; else tab = xmlEscapeTab; } cur = string; while (1) { const xmlChar *base; int c; int offset; base = cur; offset = -1; while (1) { if ((size_t) (cur - string) >= maxSize) break; c = (unsigned char) *cur; if (c < 0x80) { offset = tab[c]; if (offset >= 0) break; } else if (flags & XML_ESCAPE_NON_ASCII) { break; } cur += 1; } if (cur > base) xmlOutputBufferWrite(buf, cur - base, (char *) base); if ((size_t) (cur - string) >= maxSize) break; if (offset >= 0) { if (c == 0) break; xmlOutputBufferWrite(buf, xmlEscapeContent[offset], &xmlEscapeContent[offset+1]); cur += 1; } else { char tempBuf[12]; int tempSize; int val = 0, len = 4; val = xmlGetUTF8Char(cur, &len); if (val < 0) { val = 0xFFFD; cur += 1; } else { if ((val == 0xFFFE) || (val == 0xFFFF)) val = 0xFFFD; cur += len; } tempSize = xmlSerializeHexCharRef(tempBuf, val); xmlOutputBufferWrite(buf, tempSize, tempBuf); } } } #endif /* LIBXML_OUTPUT_ENABLED */ /************************************************************************ * * * Error handling * * * ************************************************************************/ /** * Convert errno to xmlParserErrors. * * @param err the error number * @returns an xmlParserErrors code. */ static int xmlIOErr(int err) { xmlParserErrors code; switch (err) { #ifdef EACCES case EACCES: code = XML_IO_EACCES; break; #endif #ifdef EAGAIN case EAGAIN: code = XML_IO_EAGAIN; break; #endif #ifdef EBADF case EBADF: code = XML_IO_EBADF; break; #endif #ifdef EBADMSG case EBADMSG: code = XML_IO_EBADMSG; break; #endif #ifdef EBUSY case EBUSY: code = XML_IO_EBUSY; break; #endif #ifdef ECANCELED case ECANCELED: code = XML_IO_ECANCELED; break; #endif #ifdef ECHILD case ECHILD: code = XML_IO_ECHILD; break; #endif #ifdef EDEADLK case EDEADLK: code = XML_IO_EDEADLK; break; #endif #ifdef EDOM case EDOM: code = XML_IO_EDOM; break; #endif #ifdef EEXIST case EEXIST: code = XML_IO_EEXIST; break; #endif #ifdef EFAULT case EFAULT: code = XML_IO_EFAULT; break; #endif #ifdef EFBIG case EFBIG: code = XML_IO_EFBIG; break; #endif #ifdef EINPROGRESS case EINPROGRESS: code = XML_IO_EINPROGRESS; break; #endif #ifdef EINTR case EINTR: code = XML_IO_EINTR; break; #endif #ifdef EINVAL case EINVAL: code = XML_IO_EINVAL; break; #endif #ifdef EIO case EIO: code = XML_IO_EIO; break; #endif #ifdef EISDIR case EISDIR: code = XML_IO_EISDIR; break; #endif #ifdef EMFILE case EMFILE: code = XML_IO_EMFILE; break; #endif #ifdef EMLINK case EMLINK: code = XML_IO_EMLINK; break; #endif #ifdef EMSGSIZE case EMSGSIZE: code = XML_IO_EMSGSIZE; break; #endif #ifdef ENAMETOOLONG case ENAMETOOLONG: code = XML_IO_ENAMETOOLONG; break; #endif #ifdef ENFILE case ENFILE: code = XML_IO_ENFILE; break; #endif #ifdef ENODEV case ENODEV: code = XML_IO_ENODEV; break; #endif #ifdef ENOENT case ENOENT: code = XML_IO_ENOENT; break; #endif #ifdef ENOEXEC case ENOEXEC: code = XML_IO_ENOEXEC; break; #endif #ifdef ENOLCK case ENOLCK: code = XML_IO_ENOLCK; break; #endif #ifdef ENOMEM case ENOMEM: code = XML_IO_ENOMEM; break; #endif #ifdef ENOSPC case ENOSPC: code = XML_IO_ENOSPC; break; #endif #ifdef ENOSYS case ENOSYS: code = XML_IO_ENOSYS; break; #endif #ifdef ENOTDIR case ENOTDIR: code = XML_IO_ENOTDIR; break; #endif #ifdef ENOTEMPTY case ENOTEMPTY: code = XML_IO_ENOTEMPTY; break; #endif #ifdef ENOTSUP case ENOTSUP: code = XML_IO_ENOTSUP; break; #endif #ifdef ENOTTY case ENOTTY: code = XML_IO_ENOTTY; break; #endif #ifdef ENXIO case ENXIO: code = XML_IO_ENXIO; break; #endif #ifdef EPERM case EPERM: code = XML_IO_EPERM; break; #endif #ifdef EPIPE case EPIPE: code = XML_IO_EPIPE; break; #endif #ifdef ERANGE case ERANGE: code = XML_IO_ERANGE; break; #endif #ifdef EROFS case EROFS: code = XML_IO_EROFS; break; #endif #ifdef ESPIPE case ESPIPE: code = XML_IO_ESPIPE; break; #endif #ifdef ESRCH case ESRCH: code = XML_IO_ESRCH; break; #endif #ifdef ETIMEDOUT case ETIMEDOUT: code = XML_IO_ETIMEDOUT; break; #endif #ifdef EXDEV case EXDEV: code = XML_IO_EXDEV; break; #endif #ifdef ENOTSOCK case ENOTSOCK: code = XML_IO_ENOTSOCK; break; #endif #ifdef EISCONN case EISCONN: code = XML_IO_EISCONN; break; #endif #ifdef ECONNREFUSED case ECONNREFUSED: code = XML_IO_ECONNREFUSED; break; #endif #ifdef ENETUNREACH case ENETUNREACH: code = XML_IO_ENETUNREACH; break; #endif #ifdef EADDRINUSE case EADDRINUSE: code = XML_IO_EADDRINUSE; break; #endif #ifdef EALREADY case EALREADY: code = XML_IO_EALREADY; break; #endif #ifdef EAFNOSUPPORT case EAFNOSUPPORT: code = XML_IO_EAFNOSUPPORT; break; #endif default: code = XML_IO_UNKNOWN; break; } return(code); } /************************************************************************ * * * Standard I/O for file accesses * * * ************************************************************************/ #if defined(_WIN32) /** * Convert a string from utf-8 to wchar (WINDOWS ONLY!) * * @param u8String uft-8 string */ 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 /** * @deprecated This never really worked. * * @param path the input file path * @returns a copy of path. */ xmlChar * xmlNormalizeWindowsPath(const xmlChar *path) { return xmlStrdup(path); } /** * if stat is not available on the target machine, * * @deprecated Internal function, don't use. * * @param path the path to check * @returns 0 if stat fails, 2 if stat succeeds and the file is * a directory, 1 otherwise. */ int xmlCheckFilename(const char *path) { #if defined(_WIN32) struct _stat stat_buffer; #else struct stat stat_buffer; #endif int res; if (path == NULL) return(0); #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 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); } typedef struct { int fd; } xmlFdIOCtxt; /** * @param filename the URI for matching * @param write whether the fd is opened for writing * @param out pointer to resulting context * @returns an xmlParserErrors code */ static xmlParserErrors xmlFdOpen(const char *filename, int write, int *out) { char *fromUri = NULL; int flags; int fd; xmlParserErrors 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, 0666); xmlFree(wpath); } #else if (write) flags = O_WRONLY | O_CREAT | O_TRUNC; else flags = O_RDONLY; fd = open(filename, flags, 0666); #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 { ret = xmlIOErr(errno); } } else { *out = fd; ret = XML_ERR_OK; } xmlFree(fromUri); return(ret); } /** * Read `len` bytes to `buffer` from the I/O channel. * * @param context the I/O context * @param buffer where to drop data * @param len number of bytes to read * @returns the number of bytes read */ static int xmlFdRead(void *context, char *buffer, int len) { xmlFdIOCtxt *fdctxt = context; int fd = fdctxt->fd; 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(errno)); } if (bytes == 0) break; ret += bytes; buffer += bytes; len -= bytes; } return(ret); } #ifdef LIBXML_OUTPUT_ENABLED /** * Write `len` bytes from `buffer` to the I/O channel. * * @param context the I/O context * @param buffer where to get data * @param len number of bytes to write * @returns the number of bytes written */ static int xmlFdWrite(void *context, const char *buffer, int len) { xmlFdIOCtxt *fdctxt = context; int fd = fdctxt->fd; int ret = 0; int bytes; while (len > 0) { bytes = write(fd, buffer, len); if (bytes < 0) return(-xmlIOErr(errno)); ret += bytes; buffer += bytes; len -= bytes; } return(ret); } #endif /* LIBXML_OUTPUT_ENABLED */ static int xmlFdFree(void *context) { xmlFree(context); return(XML_ERR_OK); } /** * Close an I/O channel * * @param context the I/O context * @returns 0 in case of success and error code otherwise */ static int xmlFdClose (void * context) { xmlFdIOCtxt *fdctxt = context; int fd = fdctxt->fd; int ret; ret = close(fd); xmlFree(fdctxt); if (ret < 0) return(xmlIOErr(errno)); return(XML_ERR_OK); } /** * @deprecated Internal function, don't use. * * @param filename the URI for matching * @returns 1 if matches, 0 otherwise */ int xmlFileMatch (const char *filename ATTRIBUTE_UNUSED) { return(1); } /** * input from FILE * * * @param filename the URI for matching * @param write whether the file is opened for writing * @param out pointer to resulting context * @returns an xmlParserErrors code */ static xmlParserErrors xmlFileOpenSafe(const char *filename, int write, void **out) { char *fromUri = NULL; FILE *fd; xmlParserErrors 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(errno); } } *out = fd; xmlFree(fromUri); return(ret); } /** * @deprecated Internal function, don't use. * * @param filename the URI for matching * @returns an IO context or NULL in case or failure */ void * xmlFileOpen(const char *filename) { void *context; xmlFileOpenSafe(filename, 0, &context); return(context); } /** * @deprecated Internal function, don't use. * * @param context the I/O context * @param buffer where to drop data * @param len number of bytes to write * @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(errno)); return(bytes); } #ifdef LIBXML_OUTPUT_ENABLED /** * Write `len` bytes from `buffer` to the I/O channel. * * @param context the I/O context * @param buffer where to drop data * @param len number of bytes to write * @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(errno)); return(len); } #endif /* LIBXML_OUTPUT_ENABLED */ /** * Flush an I/O channel * * @param context the I/O context */ static int xmlFileFlush (void * context) { FILE *file = context; if (file == NULL) return(-1); if (fflush(file) != 0) return(xmlIOErr(errno)); return(XML_ERR_OK); } /** * @deprecated Internal function, don't use. * * @param context the I/O context * @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(errno)); return(0); } #ifdef LIBXML_OUTPUT_ENABLED /** * Write `len` bytes from `buffer` to the xml buffer * * @param context the xmlBuffer * @param buffer the data to write * @param len number of bytes to write * @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 * * * ************************************************************************/ /** * Read `len` bytes to `buffer` from the compressed I/O channel. * * @param context the I/O context * @param buffer where to drop data * @param len number of bytes to write * @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) return(-XML_IO_UNKNOWN); return(ret); } #ifdef LIBXML_OUTPUT_ENABLED /** * Write `len` bytes from `buffer` to the compressed I/O channel. * * @param context the I/O context * @param buffer where to drop data * @param len number of bytes to write * @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) return(-XML_IO_UNKNOWN); return(ret); } #endif /* LIBXML_OUTPUT_ENABLED */ /** * Close a compressed I/O channel * * @param context the I/O context */ static int xmlGzfileClose (void * context) { if (gzclose((gzFile) context) != Z_OK) return(XML_IO_UNKNOWN); return(0); } #endif /* LIBXML_ZLIB_ENABLED */ /************************************************************************ * * * I/O for compressed file accesses * * * ************************************************************************/ #ifdef LIBXML_LZMA_ENABLED #include "private/xzlib.h" /** * Read `len` bytes to `buffer` from the compressed I/O channel. * * @param context the I/O context * @param buffer where to drop data * @param len number of bytes to write * @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) return(-XML_IO_UNKNOWN); return(ret); } /** * Close a compressed I/O channel * * @param context the I/O context */ static int xmlXzfileClose (void * context) { if (__libxml2_xzclose((xzFile) context) != LZMA_OK) return(XML_IO_UNKNOWN); return(0); } #endif /* LIBXML_LZMA_ENABLED */ /************************************************************************ * * * Input/output buffers * * * ************************************************************************/ static int xmlIODefaultMatch(const char *filename ATTRIBUTE_UNUSED) { return(1); } #if defined(LIBXML_LZMA_ENABLED) || defined(LIBXML_ZLIB_ENABLED) #ifdef _WIN32 typedef __int64 xmlFileOffset; #else typedef off_t xmlFileOffset; #endif static xmlFileOffset xmlSeek(int fd, xmlFileOffset offset, int whence) { #ifdef _WIN32 HANDLE h = (HANDLE) _get_osfhandle(fd); /* * Windows doesn't return an error on unseekable files like pipes. */ if (h != INVALID_HANDLE_VALUE && GetFileType(h) != FILE_TYPE_DISK) return -1; return _lseeki64(fd, offset, whence); #else return lseek(fd, offset, whence); #endif } #endif /* defined(LIBXML_LZMA_ENABLED) || defined(LIBXML_ZLIB_ENABLED) */ /** * Update the buffer to read from `fd`. Supports the XML_INPUT_UNZIP * flag. * * @param buf parser input buffer * @param fd file descriptor * @param flags flags * @returns an xmlParserErrors code. */ xmlParserErrors xmlInputFromFd(xmlParserInputBuffer *buf, int fd, xmlParserInputFlags flags) { xmlFdIOCtxt *fdctxt; int copy; (void) flags; #ifdef LIBXML_LZMA_ENABLED if (flags & XML_INPUT_UNZIP) { xzFile xzStream; xmlFileOffset pos; pos = xmlSeek(fd, 0, SEEK_CUR); copy = dup(fd); if (copy == -1) return(xmlIOErr(errno)); xzStream = __libxml2_xzdopen("?", copy, "rb"); if (xzStream == NULL) { close(copy); } else { int compressed = (__libxml2_xzcompressed(xzStream) > 0); if ((compressed) || /* Try to rewind if not gzip compressed */ (pos < 0) || (xmlSeek(fd, pos, SEEK_SET) < 0)) { /* * If a file isn't seekable, we pipe uncompressed * input through xzlib. */ buf->context = xzStream; buf->readcallback = xmlXzfileRead; buf->closecallback = xmlXzfileClose; buf->compressed = compressed; return(XML_ERR_OK); } xmlXzfileClose(xzStream); } } #endif /* LIBXML_LZMA_ENABLED */ #ifdef LIBXML_ZLIB_ENABLED if (flags & XML_INPUT_UNZIP) { gzFile gzStream; xmlFileOffset pos; pos = xmlSeek(fd, 0, SEEK_CUR); copy = dup(fd); if (copy == -1) return(xmlIOErr(errno)); gzStream = gzdopen(copy, "rb"); if (gzStream == NULL) { close(copy); } else { int compressed = (gzdirect(gzStream) == 0); if ((compressed) || /* Try to rewind if not gzip compressed */ (pos < 0) || (xmlSeek(fd, pos, SEEK_SET) < 0)) { /* * If a file isn't seekable, we pipe uncompressed * input through zlib. */ buf->context = gzStream; buf->readcallback = xmlGzfileRead; buf->closecallback = xmlGzfileClose; buf->compressed = compressed; return(XML_ERR_OK); } xmlGzfileClose(gzStream); } } #endif /* LIBXML_ZLIB_ENABLED */ copy = dup(fd); if (copy == -1) return(xmlIOErr(errno)); fdctxt = xmlMalloc(sizeof(*fdctxt)); if (fdctxt == NULL) { close(copy); return(XML_ERR_NO_MEMORY); } fdctxt->fd = copy; buf->context = fdctxt; buf->readcallback = xmlFdRead; buf->closecallback = xmlFdClose; return(XML_ERR_OK); } #ifdef LIBXML_OUTPUT_ENABLED /** * @param buf input buffer to be filled * @param filename filename or URI * @param compression compression level or 0 * @returns an xmlParserErrors code. */ static xmlParserErrors xmlOutputDefaultOpen(xmlOutputBufferPtr buf, const char *filename, int compression) { xmlFdIOCtxt *fdctxt; int fd; (void) compression; if (!strcmp(filename, "-")) { fd = dup(STDOUT_FILENO); if (fd < 0) return(xmlIOErr(errno)); } 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(XML_IO_UNKNOWN); } buf->context = gzStream; buf->writecallback = xmlGzfileWrite; buf->closecallback = xmlGzfileClose; return(XML_ERR_OK); } #endif /* LIBXML_ZLIB_ENABLED */ fdctxt = xmlMalloc(sizeof(*fdctxt)); if (fdctxt == NULL) { close(fd); return(XML_ERR_NO_MEMORY); } fdctxt->fd = fd; buf->context = fdctxt; buf->writecallback = xmlFdWrite; buf->closecallback = xmlFdClose; return(XML_ERR_OK); } #endif /** * Create a buffered parser input for progressive parsing. * * @deprecated Use xmlNewInputFrom*. * * 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. * * @param enc the charset encoding if known (deprecated) * @returns the new parser input or NULL */ xmlParserInputBuffer * xmlAllocParserInputBuffer(xmlCharEncoding enc) { xmlParserInputBufferPtr ret; ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer)); if (ret == NULL) { return(NULL); } memset(ret, 0, sizeof(xmlParserInputBuffer)); ret->buffer = xmlBufCreate(XML_IO_BUFFER_SIZE); if (ret->buffer == NULL) { xmlFree(ret); return(NULL); } if (enc != XML_CHAR_ENCODING_NONE) { if (xmlLookupCharEncodingHandler(enc, &ret->encoder) != XML_ERR_OK) { /* We can't handle errors properly here. */ xmlFreeParserInputBuffer(ret); return(NULL); } } if (ret->encoder != NULL) ret->raw = xmlBufCreate(XML_IO_BUFFER_SIZE); 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 /** * Create a buffered parser output * * Consumes `encoder` but not in error case. * * @param encoder the encoding converter or NULL * @returns the new parser output or NULL */ xmlOutputBuffer * xmlAllocOutputBuffer(xmlCharEncodingHandler *encoder) { xmlOutputBufferPtr ret; ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer)); if (ret == NULL) { return(NULL); } memset(ret, 0, sizeof(xmlOutputBuffer)); ret->buffer = xmlBufCreate(MINLEN); if (ret->buffer == NULL) { xmlFree(ret); return(NULL); } ret->encoder = encoder; if (encoder != NULL) { ret->conv = xmlBufCreate(MINLEN); 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 */ /** * Free up the memory used by a buffered parser input * * @param in a buffered parser input */ void xmlFreeParserInputBuffer(xmlParserInputBuffer *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 /** * flushes and close the output I/O channel * and free up all the associated resources * * @param out a buffered output * @returns the number of byte written or a negative xmlParserErrors * code in case of error. */ int xmlOutputBufferClose(xmlOutputBuffer *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) && (!xmlIsCatastrophicError(XML_ERR_FATAL, out->error))) { if (code < 0) out->error = XML_IO_UNKNOWN; else out->error = code; } } if (out->error != XML_ERR_OK) ret = -out->error; 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 */ /** * @param URI the filename or URI * @param enc encoding enum (deprecated) * @param flags XML_INPUT flags * @param out pointer to resulting input buffer * @returns an xmlParserErrors code. */ xmlParserErrors xmlParserInputBufferCreateUrl(const char *URI, xmlCharEncoding enc, xmlParserInputFlags flags, xmlParserInputBuffer **out) { xmlParserInputBufferPtr buf; xmlParserErrors ret; int i; xmlInitParser(); *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) { int fd; ret = xmlFdOpen(URI, 0, &fd); if (ret == XML_ERR_OK) { ret = xmlInputFromFd(buf, fd, flags); close(fd); break; } else if (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); } /** * 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 * * Internal implementation, never uses the callback installed with * #xmlParserInputBufferCreateFilenameDefault. * * @deprecated Use #xmlNewInputFromUrl. * * @param URI a C string containing the URI or filename * @param enc the charset encoding if known * @returns the new parser input or NULL */ xmlParserInputBuffer * __xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) { xmlParserInputBufferPtr ret; xmlParserInputBufferCreateUrl(URI, enc, 0, &ret); return(ret); } /** * 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 * * Allows the actual function to be overridden with * #xmlParserInputBufferCreateFilenameDefault. * * @deprecated Use #xmlNewInputFromUrl. * * @param URI a C string containing the URI or filename * @param enc the charset encoding if known * @returns the new parser input or NULL */ xmlParserInputBuffer * xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) { xmlParserInputBufferPtr ret; xmlParserErrors code; if (xmlParserInputBufferCreateFilenameValue != NULL) return(xmlParserInputBufferCreateFilenameValue(URI, enc)); code = xmlParserInputBufferCreateUrl(URI, enc, 0, &ret); /* * xmlParserInputBufferCreateFilename has no way to return * the kind of error although it really is crucial. * All we can do is to set the global error. */ if ((code != XML_ERR_OK) && (code != XML_IO_ENOENT)) { if (xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_IO, code, XML_ERR_ERROR, URI, 0, NULL, NULL, NULL, 0, 0, "Failed to open file\n") < 0) xmlRaiseMemoryError(NULL, NULL, NULL, XML_FROM_IO, NULL); } return(ret); } #ifdef LIBXML_OUTPUT_ENABLED /** * 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. * * Consumes `encoder` but not in error case. * * Internal implementation, never uses the callback installed with * #xmlOutputBufferCreateFilenameDefault. * * @param URI a C string containing the URI or filename * @param encoder the encoding converter or NULL * @param compression the compression ration (0 none, 9 max). * @returns the new output or NULL */ xmlOutputBuffer * __xmlOutputBufferCreateFilename(const char *URI, xmlCharEncodingHandler *encoder, int compression) { xmlOutputBufferPtr ret = NULL; xmlURIPtr puri; int i = 0; char *unescaped = NULL; xmlInitParser(); if (URI == NULL) goto error; 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); goto error; } URI = unescaped; } xmlFreeURI(puri); } /* * Allocate the Output buffer front-end. */ ret = xmlAllocOutputBuffer(encoder); if (ret == NULL) goto error; /* * 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]; xmlParserErrors 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) { /* Don't free encoder */ ret->encoder = NULL; xmlOutputBufferClose(ret); ret = NULL; } error: xmlFree(unescaped); return(ret); } /** * 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. * * Consumes `encoder` but not in error case. * * Allows the actual function to be overridden with * #xmlOutputBufferCreateFilenameDefault. * * @param URI a C string containing the URI or filename * @param encoder the encoding converter or NULL * @param compression the compression ration (0 none, 9 max). * @returns the new output or NULL */ xmlOutputBuffer * xmlOutputBufferCreateFilename(const char *URI, xmlCharEncodingHandler *encoder, int compression ATTRIBUTE_UNUSED) { if ((xmlOutputBufferCreateFilenameValue)) { return xmlOutputBufferCreateFilenameValue(URI, encoder, compression); } return __xmlOutputBufferCreateFilename(URI, encoder, compression); } #endif /* LIBXML_OUTPUT_ENABLED */ /** * Create a buffered parser input for the progressive parsing of a FILE * * buffered C I/O * * @deprecated Don't use. * * 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. * * @param file a FILE* * @param enc the charset encoding if known (deprecated) * @returns the new parser input or NULL */ xmlParserInputBuffer * 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 /** * Create a buffered output for the progressive saving to a `FILE *` * buffered C I/O. * * Consumes `encoder` but not in error case. * * @param file a `FILE *` * @param encoder the encoding converter or NULL * @returns the new parser output or NULL */ xmlOutputBuffer * xmlOutputBufferCreateFile(FILE *file, xmlCharEncodingHandler *encoder) { xmlOutputBufferPtr ret; if (file == NULL) { return(NULL); } ret = xmlAllocOutputBuffer(encoder); if (ret != NULL) { ret->context = file; ret->writecallback = xmlFileWrite; ret->closecallback = xmlFileFlush; } return(ret); } /** * Create a buffered output for the progressive saving to a xmlBuffer * * Consumes `encoder` but not in error case. * * @param buffer a xmlBuffer * @param encoder the encoding converter or NULL * @returns the new parser output or NULL */ xmlOutputBuffer * xmlOutputBufferCreateBuffer(xmlBuffer *buffer, xmlCharEncodingHandler *encoder) { xmlOutputBufferPtr ret; if (buffer == NULL) { return(NULL); } ret = xmlOutputBufferCreateIO(xmlBufferWrite, NULL, (void *) buffer, encoder); return(ret); } /** * Gives a pointer to the data currently held in the output buffer * * @param out an xmlOutputBuffer * @returns a pointer to the data or NULL in case of error */ const xmlChar * xmlOutputBufferGetContent(xmlOutputBuffer *out) { if ((out == NULL) || (out->buffer == NULL) || (out->error != 0)) return(NULL); return(xmlBufContent(out->buffer)); } /** * Gives the length of the data currently held in the output buffer * * @param out an xmlOutputBuffer * @returns 0 in case or error or no data is held, the size otherwise */ size_t xmlOutputBufferGetSize(xmlOutputBuffer *out) { if ((out == NULL) || (out->buffer == NULL) || (out->error != 0)) return(0); return(xmlBufUse(out->buffer)); } #endif /* LIBXML_OUTPUT_ENABLED */ /** * Create a buffered parser input for the progressive parsing for the input * from a file descriptor * * @deprecated Use #xmlNewInputFromFd. * * 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. * * @param fd a file descriptor number * @param enc the charset encoding if known (deprecated) * @returns the new parser input or NULL */ xmlParserInputBuffer * xmlParserInputBufferCreateFd(int fd, xmlCharEncoding enc) { xmlParserInputBufferPtr ret; if (fd < 0) return(NULL); ret = xmlAllocParserInputBuffer(enc); if (ret != NULL) { xmlFdIOCtxt *fdctxt; fdctxt = xmlMalloc(sizeof(*fdctxt)); if (fdctxt == NULL) { return(NULL); } fdctxt->fd = fd; ret->context = fdctxt; ret->readcallback = xmlFdRead; ret->closecallback = xmlFdFree; } return(ret); } typedef struct { 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; xmlFree(ctxt); return(0); } /** * Create an input buffer for memory. * * @param mem memory buffer * @param size size of buffer * @param flags flags * @param enc the charset encoding if known (deprecated) * @returns the new input buffer or NULL. */ xmlParserInputBuffer * xmlNewInputBufferMemory(const void *mem, size_t size, xmlParserInputFlags flags, xmlCharEncoding enc) { xmlParserInputBufferPtr ret; if ((flags & XML_INPUT_BUF_STATIC) && ((flags & XML_INPUT_BUF_ZERO_TERMINATED) == 0)) { xmlMemIOCtxt *ctxt; /* * Static buffer without zero terminator. * Stream memory to avoid a copy. */ ret = xmlAllocParserInputBuffer(enc); if (ret == NULL) return(NULL); ctxt = xmlMalloc(sizeof(*ctxt)); if (ctxt == NULL) { xmlFreeParserInputBuffer(ret); return(NULL); } ctxt->cur = mem; ctxt->size = size; ret->context = ctxt; ret->readcallback = xmlMemRead; ret->closecallback = xmlMemClose; } else { ret = xmlMalloc(sizeof(*ret)); if (ret == NULL) return(NULL); memset(ret, 0, sizeof(xmlParserInputBuffer)); ret->compressed = -1; ret->buffer = xmlBufCreateMem((const xmlChar *) mem, size, (flags & XML_INPUT_BUF_STATIC ? 1 : 0)); if (ret->buffer == NULL) { xmlFree(ret); return(NULL); } } return(ret); } /** * Create a parser input buffer for parsing from a memory area. * * @deprecated Use #xmlNewInputFromMemory. * * 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. * * @param mem the memory input * @param size the length of the memory block * @param enc the charset encoding if known (deprecated) * @returns the new parser input or NULL in case of error. */ xmlParserInputBuffer * xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) { if ((mem == NULL) || (size < 0)) return(NULL); return(xmlNewInputBufferMemory(mem, size, 0, enc)); } /** * Create a parser input buffer for parsing from a memory area. * * @deprecated Use #xmlNewInputFromMemory. * * 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. * * @param mem the memory input * @param size the length of the memory block * @param enc the charset encoding if known * @returns the new parser input or NULL in case of error. */ xmlParserInputBuffer * xmlParserInputBufferCreateStatic(const char *mem, int size, xmlCharEncoding enc) { if ((mem == NULL) || (size < 0)) return(NULL); return(xmlNewInputBufferMemory(mem, size, XML_INPUT_BUF_STATIC, enc)); } /** * Create an input buffer for a null-terminated C string. * * @deprecated Use #xmlNewInputFromString. * * @param str C string * @param flags flags * @returns the new input buffer or NULL. */ xmlParserInputBuffer * xmlNewInputBufferString(const char *str, xmlParserInputFlags flags) { xmlParserInputBufferPtr ret; ret = xmlMalloc(sizeof(*ret)); if (ret == NULL) return(NULL); memset(ret, 0, sizeof(xmlParserInputBuffer)); ret->compressed = -1; ret->buffer = xmlBufCreateMem((const xmlChar *) str, strlen(str), (flags & XML_INPUT_BUF_STATIC ? 1 : 0)); if (ret->buffer == NULL) { xmlFree(ret); return(NULL); } return(ret); } #ifdef LIBXML_OUTPUT_ENABLED /** * Create a buffered output for the progressive saving * to a file descriptor * * Consumes `encoder` but not in error case. * * @param fd a file descriptor number * @param encoder the encoding converter or NULL * @returns the new parser output or NULL */ xmlOutputBuffer * xmlOutputBufferCreateFd(int fd, xmlCharEncodingHandler *encoder) { xmlOutputBufferPtr ret; if (fd < 0) { return(NULL); } ret = xmlAllocOutputBuffer(encoder); if (ret != NULL) { xmlFdIOCtxt *fdctxt; fdctxt = xmlMalloc(sizeof(*fdctxt)); if (fdctxt == NULL) { return(NULL); } fdctxt->fd = fd; ret->context = fdctxt; ret->writecallback = xmlFdWrite; ret->closecallback = xmlFdFree; } return(ret); } #endif /* LIBXML_OUTPUT_ENABLED */ /** * Create a buffered parser input for the progressive parsing for the input * from an I/O handler * * @deprecated Use #xmlNewInputFromIO. * * 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. * * @param ioread an I/O read function * @param ioclose an I/O close function * @param ioctx an I/O handler * @param enc the charset encoding if known (deprecated) * @returns the new parser input or NULL */ xmlParserInputBuffer * 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 /** * Create a buffered output for the progressive saving * to an I/O handler * * Consumes `encoder` but not in error case. * * @param iowrite an I/O write function * @param ioclose an I/O close function * @param ioctx an I/O handler * @param encoder the charset encoding if known * @returns the new parser output or NULL */ xmlOutputBuffer * xmlOutputBufferCreateIO(xmlOutputWriteCallback iowrite, xmlOutputCloseCallback ioclose, void *ioctx, xmlCharEncodingHandler *encoder) { xmlOutputBufferPtr ret; if (iowrite == NULL) { return(NULL); } ret = xmlAllocOutputBuffer(encoder); if (ret != NULL) { ret->context = (void *) ioctx; ret->writecallback = iowrite; ret->closecallback = ioclose; } return(ret); } #endif /* LIBXML_OUTPUT_ENABLED */ /** * Registers a callback for URI input file handling * * @deprecated Use #xmlCtxtSetResourceLoader or similar functions. * * @param func function pointer to the new ParserInputBufferCreateFilenameFunc * @returns the old value of the registration function */ xmlParserInputBufferCreateFilenameFunc xmlParserInputBufferCreateFilenameDefault( xmlParserInputBufferCreateFilenameFunc func) { xmlParserInputBufferCreateFilenameFunc old; old = xmlParserInputBufferCreateFilenameValue; if (old == NULL) old = __xmlParserInputBufferCreateFilename; if (func == __xmlParserInputBufferCreateFilename) func = NULL; xmlParserInputBufferCreateFilenameValue = func; return(old); } /** * Registers a callback for URI output file handling * * @param func function pointer to the new OutputBufferCreateFilenameFunc * @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); } /** * 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. * * @deprecated Internal function, don't use. * * @param in a buffered parser input * @param len the size in bytes of the array. * @param buf an char array * @returns the number of chars read and stored in the buffer, or -1 * in case of error. */ int xmlParserInputBufferPush(xmlParserInputBuffer *in, int len, const char *buf) { size_t 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(50); 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 = SIZE_MAX; if (xmlCharEncInput(in, &nbchars, /* flush */ 0) != XML_ENC_ERR_SUCCESS) return(-1); if (nbchars > INT_MAX) nbchars = INT_MAX; } else { nbchars = len; ret = xmlBufAdd(in->buffer, (xmlChar *) buf, nbchars); if (ret != 0) { in->error = XML_ERR_NO_MEMORY; return(-1); } } return(nbchars); } /* * 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); } /** * 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 * * @deprecated Internal function, don't use. * * @param in a buffered parser input * @param len indicative value of the amount of chars to read * @returns the number of chars read and stored in the buffer, or -1 * in case of error. */ int xmlParserInputBufferGrow(xmlParserInputBuffer *in, int len) { int res = 0; if ((in == NULL) || (in->error)) return(-1); if (len < MINLEN) len = MINLEN; /* * Call the read method for this I/O type. */ if (in->readcallback != NULL) { xmlBufPtr buf; if (in->encoder == NULL) { buf = in->buffer; } else { /* * Some users only set 'encoder' and expect us to create * the raw buffer lazily. */ if (in->raw == NULL) { in->raw = xmlBufCreate(XML_IO_BUFFER_SIZE); if (in->raw == NULL) { in->error = XML_ERR_NO_MEMORY; return(-1); } } buf = in->raw; } if (xmlBufGrow(buf, len) < 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); } } /* * Handle encoding. */ if (in->encoder != NULL) { size_t sizeOut; /* * Don't convert whole buffer when reading from memory. */ if (in->readcallback == NULL) sizeOut = len; else sizeOut = SIZE_MAX; if (xmlCharEncInput(in, &sizeOut, /* flush */ 0) != XML_ENC_ERR_SUCCESS) return(-1); res = sizeOut; } return(res); } /** * Same as #xmlParserInputBufferGrow. * * @deprecated Internal function, don't use. * * @param in a buffered parser input * @param len indicative value of the amount of chars to read * @returns the number of chars read and stored in the buffer, or -1 * in case of error. */ int xmlParserInputBufferRead(xmlParserInputBuffer *in, int len) { return(xmlParserInputBufferGrow(in, len)); } #ifdef LIBXML_OUTPUT_ENABLED /** * 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. * * @param out a buffered parser output * @param len the size in bytes of the array. * @param data an char array * @returns the number of chars immediately written, or -1 * in case of error. */ int xmlOutputBufferWrite(xmlOutputBuffer *out, int len, const char *data) { xmlBufPtr buf = NULL; size_t written = 0; int ret; if ((out == NULL) || (out->error)) return(-1); if (len < 0) return(0); ret = xmlBufAdd(out->buffer, (const xmlChar *) data, len); if (ret != 0) { out->error = XML_ERR_NO_MEMORY; return(-1); } /* * first handle encoding stuff. */ if (out->encoder != NULL) { /* * Store the data in the incoming raw buffer */ if (out->conv == NULL) { out->conv = xmlBufCreate(MINLEN); if (out->conv == NULL) { out->error = XML_ERR_NO_MEMORY; return(-1); } } /* * convert as much as possible to the parser reading buffer. */ if (xmlBufUse(out->buffer) < 256) { ret = 0; } else { ret = xmlCharEncOutput(out, 0); if (ret < 0) return(-1); } if (out->writecallback) buf = out->conv; else written = ret; } else { if (out->writecallback) buf = out->buffer; else written = len; } if ((buf != NULL) && (out->writecallback)) { /* * second write the stuff to the I/O channel */ while (1) { size_t nbchars = xmlBufUse(buf); if (nbchars < MINLEN) break; ret = out->writecallback(out->context, (const char *)xmlBufContent(buf), nbchars); if (ret < 0) { out->error = (ret == -1) ? XML_IO_WRITE : -ret; return(-1); } if ((ret == 0) || ((size_t) ret > nbchars)) { out->error = XML_ERR_INTERNAL_ERROR; return(-1); } xmlBufShrink(buf, ret); written += ret; if (out->written > INT_MAX - ret) out->written = INT_MAX; else out->written += ret; } } return(written <= INT_MAX ? written : INT_MAX); } /** * 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. * * @param out a buffered parser output * @param str a zero terminated UTF-8 string * @param escaping an optional escaping function (or NULL) * @returns the number of chars immediately written, or -1 * in case of error. */ int xmlOutputBufferWriteEscape(xmlOutputBuffer *out, const xmlChar *str, xmlCharEncodingOutputFunc escaping) { int ret; int written = 0; size_t len; if ((out == NULL) || (out->error) || (str == NULL)) return(-1); len = strlen((const char *) str); if (len >= INT_MAX) { out->error = XML_ERR_RESOURCE_LIMIT; return(-1); } if (escaping == NULL) { char *escaped = (char *) xmlEscapeText(str, 0); if (escaped == NULL) { out->error = XML_ERR_NO_MEMORY; return(-1); } len = strlen(escaped); if (len >= INT_MAX) { out->error = XML_ERR_RESOURCE_LIMIT; return(-1); } ret = xmlOutputBufferWrite(out, len, escaped); xmlFree(escaped); return(ret); } while (len > 0) { xmlChar buf[1024]; int c_out; int c_in; c_out = 1024; c_in = len; ret = escaping(buf, &c_out, str, &c_in); if (ret < 0) { out->error = XML_ERR_NO_MEMORY; return(-1); } str += c_in; len -= c_in; ret = xmlOutputBufferWrite(out, c_out, (char *) buf); if (ret < 0) return(ret); written += ret; } return(written); } /** * 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. * * @param out a buffered parser output * @param str a zero terminated C string * @returns the number of chars immediately written, or -1 * in case of error. */ int xmlOutputBufferWriteString(xmlOutputBuffer *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); } /** * Write a string surrounded by quotes to an output buffer. * * Uses double quotes if the string contains no double quotes. * Otherwise, uses single quotes if the string contains no * single quotes. Otherwise, uses double quotes and escapes * double quotes. * * This should only be used to escape system IDs. Currently, * we also use it for public IDs and original entity values. * * @param buf output buffer * @param string the string to add */ void xmlOutputBufferWriteQuotedString(xmlOutputBuffer *buf, const xmlChar *string) { const xmlChar *cur, *base; if ((buf == NULL) || (buf->error)) return; if (xmlStrchr(string, '\"')) { if (xmlStrchr(string, '\'')) { xmlOutputBufferWrite(buf, 1, "\""); base = cur = string; while(*cur != 0){ if(*cur == '"'){ if (base != cur) xmlOutputBufferWrite(buf, cur - base, (const char *) base); xmlOutputBufferWrite(buf, 6, """); cur++; base = cur; } else { cur++; } } if (base != cur) xmlOutputBufferWrite(buf, cur - base, (const char *) base); xmlOutputBufferWrite(buf, 1, "\""); } else{ xmlOutputBufferWrite(buf, 1, "'"); xmlOutputBufferWriteString(buf, (const char *) string); xmlOutputBufferWrite(buf, 1, "'"); } } else { xmlOutputBufferWrite(buf, 1, "\""); xmlOutputBufferWriteString(buf, (const char *) string); xmlOutputBufferWrite(buf, 1, "\""); } } /** * flushes the output I/O channel * * @param out a buffered output * @returns the number of byte written or -1 in case of error. */ int xmlOutputBufferFlush(xmlOutputBuffer *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) { out->error = (ret == -1) ? XML_IO_WRITE : -ret; return(ret); } if (out->written > INT_MAX - ret) out->written = INT_MAX; else out->written += ret; return(ret); } #endif /* LIBXML_OUTPUT_ENABLED */ /** * lookup the directory for that file * * @param filename the path to a 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 { ret = xmlMemStrdup("."); } return(ret); #undef IS_XMLPGD_SEP } /** * Like #xmlCheckFilename but handles file URIs. * * @deprecated Internal function, don't use. * * @param filename the path to check * @returns 0, 1, or 2. */ 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 * * * ************************************************************************/ /** * Initialize callback tables. */ void xmlInitIOCallbacks(void) { xmlInputCallbackNr = 1; xmlInputCallbackTable[0].matchcallback = xmlIODefaultMatch; #ifdef LIBXML_OUTPUT_ENABLED xmlOutputCallbackNr = 1; xmlOutputCallbackTable[0].matchcallback = xmlIODefaultMatch; #endif } /** * Register a new set of I/O callback for handling parser input. * * @deprecated Use #xmlCtxtSetResourceLoader or similar functions. * * @param matchFunc the xmlInputMatchCallback * @param openFunc the xmlInputOpenCallback * @param readFunc the xmlInputReadCallback * @param closeFunc the xmlInputCloseCallback * @returns the registered handler number or -1 in case of error */ int xmlRegisterInputCallbacks(xmlInputMatchCallback matchFunc, xmlInputOpenCallback openFunc, xmlInputReadCallback readFunc, xmlInputCloseCallback closeFunc) { xmlInitParser(); 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++); } /** * Registers the default compiled-in I/O handlers. */ void xmlRegisterDefaultInputCallbacks(void) { xmlRegisterInputCallbacks(xmlIODefaultMatch, NULL, NULL, NULL); } /** * 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) { xmlInitParser(); if (xmlInputCallbackNr <= 0) return(-1); xmlInputCallbackNr--; return(xmlInputCallbackNr); } /** * clears the entire input callback table. this includes the * compiled-in I/O. */ void xmlCleanupInputCallbacks(void) { xmlInitParser(); xmlInputCallbackNr = 0; } #ifdef LIBXML_OUTPUT_ENABLED /** * Register a new set of I/O callback for handling output. * * @param matchFunc the xmlOutputMatchCallback * @param openFunc the xmlOutputOpenCallback * @param writeFunc the xmlOutputWriteCallback * @param closeFunc the xmlOutputCloseCallback * @returns the registered handler number or -1 in case of error */ int xmlRegisterOutputCallbacks(xmlOutputMatchCallback matchFunc, xmlOutputOpenCallback openFunc, xmlOutputWriteCallback writeFunc, xmlOutputCloseCallback closeFunc) { xmlInitParser(); 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++); } /** * Registers the default compiled-in I/O handlers. */ void xmlRegisterDefaultOutputCallbacks (void) { xmlRegisterOutputCallbacks(xmlIODefaultMatch, NULL, NULL, NULL); } /** * 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) { xmlInitParser(); if (xmlOutputCallbackNr <= 0) return(-1); xmlOutputCallbackNr--; return(xmlOutputCallbackNr); } /** * clears the entire output callback table. this includes the * compiled-in I/O callbacks. */ void xmlCleanupOutputCallbacks(void) { xmlInitParser(); xmlOutputCallbackNr = 0; } #endif /* LIBXML_OUTPUT_ENABLED */