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