libxml-libxml-perl/perl-libxml-sax.c

1686 lines
38 KiB
C

/**
* perl-libxml-sax.c
* $Id$
*
* This is free software, you may use it and distribute it under the same terms as
* Perl itself.
*
* Copyright 2001-2003 AxKit.com Ltd., 2002-2006 Christian Glahn, 2006-2009 Petr Pajas
*/
#ifdef __cplusplus
extern "C" {
#endif
/* Disable this and use a threaded perl to test MSVC compilation errors */
#define PERL_NO_GET_CONTEXT /* we want efficiency */
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include <stdlib.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/parserInternals.h>
#include <libxml/tree.h>
#include <libxml/entities.h>
#include <libxml/xmlerror.h>
#include "perl-libxml-sax.h"
#ifdef __cplusplus
}
#endif
/*
we must call CLEAR_SERROR_HANDLER upon each excurse from
perl
*/
#define WITH_SERRORS
#ifdef WITH_SERRORS
#define CLEAR_SERROR_HANDLER /*xmlSetStructuredErrorFunc(NULL,NULL);*/
#else
#define CLEAR_SERROR_HANDLER
#endif
#define NSDELIM ':'
/* #define NSDEFAULTURI "http://www.w3.org/XML/1998/namespace" */
#define NSDEFAULTURI "http://www.w3.org/2000/xmlns/"
typedef struct {
SV * parser;
xmlNodePtr ns_stack;
HV * locator;
xmlDocPtr ns_stack_root;
SV * handler;
SV * saved_error;
struct CBuffer *charbuf;
int joinchars;
} PmmSAXVector;
typedef PmmSAXVector* PmmSAXVectorPtr;
struct CBufferChunk {
struct CBufferChunk *next;
xmlChar *data;
int len;
};
struct CBuffer {
struct CBufferChunk *head;
struct CBufferChunk *tail;
};
static U32 PrefixHash; /* pre-computed */
static U32 NsURIHash;
static U32 NameHash;
static U32 LocalNameHash;
static U32 AttributesHash;
static U32 ValueHash;
static U32 DataHash;
static U32 TargetHash;
static U32 VersionHash;
static U32 EncodingHash;
static U32 PublicIdHash;
static U32 SystemIdHash;
/* helper function C2Sv is ment to work faster than the perl-libxml-mm
version. this shortcut is useful, because SAX handles only UTF8
strings, so there is no conversion logic required.
*/
SV*
_C2Sv( const xmlChar *string, const xmlChar *dummy )
{
dTHX;
SV *retval = &PL_sv_undef;
STRLEN len;
if ( string != NULL ) {
len = xmlStrlen( string );
retval = NEWSV(0, len+1);
sv_setpvn(retval, (const char *)string, len );
#ifdef HAVE_UTF8
SvUTF8_on( retval );
#endif
}
return retval;
}
SV*
_C2Sv_len( const xmlChar *string, int len )
{
dTHX;
SV *retval = &PL_sv_undef;
if ( string != NULL ) {
retval = NEWSV(0, len+1);
sv_setpvn(retval, (const char *)string, (STRLEN) len );
#ifdef HAVE_UTF8
SvUTF8_on( retval );
#endif
}
return retval;
}
void
PmmSAXInitialize(pTHX)
{
PERL_HASH(PrefixHash, "Prefix", 6);
PERL_HASH(NsURIHash, "NamespaceURI", 12);
PERL_HASH(NameHash, "Name", 4);
PERL_HASH(LocalNameHash, "LocalName", 9);
PERL_HASH(AttributesHash, "Attributes", 10);
PERL_HASH(ValueHash, "Value", 5);
PERL_HASH(DataHash, "Data", 4);
PERL_HASH(TargetHash, "Target", 6);
PERL_HASH(VersionHash, "Version", 7);
PERL_HASH(EncodingHash, "Encoding", 8);
PERL_HASH(PublicIdHash, "PublicId", 8);
PERL_HASH(SystemIdHash, "SystemId", 8);
}
xmlSAXHandlerPtr PSaxGetHandler();
int PSaxCharactersFlush(void *, struct CBuffer *);
/* Character buffering functions */
struct CBufferChunk * CBufferChunkNew(void) {
struct CBufferChunk *newchunk = xmlMalloc(sizeof(struct CBufferChunk));
memset(newchunk, 0, sizeof(struct CBufferChunk));
return newchunk;
}
struct CBuffer * CBufferNew(void) {
struct CBuffer *new = xmlMalloc(sizeof(struct CBuffer));
struct CBufferChunk *newchunk = CBufferChunkNew();
memset(new, 0, sizeof(struct CBuffer));
new->head = newchunk;
new->tail = newchunk;
return new;
}
void CBufferPurge(struct CBuffer *buffer) {
struct CBufferChunk *p1;
struct CBufferChunk *p2;
if (buffer == NULL || buffer->head->data == NULL) {
return;
}
if ((p1 = buffer->head)) {
while(p1) {
p2 = p1->next;
if (p1->data) {
xmlFree(p1->data);
}
xmlFree(p1);
p1 = p2;
}
}
buffer->head = CBufferChunkNew();
buffer->tail = buffer->head;
}
void CBufferFree(struct CBuffer *buffer) {
struct CBufferChunk *p1;
struct CBufferChunk *p2;
if (buffer == NULL) {
return;
}
if ((p1 = buffer->head)) {
while(p1) {
p2 = p1->next;
if (p1->data) {
xmlFree(p1->data);
}
xmlFree(p1);
p1 = p2;
}
}
xmlFree(buffer);
return;
}
int CBufferLength(struct CBuffer *buffer) {
int length = 0;
struct CBufferChunk *cur;
for(cur = buffer->head; cur; cur = cur->next) {
length += cur->len;
}
return length;
}
void CBufferAppend(struct CBuffer *buffer, const xmlChar *newstring, int len) {
xmlChar *copy = xmlMalloc(len);
memcpy(copy, newstring, len);
buffer->tail->data = copy;
buffer->tail->len = len;
buffer->tail->next = CBufferChunkNew();
buffer->tail = buffer->tail->next;
}
xmlChar * CBufferCharacters(struct CBuffer *buffer) {
int length = CBufferLength(buffer);
xmlChar *new = xmlMalloc(length + 1);
xmlChar *p = new;
int copied = 0;
struct CBufferChunk *cur;
/* We need this because stderr on some perls requires
* my_perl. See:
*
* https://rt.cpan.org/Public/Bug/Display.html?id=69082
*
* */
dTHX;
if (buffer->head->data == NULL) {
return NULL;
}
for(cur = buffer->head;cur;cur = cur->next) {
if (! cur->data) {
continue;
}
if ((copied = copied + cur->len) > length) {
fprintf(stderr, "string overflow\n");
abort();
}
memcpy(p, cur->data, cur->len);
p += cur->len;
}
new[length] = '\0';
return new;
}
/* end character buffering functions */
void
PmmSAXInitContext( xmlParserCtxtPtr ctxt, SV * parser, SV * saved_error )
{
PmmSAXVectorPtr vec = NULL;
SV ** th;
SV ** joinchars;
dTHX;
CLEAR_SERROR_HANDLER
vec = (PmmSAXVector*) xmlMalloc( sizeof(PmmSAXVector) );
vec->ns_stack_root = xmlNewDoc(NULL);
vec->ns_stack = xmlNewDocNode(vec->ns_stack_root,
NULL,
(const xmlChar*)"stack",
NULL );
xmlAddChild((xmlNodePtr)vec->ns_stack_root, vec->ns_stack);
vec->locator = NULL;
vec->saved_error = saved_error;
vec->parser = SvREFCNT_inc( parser );
th = hv_fetch( (HV*)SvRV(parser), "HANDLER", 7, 0 );
if ( th != NULL && SvTRUE(*th) ) {
vec->handler = SvREFCNT_inc(*th) ;
}
else {
vec->handler = NULL;
}
joinchars = hv_fetch((HV*)SvRV(parser), "JOIN_CHARACTERS", 15, 0);
if (joinchars != NULL) {
vec->joinchars = (SvIV(*joinchars));
} else {
vec->joinchars = 0;
}
if (vec->joinchars) {
vec->charbuf = CBufferNew();
} else {
vec->charbuf = NULL;
}
if ( ctxt->sax ) {
xmlFree( ctxt->sax );
}
ctxt->sax = PSaxGetHandler();
ctxt->_private = (void*)vec;
}
void
PmmSAXCloseContext( xmlParserCtxtPtr ctxt )
{
PmmSAXVector * vec = (PmmSAXVectorPtr) ctxt->_private;
dTHX;
if ( vec->handler != NULL ) {
SvREFCNT_dec( vec->handler );
vec->handler = NULL;
}
CBufferFree(vec->charbuf);
vec->charbuf = NULL;
xmlFree( ctxt->sax );
ctxt->sax = NULL;
SvREFCNT_dec( vec->parser );
vec->parser = NULL;
xmlFreeDoc( vec->ns_stack_root );
vec->ns_stack_root = NULL;
if ( vec->locator != NULL ) {
SvREFCNT_dec( vec->locator );
vec->locator = NULL;
}
xmlFree( vec );
ctxt->_private = NULL;
}
xmlNsPtr
PmmGetNsMapping( xmlNodePtr ns_stack, const xmlChar * prefix )
{
if ( ns_stack != NULL ) {
return xmlSearchNs( ns_stack->doc, ns_stack, prefix );
}
return NULL;
}
void
PSaxStartPrefix( PmmSAXVectorPtr sax, const xmlChar * prefix,
const xmlChar * uri, SV * handler )
{
dTHX;
HV * param;
SV * rv;
dSP;
ENTER;
SAVETMPS;
param = newHV();
(void) hv_store(param, "NamespaceURI", 12,
_C2Sv(uri, NULL), NsURIHash);
if ( prefix != NULL ) {
(void) hv_store(param, "Prefix", 6,
_C2Sv(prefix, NULL), PrefixHash);
}
else {
(void) hv_store(param, "Prefix", 6,
_C2Sv((const xmlChar*)"", NULL), PrefixHash);
}
PUSHMARK(SP) ;
XPUSHs(handler);
rv = newRV_noinc((SV*)param);
XPUSHs(rv);
PUTBACK;
call_method( "start_prefix_mapping", G_SCALAR | G_EVAL | G_DISCARD );
sv_2mortal(rv);
if (SvTRUE(ERRSV)) {
croak_obj;
}
FREETMPS ;
LEAVE ;
CLEAR_SERROR_HANDLER
}
void
PSaxEndPrefix( PmmSAXVectorPtr sax, const xmlChar * prefix,
const xmlChar * uri, SV * handler )
{
dTHX;
HV * param;
SV * rv;
dSP;
ENTER;
SAVETMPS;
param = newHV();
(void) hv_store(param, "NamespaceURI", 12,
_C2Sv(uri, NULL), NsURIHash);
if ( prefix != NULL ) {
(void) hv_store(param, "Prefix", 6,
_C2Sv(prefix, NULL), PrefixHash);
}
else {
(void) hv_store(param, "Prefix", 6,
_C2Sv((const xmlChar *)"", NULL), PrefixHash);
}
PUSHMARK(SP) ;
XPUSHs(handler);
rv = newRV_noinc((SV*)param);
XPUSHs(rv);
PUTBACK;
call_method( "end_prefix_mapping", G_SCALAR | G_EVAL | G_DISCARD );
sv_2mortal(rv);
if (SvTRUE(ERRSV)) {
croak_obj;
}
FREETMPS ;
LEAVE ;
CLEAR_SERROR_HANDLER
}
void
PmmExtendNsStack( PmmSAXVectorPtr sax , const xmlChar * name) {
xmlNodePtr newNS = NULL;
xmlChar * localname = NULL;
xmlChar * prefix = NULL;
localname = xmlSplitQName( NULL, name, &prefix );
if ( prefix != NULL ) {
/* check if we can find a namespace with that prefix... */
xmlNsPtr ns = xmlSearchNs( sax->ns_stack->doc, sax->ns_stack, prefix );
if ( ns != NULL ) {
newNS = xmlNewDocNode( sax->ns_stack_root, ns, localname, NULL );
}
else {
newNS = xmlNewDocNode( sax->ns_stack_root, NULL, name, NULL );
}
}
else {
newNS = xmlNewDocNode( sax->ns_stack_root, NULL, name, NULL );
}
if ( newNS != NULL ) {
xmlAddChild(sax->ns_stack, newNS);
sax->ns_stack = newNS;
}
if ( localname != NULL ) {
xmlFree( localname ) ;
}
if ( prefix != NULL ) {
xmlFree( prefix );
}
}
void
PmmNarrowNsStack( PmmSAXVectorPtr sax, SV *handler )
{
xmlNodePtr parent = sax->ns_stack->parent;
xmlNsPtr list = sax->ns_stack->nsDef;
while ( list ) {
if ( !xmlStrEqual(list->prefix, (const xmlChar*)"xml") ) {
PSaxEndPrefix( sax, list->prefix, list->href, handler );
}
list = list->next;
}
xmlUnlinkNode(sax->ns_stack);
xmlFreeNode(sax->ns_stack);
sax->ns_stack = parent;
}
void
PmmAddNamespace( PmmSAXVectorPtr sax, const xmlChar * name,
const xmlChar * href, SV *handler)
{
xmlNsPtr ns = NULL;
xmlChar * prefix = NULL;
xmlChar * localname = NULL;
if ( sax->ns_stack == NULL ) {
return;
}
ns = xmlNewNs( sax->ns_stack, href, name );
if ( sax->ns_stack->ns == NULL ) {
localname = xmlSplitQName( NULL, sax->ns_stack->name, &prefix );
if ( name != NULL ) {
if ( xmlStrEqual( prefix , name ) ) {
xmlChar * oname = (xmlChar*)(sax->ns_stack->name);
sax->ns_stack->ns = ns;
xmlFree( oname );
sax->ns_stack->name = (const xmlChar*) xmlStrdup( localname );
}
}
else if ( prefix == NULL ) {
sax->ns_stack->ns = ns;
}
}
if ( prefix ) {
xmlFree( prefix );
}
if ( localname ) {
xmlFree( localname );
}
PSaxStartPrefix( sax, name, href, handler );
}
#define XML_STR_NOT_EMPTY(s) ((s)[0] != 0)
HV *
PmmGenElementSV( pTHX_ PmmSAXVectorPtr sax, const xmlChar * name )
{
HV * retval = newHV();
xmlChar * localname = NULL;
xmlChar * prefix = NULL;
xmlNsPtr ns = NULL;
if ( name != NULL && XML_STR_NOT_EMPTY( name ) ) {
(void) hv_store(retval, "Name", 4,
_C2Sv(name, NULL), NameHash);
localname = xmlSplitQName(NULL, name, &prefix);
if (localname != NULL) xmlFree(localname);
ns = PmmGetNsMapping( sax->ns_stack, prefix );
if (prefix != NULL) xmlFree(prefix);
if ( ns != NULL ) {
(void) hv_store(retval, "NamespaceURI", 12,
_C2Sv(ns->href, NULL), NsURIHash);
if ( ns->prefix ) {
(void) hv_store(retval, "Prefix", 6,
_C2Sv(ns->prefix, NULL), PrefixHash);
}
else {
(void) hv_store(retval, "Prefix", 6,
_C2Sv((const xmlChar *)"",NULL), PrefixHash);
}
(void) hv_store(retval, "LocalName", 9,
_C2Sv(sax->ns_stack->name, NULL), LocalNameHash);
}
else {
(void) hv_store(retval, "NamespaceURI", 12,
_C2Sv((const xmlChar *)"",NULL), NsURIHash);
(void) hv_store(retval, "Prefix", 6,
_C2Sv((const xmlChar *)"",NULL), PrefixHash);
(void) hv_store(retval, "LocalName", 9,
_C2Sv(name, NULL), LocalNameHash);
}
}
return retval;
}
xmlChar *
PmmGenNsName( const xmlChar * name, const xmlChar * nsURI )
{
int namelen = 0;
int urilen = 0;
xmlChar * retval = NULL;
if ( name == NULL ) {
return NULL;
}
namelen = xmlStrlen( name );
retval = xmlStrncat( retval, (const xmlChar *)"{", 1 );
if ( nsURI != NULL ) {
urilen = xmlStrlen( nsURI );
retval = xmlStrncat( retval, nsURI, urilen );
}
retval = xmlStrncat( retval, (const xmlChar *)"}", 1 );
retval = xmlStrncat( retval, name, namelen );
return retval;
}
HV *
PmmGenAttributeHashSV( pTHX_ PmmSAXVectorPtr sax,
const xmlChar **attr, SV * handler )
{
HV * retval = NULL;
HV * atV = NULL;
xmlNsPtr ns = NULL;
U32 atnameHash = 0;
int len = 0;
const xmlChar * nsURI = NULL;
const xmlChar **ta = attr;
const xmlChar * name = NULL;
const xmlChar * value = NULL;
xmlChar * keyname = NULL;
xmlChar * localname = NULL;
xmlChar * prefix = NULL;
retval = newHV();
if ( ta != NULL ) {
while ( *ta != NULL ) {
atV = newHV();
name = *ta; ta++;
value = *ta; ta++;
if ( name != NULL && XML_STR_NOT_EMPTY( name ) ) {
localname = xmlSplitQName(NULL, name, &prefix);
(void) hv_store(atV, "Name", 4,
_C2Sv(name, NULL), NameHash);
if ( value != NULL ) {
(void) hv_store(atV, "Value", 5,
_C2Sv(value, NULL), ValueHash);
}
if ( xmlStrEqual( (const xmlChar *)"xmlns", name ) ) {
/* a default namespace */
PmmAddNamespace( sax, NULL, value, handler);
/* nsURI = (const xmlChar*)NSDEFAULTURI; */
nsURI = NULL;
(void) hv_store(atV, "Name", 4,
_C2Sv(name, NULL), NameHash);
(void) hv_store(atV, "Prefix", 6,
_C2Sv((const xmlChar *)"", NULL), PrefixHash);
(void) hv_store(atV, "LocalName", 9,
_C2Sv(name,NULL), LocalNameHash);
(void) hv_store(atV, "NamespaceURI", 12,
_C2Sv((const xmlChar *)"", NULL), NsURIHash);
}
else if (xmlStrncmp((const xmlChar *)"xmlns:", name, 6 ) == 0 ) {
PmmAddNamespace( sax,
localname,
value,
handler);
nsURI = (const xmlChar*)NSDEFAULTURI;
(void) hv_store(atV, "Prefix", 6,
_C2Sv(prefix, NULL), PrefixHash);
(void) hv_store(atV, "LocalName", 9,
_C2Sv(localname, NULL), LocalNameHash);
(void) hv_store(atV, "NamespaceURI", 12,
_C2Sv((const xmlChar *)NSDEFAULTURI,NULL),
NsURIHash);
}
else if ( prefix != NULL
&& (ns = PmmGetNsMapping( sax->ns_stack, prefix ) ) ) {
nsURI = ns->href;
(void) hv_store(atV, "NamespaceURI", 12,
_C2Sv(ns->href, NULL), NsURIHash);
(void) hv_store(atV, "Prefix", 6,
_C2Sv(ns->prefix, NULL), PrefixHash);
(void) hv_store(atV, "LocalName", 9,
_C2Sv(localname, NULL), LocalNameHash);
}
else {
nsURI = NULL;
(void) hv_store(atV, "NamespaceURI", 12,
_C2Sv((const xmlChar *)"", NULL), NsURIHash);
(void) hv_store(atV, "Prefix", 6,
_C2Sv((const xmlChar *)"", NULL), PrefixHash);
(void) hv_store(atV, "LocalName", 9,
_C2Sv(name, NULL), LocalNameHash);
}
keyname = PmmGenNsName( localname != NULL ? localname : name,
nsURI );
len = xmlStrlen( keyname );
PERL_HASH( atnameHash, (const char *)keyname, len );
(void) hv_store(retval,
(const char *)keyname,
len,
newRV_noinc((SV*)atV),
atnameHash );
if ( keyname != NULL ) {
xmlFree( keyname );
}
if ( localname != NULL ) {
xmlFree(localname);
}
localname = NULL;
if ( prefix != NULL ) {
xmlFree( prefix );
}
prefix = NULL;
}
}
}
return retval;
}
HV *
PmmGenCharDataSV( pTHX_ PmmSAXVectorPtr sax, const xmlChar * data, int len )
{
HV * retval = newHV();
if ( data != NULL && XML_STR_NOT_EMPTY( data ) ) {
(void) hv_store(retval, "Data", 4,
_C2Sv_len(data, len), DataHash);
}
return retval;
}
HV *
PmmGenPISV( pTHX_ PmmSAXVectorPtr sax,
const xmlChar * target,
const xmlChar * data )
{
HV * retval = newHV();
if ( target != NULL && XML_STR_NOT_EMPTY( target ) ) {
(void) hv_store(retval, "Target", 6,
_C2Sv(target, NULL), TargetHash);
if ( data != NULL && XML_STR_NOT_EMPTY( data ) ) {
(void) hv_store(retval, "Data", 4,
_C2Sv(data, NULL), DataHash);
}
else {
(void) hv_store(retval, "Data", 4,
_C2Sv((const xmlChar *)"", NULL), DataHash);
}
}
return retval;
}
HV *
PmmGenDTDSV( pTHX_ PmmSAXVectorPtr sax,
const xmlChar * name,
const xmlChar * publicId,
const xmlChar * systemId )
{
HV * retval = newHV();
if ( name != NULL && XML_STR_NOT_EMPTY( name ) ) {
(void) hv_store(retval, "Name", 4,
_C2Sv(name, NULL), NameHash);
}
if ( publicId != NULL && XML_STR_NOT_EMPTY( publicId ) ) {
(void) hv_store(retval, "PublicId", 8,
_C2Sv(publicId, NULL), PublicIdHash);
}
if ( systemId != NULL && XML_STR_NOT_EMPTY( systemId ) ) {
(void) hv_store(retval, "SystemId", 8,
_C2Sv(systemId, NULL), SystemIdHash);
}
return retval;
}
HV *
PmmGenLocator( xmlSAXLocatorPtr loc)
{
dTHX;
HV * locator = newHV();
const xmlChar * PublicId = loc->getPublicId(NULL);
const xmlChar * SystemId = loc->getSystemId(NULL);
if ( PublicId != NULL && XML_STR_NOT_EMPTY( PublicId ) ) {
(void) hv_store(locator, "PublicId", 8,
newSVpv((char *)PublicId, 0), 0);
}
if ( SystemId != NULL && XML_STR_NOT_EMPTY( SystemId ) ) {
(void) hv_store(locator, "SystemId", 8,
newSVpv((char *)SystemId, 0), 0);
}
return locator;
}
void
PmmUpdateLocator( xmlParserCtxtPtr ctxt )
{
dTHX;
const xmlChar * encoding;
const xmlChar * version;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
if (sax->locator == NULL) {
return;
}
(void) hv_store(sax->locator, "LineNumber", 10,
newSViv(ctxt->input->line), 0);
(void) hv_store(sax->locator, "ColumnNumber", 12,
newSViv(ctxt->input->col), 0);
encoding = ctxt->input->encoding;
version = ctxt->input->version;
if ( encoding != NULL && XML_STR_NOT_EMPTY( encoding ) ) {
(void) hv_store(sax->locator, "Encoding", 8,
newSVpv((char *)encoding, 0), 0);
}
if ( version != NULL && XML_STR_NOT_EMPTY( version ) ) {
(void) hv_store(sax->locator, "XMLVersion", 10,
newSVpv((char *)version, 0), 0);
}
}
int
PSaxSetDocumentLocator(void *ctx, xmlSAXLocatorPtr loc)
{
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
dTHX;
HV* empty;
SV * handler = sax->handler;
SV * rv;
dSP;
if (sax->joinchars)
{
PSaxCharactersFlush(ctxt, sax->charbuf);
}
ENTER;
SAVETMPS;
PUSHMARK(SP) ;
XPUSHs(handler);
sax->locator = PmmGenLocator(loc);
rv = newRV_inc((SV*)sax->locator);
XPUSHs( rv);
PUTBACK;
call_method( "set_document_locator", G_SCALAR | G_EVAL | G_DISCARD );
sv_2mortal(rv) ;
if (SvTRUE(ERRSV)) {
croak_obj;
}
FREETMPS ;
LEAVE ;
CLEAR_SERROR_HANDLER
return 1;
}
int
PSaxStartDocument(void * ctx)
{
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
dTHX;
HV* empty;
SV * handler = sax->handler;
SV * rv;
if ( handler != NULL ) {
dSP;
PmmUpdateLocator(ctx);
ENTER;
SAVETMPS;
empty = newHV();
PUSHMARK(SP) ;
XPUSHs(handler);
XPUSHs(sv_2mortal(newRV_noinc((SV*)empty)));
PUTBACK;
call_method( "start_document", G_SCALAR | G_EVAL | G_DISCARD );
if (SvTRUE(ERRSV)) {
croak_obj;
}
SPAGAIN;
PUSHMARK(SP) ;
XPUSHs(handler);
empty = newHV();
if ( ctxt->version != NULL ) {
(void) hv_store(empty, "Version", 7,
_C2Sv(ctxt->version, NULL), VersionHash);
}
else {
(void) hv_store(empty, "Version", 7,
_C2Sv((const xmlChar *)"1.0", NULL), VersionHash);
}
if ( ctxt->input->encoding != NULL ) {
(void) hv_store(empty, "Encoding", 8,
_C2Sv(ctxt->input->encoding, NULL), EncodingHash);
}
rv = newRV_noinc((SV*)empty);
XPUSHs( rv);
PUTBACK;
call_method( "xml_decl", G_SCALAR | G_EVAL | G_DISCARD );
CLEAR_SERROR_HANDLER
sv_2mortal(rv);
if (SvTRUE(ERRSV)) {
croak_obj;
}
FREETMPS ;
LEAVE ;
}
CLEAR_SERROR_HANDLER
return 1;
}
int
PSaxEndDocument(void * ctx)
{
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
dTHX;
dSP;
PmmUpdateLocator(ctx);
if (sax->joinchars)
{
PSaxCharactersFlush(ctxt, sax->charbuf);
}
ENTER;
SAVETMPS;
PUSHMARK(SP) ;
XPUSHs(sax->parser);
PUTBACK;
call_pv( "XML::LibXML::_SAXParser::end_document", G_SCALAR | G_EVAL | G_DISCARD );
if (SvTRUE(ERRSV)) {
croak_obj;
}
FREETMPS ;
LEAVE ;
CLEAR_SERROR_HANDLER
return 1;
}
int
PSaxStartElement(void *ctx, const xmlChar * name, const xmlChar** attr)
{
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
dTHX;
HV * attrhash = NULL;
HV * element = NULL;
SV * handler = sax->handler;
SV * rv;
SV * arv;
dSP;
PmmUpdateLocator(ctx);
if (sax->joinchars)
{
PSaxCharactersFlush(ctxt, sax->charbuf);
}
ENTER;
SAVETMPS;
PmmExtendNsStack(sax, name);
attrhash = PmmGenAttributeHashSV(aTHX_ sax, attr, handler );
element = PmmGenElementSV(aTHX_ sax, name);
arv = newRV_noinc((SV*)attrhash);
(void) hv_store( element,
"Attributes",
10,
arv,
AttributesHash );
PUSHMARK(SP) ;
XPUSHs(handler);
rv = newRV_noinc((SV*)element);
XPUSHs(rv);
PUTBACK;
call_method( "start_element", G_SCALAR | G_EVAL | G_DISCARD );
sv_2mortal(rv) ;
if (SvTRUE(ERRSV)) {
croak_obj;
}
FREETMPS ;
LEAVE ;
CLEAR_SERROR_HANDLER
return 1;
}
int
PSaxEndElement(void *ctx, const xmlChar * name) {
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
dTHX;
SV * handler = sax->handler;
SV * rv;
HV * element;
dSP;
PmmUpdateLocator(ctx);
if (sax->joinchars)
{
PSaxCharactersFlush(ctxt, sax->charbuf);
}
ENTER;
SAVETMPS;
PUSHMARK(SP) ;
XPUSHs(handler);
element = PmmGenElementSV(aTHX_ sax, name);
rv = newRV_noinc((SV*)element);
XPUSHs(rv);
PUTBACK;
call_method( "end_element", G_SCALAR | G_EVAL | G_DISCARD );
sv_2mortal(rv);
if (SvTRUE(ERRSV)) {
croak_obj;
}
FREETMPS ;
LEAVE ;
PmmNarrowNsStack(sax, handler);
CLEAR_SERROR_HANDLER
return 1;
}
int
PSaxCharactersDispatch(void *ctx, const xmlChar * ch, int len) {
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
dTHX;
HV* element;
SV * handler;
SV * rv = NULL;
if ( sax == NULL ) {
/* warn( "lost my sax context!? ( %s, %d )\n", ch, len ); */
return 0;
}
handler = sax->handler;
if ( ch != NULL && handler != NULL ) {
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP) ;
XPUSHs(handler);
element = PmmGenCharDataSV(aTHX_ sax, ch, len );
rv = newRV_noinc((SV*)element);
XPUSHs(rv);
sv_2mortal(rv);
PUTBACK;
call_method( "characters", G_SCALAR | G_EVAL | G_DISCARD );
if (SvTRUE(ERRSV)) {
croak_obj;
}
FREETMPS ;
LEAVE ;
}
CLEAR_SERROR_HANDLER;
return 1;
}
int PSaxCharactersFlush (void *ctx, struct CBuffer *buffer) {
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
xmlChar *ch;
int len;
if (buffer->head->data == NULL) {
return 1;
}
ch = CBufferCharacters(sax->charbuf);
len = CBufferLength(sax->charbuf);
CBufferPurge(buffer);
return PSaxCharactersDispatch(ctx, ch, len);
}
int PSaxCharacters (void *ctx, const xmlChar * ch, int len) {
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
PmmUpdateLocator(ctx);
if (sax->joinchars) {
struct CBuffer *buffer = sax->charbuf;
CBufferAppend(buffer, ch, len);
return 1;
}
return PSaxCharactersDispatch(ctx, ch, len);
}
int
PSaxComment(void *ctx, const xmlChar * ch) {
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
dTHX;
HV* element;
SV * handler = sax->handler;
SV * rv = NULL;
PmmUpdateLocator(ctx);
if ( ch != NULL && handler != NULL ) {
dSP;
int len = xmlStrlen( ch );
if (sax->joinchars)
{
PSaxCharactersFlush(ctxt, sax->charbuf);
}
ENTER;
SAVETMPS;
PUSHMARK(SP) ;
XPUSHs(handler);
element = PmmGenCharDataSV(aTHX_ sax, ch, len);
rv = newRV_noinc((SV*)element);
XPUSHs(rv);
PUTBACK;
call_method( "comment", G_SCALAR | G_EVAL | G_DISCARD );
sv_2mortal(rv);
if (SvTRUE(ERRSV)) {
croak_obj;
}
FREETMPS ;
LEAVE ;
}
CLEAR_SERROR_HANDLER
return 1;
}
int
PSaxCDATABlock(void *ctx, const xmlChar * ch, int len) {
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
dTHX;
HV* element;
SV * handler = sax->handler;
SV * rv = NULL;
PmmUpdateLocator(ctx);
if ( ch != NULL && handler != NULL ) {
dSP;
if (sax->joinchars)
{
PSaxCharactersFlush(ctxt, sax->charbuf);
}
ENTER;
SAVETMPS;
PUSHMARK(SP) ;
XPUSHs(handler);
PUTBACK;
call_method( "start_cdata", G_SCALAR | G_EVAL | G_DISCARD );
if (SvTRUE(ERRSV)) {
croak_obj;
}
SPAGAIN;
PUSHMARK(SP) ;
XPUSHs(handler);
element = PmmGenCharDataSV(aTHX_ sax, ch, len);
rv = newRV_noinc((SV*)element);
XPUSHs(rv);
PUTBACK;
call_method( "characters", G_SCALAR | G_EVAL | G_DISCARD);
if (SvTRUE(ERRSV)) {
croak_obj;
}
SPAGAIN;
PUSHMARK(SP) ;
XPUSHs(handler);
PUTBACK;
call_method( "end_cdata", G_SCALAR | G_EVAL | G_DISCARD );
sv_2mortal(rv);
if (SvTRUE(ERRSV)) {
croak_obj;
}
FREETMPS ;
LEAVE ;
}
CLEAR_SERROR_HANDLER
return 1;
}
int
PSaxProcessingInstruction( void * ctx, const xmlChar * target, const xmlChar * data )
{
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
dTHX;
SV * handler = sax->handler;
SV * element;
SV * rv = NULL;
PmmUpdateLocator(ctx);
if ( handler != NULL ) {
dSP;
if (sax->joinchars)
{
PSaxCharactersFlush(ctxt, sax->charbuf);
}
ENTER;
SAVETMPS;
PUSHMARK(SP) ;
XPUSHs(handler);
element = (SV*)PmmGenPISV(aTHX_ sax, (const xmlChar *)target, data);
rv = newRV_noinc((SV*)element);
XPUSHs(rv);
PUTBACK;
call_method( "processing_instruction", G_SCALAR | G_EVAL | G_DISCARD );
sv_2mortal(rv);
if (SvTRUE(ERRSV)) {
croak_obj;
}
FREETMPS ;
LEAVE ;
}
CLEAR_SERROR_HANDLER
return 1;
}
void PSaxExternalSubset (void * ctx,
const xmlChar * name,
const xmlChar * ExternalID,
const xmlChar * SystemID)
{
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
dTHX;
SV * handler = sax->handler;
SV * element;
SV * rv = NULL;
PmmUpdateLocator(ctx);
if ( handler != NULL ) {
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP) ;
XPUSHs(handler);
element = (SV*)PmmGenDTDSV(aTHX_ sax,
name,
ExternalID,
SystemID);
rv = newRV_noinc((SV*)element);
XPUSHs(rv);
PUTBACK;
call_method( "start_dtd", G_SCALAR | G_EVAL | G_DISCARD );
sv_2mortal(rv);
if (SvTRUE(ERRSV)) {
croak_obj;
}
PUSHMARK(SP) ;
XPUSHs(handler);
rv = newRV_noinc((SV*)newHV()); /* empty */
XPUSHs(rv);
PUTBACK;
call_method( "end_dtd", G_SCALAR | G_EVAL | G_DISCARD );
FREETMPS ;
LEAVE ;
}
CLEAR_SERROR_HANDLER
return;
}
/*
void PSaxInternalSubset (void * ctx,
const xmlChar * name,
const xmlChar * ExternalID,
const xmlChar * SystemID)
{
// called before ExternalSubset
// if used, how do we generate the correct start_dtd ?
}
void PSaxElementDecl (void *ctx, const xmlChar *name,
int type,
xmlElementContentPtr content) {
// this one is not easy to implement
// since libxml2 has no (reliable) public method
// for dumping xmlElementContent :-(
}
void
PSaxAttributeDecl (void * ctx,
const xmlChar * elem,
const xmlChar * fullname,
int type,
int def,
const xmlChar * defaultValue,
xmlEnumerationPtr tree)
{
}
void
PSaxEntityDecl (void * ctx,
const xmlChar * name,
int type,
const xmlChar * publicId,
const xmlChar * systemId,
xmlChar * content)
{
}
void
PSaxNotationDecl (void * ctx,
const xmlChar * name,
const xmlChar * publicId,
const xmlChar * systemId)
{
}
void
PSaxUnparsedEntityDecl (void * ctx,
const xmlChar * name,
const xmlChar * publicId,
const xmlChar * systemId,
const xmlChar * notationName)
{
}
*/
int
PmmSaxWarning(void * ctx, const char * msg, ...)
{
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
va_list args;
SV * svMessage;
dTHX;
dSP;
svMessage = NEWSV(0,512);
va_start(args, msg);
sv_vsetpvfn(svMessage,
msg,
xmlStrlen((const xmlChar *)msg),
&args,
NULL,
0,
NULL);
va_end(args);
ENTER;
SAVETMPS;
PUSHMARK(SP) ;
XPUSHs(sax->parser);
XPUSHs(sv_2mortal(svMessage));
XPUSHs(sv_2mortal(newSViv(ctxt->input->line)));
XPUSHs(sv_2mortal(newSViv(ctxt->input->col)));
PUTBACK;
call_pv( "XML::LibXML::_SAXParser::warning", G_SCALAR | G_EVAL | G_DISCARD );
if (SvTRUE(ERRSV)) {
croak_obj;
}
FREETMPS ;
LEAVE ;
CLEAR_SERROR_HANDLER
return 1;
}
int
PmmSaxError(void * ctx, const char * msg, ...)
{
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
va_list args;
SV * svMessage;
#if LIBXML_VERSION > 20600
xmlErrorPtr last_err = xmlCtxtGetLastError( ctxt );
#endif
dTHX;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP) ;
XPUSHs(sax->parser);
svMessage = NEWSV(0,512);
va_start(args, msg);
sv_vsetpvfn(svMessage, msg, xmlStrlen((const xmlChar *)msg), &args, NULL, 0, NULL);
va_end(args);
if (SvOK(sax->saved_error)) {
sv_catsv( sax->saved_error, svMessage );
} else {
sv_setsv( sax->saved_error, svMessage );
}
XPUSHs(sv_2mortal(svMessage));
XPUSHs(sv_2mortal(newSViv(ctxt->input->line)));
XPUSHs(sv_2mortal(newSViv(ctxt->input->col)));
PUTBACK;
#if LIBXML_VERSION > 20600
/*
this is a workaround: at least some versions of libxml2 didn't not call
the fatalError callback at all
*/
if (last_err && last_err->level == XML_ERR_FATAL) {
call_pv( "XML::LibXML::_SAXParser::fatal_error", G_SCALAR | G_EVAL | G_DISCARD );
} else {
call_pv( "XML::LibXML::_SAXParser::error", G_SCALAR | G_EVAL | G_DISCARD );
}
#else
/* actually, we do not know if it is a fatal error or not */
call_pv( "XML::LibXML::_SAXParser::fatal_error", G_SCALAR | G_EVAL | G_DISCARD );
#endif
if (SvTRUE(ERRSV)) {
croak_obj;
}
FREETMPS ;
LEAVE ;
CLEAR_SERROR_HANDLER
return 1;
}
int
PmmSaxFatalError(void * ctx, const char * msg, ...)
{
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
PmmSAXVectorPtr sax = (PmmSAXVectorPtr)ctxt->_private;
va_list args;
SV * svMessage;
dTHX;
dSP;
svMessage = NEWSV(0,512);
va_start(args, msg);
sv_vsetpvfn(svMessage, msg, xmlStrlen((const xmlChar *)msg), &args, NULL, 0, NULL);
va_end(args);
ENTER;
SAVETMPS;
PUSHMARK(SP) ;
XPUSHs(sax->parser);
if (SvOK(sax->saved_error)) {
sv_catsv( sax->saved_error, svMessage );
} else {
sv_setsv( sax->saved_error, svMessage );
}
XPUSHs(sv_2mortal(svMessage));
XPUSHs(sv_2mortal(newSViv(ctxt->input->line)));
XPUSHs(sv_2mortal(newSViv(ctxt->input->col)));
PUTBACK;
call_pv( "XML::LibXML::_SAXParser::fatal_error", G_SCALAR | G_EVAL | G_DISCARD );
if (SvTRUE(ERRSV)) {
croak_obj;
}
FREETMPS ;
LEAVE ;
CLEAR_SERROR_HANDLER
return 1;
}
/* NOTE:
* end document is not handled by the parser itself! use
* XML::LibXML::SAX instead!
*/
xmlSAXHandlerPtr
PSaxGetHandler()
{
xmlSAXHandlerPtr retval = (xmlSAXHandlerPtr)xmlMalloc(sizeof(xmlSAXHandler));
memset(retval, 0, sizeof(xmlSAXHandler));
retval->setDocumentLocator = (setDocumentLocatorSAXFunc)&PSaxSetDocumentLocator;
retval->startDocument = (startDocumentSAXFunc)&PSaxStartDocument;
/* libxml2 will not handle perls returnvalue correctly, so we have
* to end the document ourselfes
*/
retval->endDocument = NULL; /* (endDocumentSAXFunc)&PSaxEndDocument; */
retval->startElement = (startElementSAXFunc)&PSaxStartElement;
retval->endElement = (endElementSAXFunc)&PSaxEndElement;
retval->characters = (charactersSAXFunc)&PSaxCharacters;
retval->ignorableWhitespace = (ignorableWhitespaceSAXFunc)&PSaxCharacters;
retval->comment = (commentSAXFunc)&PSaxComment;
retval->cdataBlock = (cdataBlockSAXFunc)&PSaxCDATABlock;
retval->processingInstruction = (processingInstructionSAXFunc)&PSaxProcessingInstruction;
/* warning functions should be internal */
retval->warning = (warningSAXFunc)&PmmSaxWarning;
retval->error = (errorSAXFunc)&PmmSaxError;
retval->fatalError = (fatalErrorSAXFunc)&PmmSaxFatalError;
retval->externalSubset = (externalSubsetSAXFunc)&PSaxExternalSubset;
/*
retval->internalSubset = (internalSubsetSAXFunc)&PSaxInternalSubset;
retval->elementDecl = (elementDeclSAXFunc)&PSaxElementDecl;
retval->entityDecl = (entityDeclSAXFunc)&PSaxEntityDecl;
retval->notationDecl = (notationDeclSAXFunc)&PSaxNotationDecl;
retval->attributeDecl = (attributeDeclSAXFunc)&PSaxAttributeDecl;
retval->unparsedEntityDecl = (unparsedEntityDeclSAXFunc)&PSaxUnparsedEntityDecl;
*/
return retval;
}