mirror of
https://github.com/GNOME/libxml2.git
synced 2025-02-17 18:19:32 +08:00
* Makefile.am configure.in include/libxml/xmlwin32version.h: preparing for a 2.4.3 release even if it may not be ready yet * catalog.c parser.c xmlIO.c include/libxml/catalog.h: redirected all file parsing lookup to go through the entity resolver, add to add an API to bypass it (needed to load catalogs themselves), some cleanup on the catalog code too. * nanoftp.c: small cleanup * doc/catalog.html: small update Daniel
2524 lines
61 KiB
C
2524 lines
61 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
|
|
*/
|
|
|
|
#include "libxml.h"
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#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
|
|
|
|
/* 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
|
|
#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
|
|
|
|
#ifdef VMS
|
|
#define xmlRegisterDefaultInputCallbacks xmlRegisterDefInputCallbacks
|
|
#define xmlRegisterDefaultOutputCallbacks xmlRegisterDefOutputCallbacks
|
|
#endif
|
|
|
|
/* #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;
|
|
|
|
/*
|
|
* 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;
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Standard I/O for file accesses *
|
|
* *
|
|
************************************************************************/
|
|
|
|
/**
|
|
* 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,
|
|
* sets errno to EISDIR and returns 0. otherwise
|
|
* returns 1.
|
|
*/
|
|
|
|
static int
|
|
xmlCheckFilename (const char *path)
|
|
{
|
|
#ifdef HAVE_STAT
|
|
#ifdef S_ISDIR
|
|
struct stat stat_buffer;
|
|
|
|
if (stat(path, &stat_buffer) == -1)
|
|
return 0;
|
|
|
|
if (S_ISDIR(stat_buffer.st_mode)) {
|
|
errno = EISDIR;
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
#endif
|
|
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) {
|
|
return(read((int) (long) context, &buffer[0], len));
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
return(write((int) (long) context, &buffer[0], len));
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
return ( close((int) (long) context) );
|
|
}
|
|
|
|
/**
|
|
* xmlFileMatch:
|
|
* @filename: the URI for matching
|
|
*
|
|
* input from FILE *
|
|
*
|
|
* Returns 1 if matches, 0 otherwise
|
|
*/
|
|
static int
|
|
xmlFileMatch (const char *filename ATTRIBUTE_UNUSED) {
|
|
return(1);
|
|
}
|
|
|
|
/**
|
|
* xmlFileOpen:
|
|
* @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 (const char *filename) {
|
|
const char *path = NULL;
|
|
FILE *fd;
|
|
|
|
if (!strcmp(filename, "-")) {
|
|
fd = stdin;
|
|
return((void *) fd);
|
|
}
|
|
|
|
if (!strncmp(filename, "file://localhost", 16))
|
|
path = &filename[16];
|
|
else if (!strncmp(filename, "file:///", 8)) {
|
|
#ifdef _WIN32
|
|
path = &filename[8];
|
|
#else
|
|
path = &filename[7];
|
|
#endif
|
|
} else
|
|
path = filename;
|
|
|
|
if (path == NULL)
|
|
return(NULL);
|
|
if (!xmlCheckFilename(path))
|
|
return(NULL);
|
|
|
|
#ifdef WIN32
|
|
fd = fopen(path, "rb");
|
|
#else
|
|
fd = fopen(path, "r");
|
|
#endif /* WIN32 */
|
|
return((void *) fd);
|
|
}
|
|
|
|
/**
|
|
* 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 (!strncmp(filename, "file://localhost", 16))
|
|
path = &filename[16];
|
|
else if (!strncmp(filename, "file:///", 8)) {
|
|
#ifdef _WIN32
|
|
path = &filename[8];
|
|
#else
|
|
path = &filename[7];
|
|
#endif
|
|
} else
|
|
path = filename;
|
|
|
|
if (path == NULL)
|
|
return(NULL);
|
|
|
|
fd = fopen(path, "w");
|
|
return((void *) fd);
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
static int
|
|
xmlFileRead (void * context, char * buffer, int len) {
|
|
return(fread(&buffer[0], 1, len, (FILE *) context));
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
return(fwrite(&buffer[0], 1, len, (FILE *) context));
|
|
}
|
|
|
|
/**
|
|
* xmlFileClose:
|
|
* @context: the I/O context
|
|
*
|
|
* Close an I/O channel
|
|
*/
|
|
static int
|
|
xmlFileClose (void * context) {
|
|
return ( ( fclose((FILE *) context) == EOF ) ? -1 : 0 );
|
|
}
|
|
|
|
/**
|
|
* xmlFileFlush:
|
|
* @context: the I/O context
|
|
*
|
|
* Flush an I/O channel
|
|
*/
|
|
static int
|
|
xmlFileFlush (void * context) {
|
|
return ( ( fflush((FILE *) context) == EOF ) ? -1 : 0 );
|
|
}
|
|
|
|
#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:
|
|
* @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 (const char *filename) {
|
|
const char *path = NULL;
|
|
gzFile fd;
|
|
|
|
if (!strcmp(filename, "-")) {
|
|
fd = gzdopen(fileno(stdin), "rb");
|
|
return((void *) fd);
|
|
}
|
|
|
|
if (!strncmp(filename, "file://localhost", 16))
|
|
path = &filename[16];
|
|
else if (!strncmp(filename, "file:///", 8)) {
|
|
#ifdef _WIN32
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
sprintf(mode, "wb%d", compression);
|
|
if (!strcmp(filename, "-")) {
|
|
fd = gzdopen(1, mode);
|
|
return((void *) fd);
|
|
}
|
|
|
|
if (!strncmp(filename, "file://localhost", 16))
|
|
path = &filename[16];
|
|
else if (!strncmp(filename, "file:///", 8)) {
|
|
#ifdef _WIN32
|
|
path = &filename[8];
|
|
#else
|
|
path = &filename[7];
|
|
#endif
|
|
} else
|
|
path = filename;
|
|
|
|
if (path == NULL)
|
|
return(NULL);
|
|
|
|
fd = gzopen(path, mode);
|
|
return((void *) fd);
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
return(gzread((gzFile) context, &buffer[0], len));
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
return(gzwrite((gzFile) context, (char *) &buffer[0], len));
|
|
}
|
|
|
|
/**
|
|
* xmlGzfileClose:
|
|
* @context: the I/O context
|
|
*
|
|
* Close a compressed I/O channel
|
|
*/
|
|
static int
|
|
xmlGzfileClose (void * context) {
|
|
return ( ( gzclose((gzFile) context) == Z_OK ) ? 0 : -1 );
|
|
}
|
|
#endif /* HAVE_ZLIB_H */
|
|
|
|
#ifdef LIBXML_HTTP_ENABLED
|
|
/************************************************************************
|
|
* *
|
|
* I/O for HTTP file accesses *
|
|
* *
|
|
************************************************************************/
|
|
|
|
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 ) {
|
|
|
|
int z_err;
|
|
|
|
if ( buff == NULL )
|
|
return;
|
|
|
|
xmlFree( buff->zbuff );
|
|
z_err = deflateEnd( &buff->zctrl );
|
|
#ifdef DEBUG_HTTP
|
|
if ( z_err != Z_OK )
|
|
xmlGenericError( xmlGenericErrorContext,
|
|
"xmlFreeZMemBuff: Error releasing zlib context: %d\n",
|
|
z_err );
|
|
#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 ) {
|
|
xmlGenericError( xmlGenericErrorContext,
|
|
"xmlCreateZMemBuff: %s\n",
|
|
"Failure allocating 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 );
|
|
xmlGenericError( xmlGenericErrorContext,
|
|
"xmlCreateZMemBuff: %s\n",
|
|
"Failure allocating data buffer." );
|
|
return ( NULL );
|
|
}
|
|
|
|
z_err = deflateInit2( &buff->zctrl, compression, Z_DEFLATED,
|
|
DFLT_WBITS, DFLT_MEM_LVL, Z_DEFAULT_STRATEGY );
|
|
if ( z_err != Z_OK ) {
|
|
xmlFreeZMemBuff( buff );
|
|
buff = NULL;
|
|
xmlGenericError( xmlGenericErrorContext,
|
|
"xmlCreateZMemBuff: %s %d\n",
|
|
"Error initializing compression context. ZLIB error:",
|
|
z_err );
|
|
return ( NULL );
|
|
}
|
|
|
|
/* Set the header data. The CRC will be needed for the trailer */
|
|
|
|
buff->crc = crc32( 0L, Z_NULL, 0 );
|
|
hdr_lgth = sprintf( (char *)buff->zbuff, "%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 {
|
|
xmlGenericError( xmlGenericErrorContext,
|
|
"xmlZMemBuffExtend: %s %lu bytes.\n",
|
|
"Allocation failure extending output buffer to",
|
|
new_size );
|
|
}
|
|
|
|
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 ) {
|
|
xmlGenericError( xmlGenericErrorContext,
|
|
"xmlZMemBuffAppend: %s %d %s - %d",
|
|
"Compression error while appending",
|
|
len, "bytes to buffer. ZLIB error", z_err );
|
|
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
|
|
xmlGenericError( xmlGenericErrorContext,
|
|
"xmlZMemBuffGetContent: %s - %d\n",
|
|
"Error flushing zlib buffers. Error code", z_err );
|
|
|
|
return ( zlgth );
|
|
}
|
|
#endif /* HAVE_ZLIB_H */
|
|
|
|
/**
|
|
* xmlFreeHTTPWriteCtxt
|
|
* @ctxt: Context to cleanup
|
|
*
|
|
* Free allocated memory and reclaim system resources.
|
|
*
|
|
* No return value.
|
|
*/
|
|
static void
|
|
xmlFreeHTTPWriteCtxt( xmlIOHTTPWriteCtxtPtr ctxt )
|
|
{
|
|
if ( ctxt->uri != NULL )
|
|
free( 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 );
|
|
}
|
|
}
|
|
|
|
free( ctxt );
|
|
return;
|
|
}
|
|
|
|
|
|
/**
|
|
* xmlIOHTTPMatch:
|
|
* @filename: the URI for matching
|
|
*
|
|
* check if the URI matches an HTTP one
|
|
*
|
|
* Returns 1 if matches, 0 otherwise
|
|
*/
|
|
static int
|
|
xmlIOHTTPMatch (const char *filename) {
|
|
if (!strncmp(filename, "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
|
|
*/
|
|
static void *
|
|
xmlIOHTTPOpen (const char *filename) {
|
|
return(xmlNanoHTTPOpen(filename, NULL));
|
|
}
|
|
|
|
/**
|
|
* 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 ) {
|
|
xmlGenericError( xmlGenericErrorContext,
|
|
"xmlIOHTTPOpenW: Failed to create output HTTP context.\n" );
|
|
return ( NULL );
|
|
}
|
|
|
|
(void)memset( ctxt, 0, sizeof( xmlIOHTTPWriteCtxt ) );
|
|
|
|
ctxt->uri = strdup( post_uri );
|
|
if ( ctxt->uri == NULL ) {
|
|
xmlGenericError( xmlGenericErrorContext,
|
|
"xmlIOHTTPOpenW: Failed to duplicate destination URI.\n" );
|
|
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 );
|
|
}
|
|
|
|
/**
|
|
* 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 ) );
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
static int
|
|
xmlIOHTTPRead(void * context, char * buffer, int len) {
|
|
return(xmlNanoHTTPRead(context, &buffer[0], len));
|
|
}
|
|
|
|
/**
|
|
* 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 ) {
|
|
xmlGenericError( xmlGenericErrorContext,
|
|
"xmlIOHTTPWrite: %s\n%s '%s'.\n",
|
|
"Error appending to internal buffer.",
|
|
"Error sending document to URI",
|
|
ctxt->uri );
|
|
}
|
|
}
|
|
|
|
return ( len );
|
|
}
|
|
|
|
|
|
/**
|
|
* xmlIOHTTPClose:
|
|
* @context: the I/O context
|
|
*
|
|
* Close an HTTP I/O channel
|
|
*/
|
|
static int
|
|
xmlIOHTTPClose (void * context) {
|
|
xmlNanoHTTPClose(context);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* 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 ) {
|
|
xmlGenericError( xmlGenericErrorContext,
|
|
"xmlIOHTTPCloseWrite: %s '%s' %s '%s'.\n",
|
|
"Error retrieving content.\nUnable to",
|
|
http_mthd, "data to URI", ctxt->uri );
|
|
}
|
|
|
|
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)sprintf( buffer, "%s.content", dump_name );
|
|
|
|
tst_file = fopen( buffer, "w" );
|
|
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)sprintf( buffer, "%s.reply", dump_name );
|
|
tst_file = fopen( buffer, "w" );
|
|
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
|
|
xmlGenericError( xmlGenericErrorContext,
|
|
"xmlIOHTTPClose: 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 );
|
|
|
|
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_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
|
|
*/
|
|
static int
|
|
xmlIOFTPMatch (const char *filename) {
|
|
if (!strncmp(filename, "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
|
|
*/
|
|
static 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
|
|
*/
|
|
static int
|
|
xmlIOFTPRead(void * context, char * buffer, int len) {
|
|
return(xmlNanoFTPRead(context, &buffer[0], len));
|
|
}
|
|
|
|
/**
|
|
* xmlIOFTPClose:
|
|
* @context: the I/O context
|
|
*
|
|
* Close an FTP I/O channel
|
|
*/
|
|
static 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;
|
|
return(xmlInputCallbackNr++);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
return(xmlOutputCallbackNr++);
|
|
}
|
|
|
|
/**
|
|
* xmlRegisterDefaultInputCallbacks:
|
|
*
|
|
* Registers the default compiled-in I/O handlers.
|
|
*/
|
|
void
|
|
#ifdef VMS
|
|
xmlRegisterDefInputCallbacks
|
|
#else
|
|
xmlRegisterDefaultInputCallbacks
|
|
#endif
|
|
(void) {
|
|
if (xmlInputCallbackInitialized)
|
|
return;
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* xmlRegisterDefaultOutputCallbacks:
|
|
*
|
|
* Registers the default compiled-in I/O handlers.
|
|
*/
|
|
void
|
|
#ifdef VMS
|
|
xmlRegisterDefOutputCallbacks
|
|
#else
|
|
xmlRegisterDefaultOutputCallbacks
|
|
#endif
|
|
(void) {
|
|
if (xmlOutputCallbackInitialized)
|
|
return;
|
|
|
|
xmlRegisterOutputCallbacks(xmlFileMatch, xmlFileOpenW,
|
|
xmlFileWrite, xmlFileClose);
|
|
|
|
#ifdef LIBXML_HTTP_ENABLED
|
|
xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
|
|
xmlIOHTTPWrite, xmlIOHTTPClosePut);
|
|
#endif
|
|
|
|
/*********************************
|
|
No way a-priori to distinguish between gzipped files from
|
|
uncompressed ones except opening if existing then closing
|
|
and saving with same compression ratio ... a pain.
|
|
|
|
#ifdef 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
|
|
|
|
/**
|
|
* 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) {
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"xmlAllocParserInputBuffer : out of memory!\n");
|
|
return(NULL);
|
|
}
|
|
memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer));
|
|
ret->buffer = xmlBufferCreate();
|
|
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 = xmlBufferCreate();
|
|
else
|
|
ret->raw = NULL;
|
|
ret->readcallback = NULL;
|
|
ret->closecallback = NULL;
|
|
ret->context = NULL;
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"xmlAllocOutputBuffer : out of memory!\n");
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* xmlFreeParserInputBuffer:
|
|
* @in: a buffered parser input
|
|
*
|
|
* Free up the memory used by a buffered parser input
|
|
*/
|
|
void
|
|
xmlFreeParserInputBuffer(xmlParserInputBufferPtr in) {
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
xmlFree(out);
|
|
return( ( err_rc == 0 ) ? written : err_rc );
|
|
}
|
|
|
|
/**
|
|
* xmlParserInputBufferCreateFname:
|
|
* @URI: a C string containing the URI or filename
|
|
* @enc: the charset encoding if known
|
|
*
|
|
* VMS version of xmlParserInputBufferCreateFilename()
|
|
*
|
|
* Returns the new parser input or NULL
|
|
*/
|
|
/**
|
|
* 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
|
|
#ifdef VMS
|
|
xmlParserInputBufferCreateFname
|
|
#else
|
|
xmlParserInputBufferCreateFilename
|
|
#endif
|
|
(const char *URI, xmlCharEncoding enc) {
|
|
xmlParserInputBufferPtr ret;
|
|
int i = 0;
|
|
void *context = NULL;
|
|
char *unescaped;
|
|
|
|
if (xmlInputCallbackInitialized == 0)
|
|
xmlRegisterDefaultInputCallbacks();
|
|
|
|
if (URI == NULL) return(NULL);
|
|
|
|
#ifdef LIBXML_CATALOG_ENABLED
|
|
#endif
|
|
|
|
/*
|
|
* Try to find one of the input accept method accepting taht scheme
|
|
* Go in reverse to give precedence to user defined handlers.
|
|
* try with an unescaped version of the URI
|
|
*/
|
|
unescaped = xmlURIUnescapeString(URI, 0, NULL);
|
|
if (unescaped != NULL) {
|
|
for (i = xmlInputCallbackNr - 1;i >= 0;i--) {
|
|
if ((xmlInputCallbackTable[i].matchcallback != NULL) &&
|
|
(xmlInputCallbackTable[i].matchcallback(unescaped) != 0)) {
|
|
context = xmlInputCallbackTable[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) {
|
|
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;
|
|
}
|
|
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) {
|
|
xmlOutputBufferPtr ret;
|
|
int i = 0;
|
|
void *context = NULL;
|
|
char *unescaped;
|
|
|
|
int is_http_uri = 0; /* Can't change if HTTP disabled */
|
|
|
|
if (xmlOutputCallbackInitialized == 0)
|
|
xmlRegisterDefaultOutputCallbacks();
|
|
|
|
if (URI == NULL) return(NULL);
|
|
|
|
#ifdef LIBXML_HTTP_ENABLED
|
|
/* Need to prevent HTTP URI's from falling into zlib short circuit */
|
|
|
|
is_http_uri = xmlIOHTTPMatch( URI );
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Try to find one of the output accept method accepting taht scheme
|
|
* Go in reverse to give precedence to user defined handlers.
|
|
* try with an unescaped version of the URI
|
|
*/
|
|
unescaped = xmlURIUnescapeString(URI, 0, NULL);
|
|
if (unescaped != NULL) {
|
|
#ifdef HAVE_ZLIB_H
|
|
if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) {
|
|
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_http_uri == 0)) {
|
|
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)) {
|
|
context = xmlOutputCallbackTable[i].opencallback(URI);
|
|
#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);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
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;
|
|
xmlBufferAdd(ret->buffer, (const xmlChar *) mem, size);
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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 = xmlFdClose;
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* xmlOutputBufferCreateIO:
|
|
* @iowrite: an I/O write function
|
|
* @ioclose: an I/O close function
|
|
* @ioctx: an I/O handler
|
|
* @enc: 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);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
if (len < 0) return(0);
|
|
if (in->encoder != NULL) {
|
|
/*
|
|
* Store the data in the incoming raw buffer
|
|
*/
|
|
if (in->raw == NULL) {
|
|
in->raw = xmlBufferCreate();
|
|
}
|
|
xmlBufferAdd(in->raw, (const xmlChar *) buf, len);
|
|
|
|
/*
|
|
* convert as much as possible to the parser reading buffer.
|
|
*/
|
|
nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
|
|
if (nbchars < 0) {
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"xmlParserInputBufferPush: encoder error\n");
|
|
return(-1);
|
|
}
|
|
} else {
|
|
nbchars = len;
|
|
xmlBufferAdd(in->buffer, (xmlChar *) buf, nbchars);
|
|
}
|
|
#ifdef DEBUG_INPUT
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"I/O: pushed %d chars, buffer %d/%d\n",
|
|
nbchars, in->buffer->use, in->buffer->size);
|
|
#endif
|
|
return(nbchars);
|
|
}
|
|
|
|
/**
|
|
* 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 directy
|
|
* 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;
|
|
|
|
if ((len <= MINLEN) && (len != 4))
|
|
len = MINLEN;
|
|
buffree = in->buffer->size - in->buffer->use;
|
|
if (buffree <= 0) {
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"xmlParserInputBufferGrow : buffer full !\n");
|
|
return(0);
|
|
}
|
|
if (len > buffree)
|
|
len = buffree;
|
|
|
|
buffer = (char *) xmlMalloc((len + 1) * sizeof(char));
|
|
if (buffer == NULL) {
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"xmlParserInputBufferGrow : out of memory !\n");
|
|
return(-1);
|
|
}
|
|
|
|
/*
|
|
* Call the read method for this I/O type.
|
|
*/
|
|
if (in->readcallback != NULL) {
|
|
res = in->readcallback(in->context, &buffer[0], len);
|
|
} else {
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"xmlParserInputBufferGrow : no input !\n");
|
|
xmlFree(buffer);
|
|
return(-1);
|
|
}
|
|
if (res < 0) {
|
|
perror ("read error");
|
|
xmlFree(buffer);
|
|
return(-1);
|
|
}
|
|
len = res;
|
|
if (in->encoder != NULL) {
|
|
/*
|
|
* Store the data in the incoming raw buffer
|
|
*/
|
|
if (in->raw == NULL) {
|
|
in->raw = xmlBufferCreate();
|
|
}
|
|
xmlBufferAdd(in->raw, (const xmlChar *) buffer, len);
|
|
|
|
/*
|
|
* convert as much as possible to the parser reading buffer.
|
|
*/
|
|
nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
|
|
if (nbchars < 0) {
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"xmlParserInputBufferGrow: encoder error\n");
|
|
return(-1);
|
|
}
|
|
} else {
|
|
nbchars = len;
|
|
buffer[nbchars] = 0;
|
|
xmlBufferAdd(in->buffer, (xmlChar *) buffer, nbchars);
|
|
}
|
|
#ifdef DEBUG_INPUT
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"I/O: read %d chars, buffer %d/%d\n",
|
|
nbchars, in->buffer->use, in->buffer->size);
|
|
#endif
|
|
xmlFree(buffer);
|
|
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) {
|
|
/* xmlBufferEmpty(in->buffer); */
|
|
if (in->readcallback != NULL)
|
|
return(xmlParserInputBufferGrow(in, len));
|
|
else
|
|
return(-1);
|
|
}
|
|
|
|
/**
|
|
* 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 (len < 0) return(0);
|
|
|
|
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();
|
|
}
|
|
xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
|
|
|
|
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) {
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"xmlOutputBufferWrite: encoder error\n");
|
|
return(-1);
|
|
}
|
|
nbchars = out->conv->use;
|
|
} else {
|
|
xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
|
|
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) {
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"I/O: error %d writing %d bytes\n", ret, nbchars);
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* 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 (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;
|
|
|
|
/*
|
|
* 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) {
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"xmlOutputBufferWrite: encoder error\n");
|
|
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) {
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"I/O: error %d flushing %d bytes\n", ret, nbchars);
|
|
return(ret);
|
|
}
|
|
out->written += ret;
|
|
|
|
#ifdef DEBUG_INPUT
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"I/O: flushed %d chars\n", ret);
|
|
#endif
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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 = '/';
|
|
|
|
if (xmlInputCallbackInitialized == 0)
|
|
xmlRegisterDefaultInputCallbacks();
|
|
|
|
if (filename == NULL) return(NULL);
|
|
#ifdef WIN32
|
|
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 *
|
|
* *
|
|
****************************************************************/
|
|
|
|
/**
|
|
* 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
|
|
#ifdef HAVE_STAT
|
|
struct stat info;
|
|
#endif
|
|
xmlCatalogAllow pref;
|
|
#endif
|
|
|
|
#ifdef DEBUG_EXTERNAL_ENTITIES
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"xmlDefaultExternalEntityLoader(%s, xxx)\n", URL);
|
|
#endif
|
|
#ifdef LIBXML_CATALOG_ENABLED
|
|
/*
|
|
* 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)
|
|
#ifdef HAVE_STAT
|
|
&& ((URL == NULL) || (stat(URL, &info) < 0))
|
|
#endif
|
|
) {
|
|
/*
|
|
* Do a local lookup
|
|
*/
|
|
if ((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)
|
|
#ifdef HAVE_STAT
|
|
&& (stat((const char *) resource, &info) < 0)
|
|
#endif
|
|
) {
|
|
xmlChar *tmp = NULL;
|
|
|
|
if ((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 ((ctxt->validate) && (ctxt->sax != NULL) &&
|
|
(ctxt->sax->error != NULL))
|
|
ctxt->sax->error(ctxt,
|
|
"failed to load external entity \"%s\"\n", ID);
|
|
else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
|
|
ctxt->sax->warning(ctxt,
|
|
"failed to load external entity \"%s\"\n", ID);
|
|
return(NULL);
|
|
}
|
|
ret = xmlNewInputFromFile(ctxt, (const char *)resource);
|
|
if (ret == NULL) {
|
|
if ((ctxt->validate) && (ctxt->sax != NULL) &&
|
|
(ctxt->sax->error != NULL))
|
|
ctxt->sax->error(ctxt,
|
|
"failed to load external entity \"%s\"\n", resource);
|
|
else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
|
|
ctxt->sax->warning(ctxt,
|
|
"failed to load external entity \"%s\"\n", 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
|
|
* TODO: a more generic External entitiy API must be designed
|
|
*
|
|
* Returns the xmlParserInputPtr or NULL
|
|
*/
|
|
xmlParserInputPtr
|
|
xmlLoadExternalEntity(const char *URL, const char *ID,
|
|
xmlParserCtxtPtr ctxt) {
|
|
return(xmlCurrentExternalEntityLoader(URL, ID, ctxt));
|
|
}
|
|
|