/*****************************************************************
** Expat.xs
**
** Copyright 1998 Larry Wall and Clark Cooper
** All rights reserved.
**
** This program is free software; you can redistribute it and/or
** modify it under the same terms as Perl itself.
**
*/
#include <expat.h>
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#undef convert
#include "encoding.h"
#define BUFSIZE 32768
#define NSDELIM '|'
/* Macro to update handler fields. Used in the various handler setting
XSUBS */
#define XMLP_UPD(fld) \
RETVAL = cbv->fld ? newSVsv(cbv->fld) : &PL_sv_undef;\
if (cbv->fld) {\
if (cbv->fld != fld)\
sv_setsv(cbv->fld, fld);\
}\
else\
cbv->fld = newSVsv(fld)
/* Macro to push old handler value onto return stack. */
#define PUSHRET \
ST(0) = RETVAL;\
if (RETVAL != &PL_sv_undef && SvREFCNT(RETVAL)) sv_2mortal(RETVAL)
typedef struct {
SV* self_sv;
XML_Parser p;
AV* context;
AV* new_prefix_list;
HV *nstab;
AV *nslst;
unsigned int st_serial;
unsigned int st_serial_stackptr;
unsigned int st_serial_stacksize;
unsigned int * st_serial_stack;
unsigned int skip_until;
SV *recstring;
char * delim;
STRLEN delimlen;
unsigned ns:1;
unsigned no_expand:1;
unsigned parseparam:1;
/* Callback handlers */
SV* start_sv;
SV* end_sv;
SV* char_sv;
SV* proc_sv;
SV* cmnt_sv;
SV* dflt_sv;
SV* entdcl_sv;
SV* eledcl_sv;
SV* attdcl_sv;
SV* doctyp_sv;
SV* doctypfin_sv;
SV* xmldec_sv;
SV* unprsd_sv;
SV* notation_sv;
SV* extent_sv;
SV* extfin_sv;
SV* startcd_sv;
SV* endcd_sv;
} CallbackVector;
static HV* EncodingTable = NULL;
static XML_Char nsdelim[] = {NSDELIM, '\0'};
static const char *QuantChar[] = {"", "?", "*", "+"};
/* Forward declarations */
static void suspend_callbacks(CallbackVector *);
static void resume_callbacks(CallbackVector *);
static SV *
newUTF8SVpv(char *s, STRLEN len) {
SV *sv;
sv = newSVpv(s, len);
SvUTF8_on(sv);
return sv;
} /* End new UTF8SVpv */
static SV *
newUTF8SVpvn(char *s, STRLEN len) {
SV *sv;
sv = newSV(0);
sv_setpvn(sv, s, len);
SvUTF8_on(sv);
return sv;
}
static void*
mymalloc(size_t size) {
return safemalloc(size);
}
static void*
myrealloc(void *p, size_t s) {
return saferealloc(p, s);
}
static void
myfree(void *p) {
Safefree(p);
}
static XML_Memory_Handling_Suite ms = {mymalloc, myrealloc, myfree};
static void
free_cbv(CallbackVector *cbv)
{
if (cbv) {
SvREFCNT_dec(cbv->self_sv);
Safefree(cbv->st_serial_stack);
Safefree(cbv);
}
}
static void
append_error(XML_Parser parser, char * err)
{
dSP;
CallbackVector * cbv;
SV ** errstr;
cbv = (CallbackVector*) XML_GetUserData(parser);
errstr = hv_fetch((HV*)SvRV(cbv->self_sv),
"ErrorMessage", 12, 0);
if (errstr && SvPOK(*errstr)) {
SV ** errctx = hv_fetch((HV*) SvRV(cbv->self_sv),
"ErrorContext", 12, 0);
int dopos = !err && errctx && SvOK(*errctx);
if (! err)
err = (char *) XML_ErrorString(XML_GetErrorCode(parser));
/* Avoid truncation on 32-bit perls when expat is built with
XML_LARGE_SIZE (long long types). Use NV (double, 53-bit
integer precision) to preserve values up to ~9 PB.
See https://github.com/cpan-authors/XML-Parser/issues/36
and https://github.com/cpan-authors/XML-Parser/issues/48 */
#if (defined(XML_LARGE_SIZE) && IVSIZE < 8)
sv_catpvf(*errstr, "\n%s at line %.0" NVff ", column %.0" NVff ", byte %.0" NVff "%s",
err,
(NV)XML_GetCurrentLineNumber(parser),
(NV)XML_GetCurrentColumnNumber(parser),
(NV)XML_GetCurrentByteIndex(parser),
dopos ? ":\n" : "");
#else
sv_catpvf(*errstr, "\n%s at line %" UVuf ", column %" UVuf ", byte %" IVdf "%s",
err,
(UV)XML_GetCurrentLineNumber(parser),
(UV)XML_GetCurrentColumnNumber(parser),
(IV)XML_GetCurrentByteIndex(parser),
dopos ? ":\n" : "");
#endif
if (dopos)
{
int count;
ENTER ;
SAVETMPS ;
PUSHMARK(sp);
XPUSHs(cbv->self_sv);
XPUSHs(*errctx);
PUTBACK ;
count = perl_call_method("position_in_context", G_SCALAR);
SPAGAIN ;
if (count >= 1) {
sv_catsv(*errstr, POPs);
}
PUTBACK ;
FREETMPS ;
LEAVE ;
}
if (XML_GetErrorCode(parser) == XML_ERROR_INVALID_TOKEN) {
sv_catpv(*errstr,
"(Hint: \"not well-formed\" often indicates unescaped '<', '>' or '&'"
" in content — use < > or & instead)\n");
}
}
} /* End append_error */
static SV *
generate_model(XML_Content *model) {
HV * hash = newHV();
SV * obj = newRV_noinc((SV *) hash);
sv_bless(obj, gv_stashpv("XML::Parser::ContentModel", 1));
hv_store(hash, "Type", 4, newSViv(model->type), 0);
if (model->quant != XML_CQUANT_NONE) {
hv_store(hash, "Quant", 5, newSVpv(QuantChar[model->quant], 1), 0);
}
switch(model->type) {
case XML_CTYPE_NAME:
hv_store(hash, "Tag", 3, newUTF8SVpv((char *)model->name, 0), 0);
break;
case XML_CTYPE_MIXED:
case XML_CTYPE_CHOICE:
case XML_CTYPE_SEQ:
if (model->children && model->numchildren)
{
AV * children = newAV();
int i;
for (i = 0; i < model->numchildren; i++) {
av_push(children, generate_model(&model->children[i]));
}
hv_store(hash, "Children", 8, newRV_noinc((SV *) children), 0);
}
break;
case XML_CTYPE_EMPTY:
case XML_CTYPE_ANY:
break;
}
return obj;
} /* End generate_model */
static int
parse_stream(XML_Parser parser, SV * ioref)
{
dSP;
SV * tbuff = NULL;
SV * tsiz = NULL;
char * linebuff = NULL;
STRLEN lblen;
STRLEN br = 0;
int buffsize;
int done = 0;
int ret = 1;
CallbackVector * cbv;
cbv = (CallbackVector*) XML_GetUserData(parser);
ENTER;
SAVETMPS;
if (cbv->delim) {
int cnt;
SV * tline;
PUSHMARK(SP);
XPUSHs(ioref);
PUTBACK ;
cnt = perl_call_method("getline", G_SCALAR);
SPAGAIN;
if (cnt != 1)
croak("getline method call failed");
tline = POPs;
if (! SvOK(tline)) {
lblen = 0;
}
else {
linebuff = SvPV(tline, lblen);
if (lblen > cbv->delimlen + 1) {
char *chk = &linebuff[lblen - cbv->delimlen - 1];
if (*chk == *cbv->delim
&& chk[cbv->delimlen] == '\n'
&& strnEQ(chk + 1, cbv->delim + 1, cbv->delimlen - 1))
lblen -= cbv->delimlen + 1;
}
}
PUTBACK ;
buffsize = lblen;
done = lblen == 0;
}
else {
tbuff = newSV(0);
tsiz = newSViv(BUFSIZE);
buffsize = BUFSIZE;
/* Register for cleanup so croak() in the loop won't leak them */
SAVEFREESV(tbuff);
SAVEFREESV(tsiz);
}
while (! done)
{
char *buffer = XML_GetBuffer(parser, buffsize);
if (! buffer)
croak("Ran out of memory for input buffer");
SAVETMPS;
if (cbv->delim) {
Copy(linebuff, buffer, lblen, char);
br = lblen;
done = 1;
}
else {
int cnt;
SV * rdres;
char * tb;
PUSHMARK(SP);
EXTEND(SP, 3);
PUSHs(ioref);
PUSHs(tbuff);
PUSHs(tsiz);
PUTBACK ;
cnt = perl_call_method("read", G_SCALAR);
SPAGAIN ;
if (cnt != 1)
croak("read method call failed");
rdres = POPs;
if (! SvOK(rdres))
croak("read error");
tb = SvPV(tbuff, br);
if (br > 0) {
if (br > buffsize) {
/* The byte count from SvPV can exceed buffsize when the
filehandle has a :utf8 layer, since Perl reads buffsize
characters but multi-byte UTF-8 chars produce more bytes.
Re-obtain the buffer at the required size. */
buffer = XML_GetBuffer(parser, br);
if (! buffer)
croak("Ran out of memory for input buffer");
}
Copy(tb, buffer, br, char);
} else
done = 1;
PUTBACK ;
}
ret = XML_ParseBuffer(parser, br, done);
SPAGAIN; /* resync local SP in case callbacks changed global stack */
if (! ret)
break;
FREETMPS;
}
if (! ret)
append_error(parser, NULL);
/* tbuff and tsiz are freed by SAVEFREESV via FREETMPS/LEAVE below */
FREETMPS;
LEAVE;
return ret;
} /* End parse_stream */
static SV *
gen_ns_name(const char * name, HV * ns_table, AV * ns_list)
{
char *pos = strchr(name, NSDELIM);
SV * ret;
if (pos && pos > name)
{
SV ** name_ent = hv_fetch(ns_table, (char *) name,
pos - name, TRUE);
ret = newUTF8SVpv(&pos[1], 0);
if (name_ent)
{
int index;
if (SvOK(*name_ent))
{
index = SvIV(*name_ent);
}
else
{
av_push(ns_list, newUTF8SVpv((char *) name, pos - name));
index = av_len(ns_list);
sv_setiv(*name_ent, (IV) index);
}
sv_setiv(ret, (IV) index);
SvPOK_on(ret);
}
}
else
ret = newUTF8SVpv((char *) name, 0);
return ret;
} /* End gen_ns_name */
static void
characterData(void *userData, const char *s, int len)
{
dSP;
CallbackVector* cbv = (CallbackVector*) userData;
ENTER;
SAVETMPS;
PUSHMARK(sp);
EXTEND(sp, 2);
PUSHs(cbv->self_sv);
PUSHs(sv_2mortal(newUTF8SVpvn((char*)s,len)));
PUTBACK;
perl_call_sv(cbv->char_sv, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
} /* End characterData */
static void
startElement(void *userData, const char *name, const char **atts)
{
dSP;
CallbackVector* cbv = (CallbackVector*) userData;
unsigned do_ns = cbv->ns;
unsigned skipping = 0;
SV * elname;
cbv->st_serial++;
if (cbv->skip_until) {
skipping = cbv->st_serial < cbv->skip_until;
if (! skipping) {
resume_callbacks(cbv);
cbv->skip_until = 0;
}
}
if (cbv->st_serial_stackptr + 1 >= cbv->st_serial_stacksize) {
unsigned int newsize = cbv->st_serial_stacksize + 512;
Renew(cbv->st_serial_stack, newsize, unsigned int);
cbv->st_serial_stacksize = newsize;
}
cbv->st_serial_stack[++cbv->st_serial_stackptr] = cbv->st_serial;
if (do_ns)
elname = gen_ns_name(name, cbv->nstab, cbv->nslst);
else
elname = newUTF8SVpv((char *)name, 0);
if (! skipping && SvTRUE(cbv->start_sv))
{
const char **attlim = atts;
while (*attlim)
attlim++;
ENTER;
SAVETMPS;
PUSHMARK(sp);
EXTEND(sp, attlim - atts + 2);
PUSHs(cbv->self_sv);
PUSHs(elname);
while (*atts)
{
SV * attname;
attname = (do_ns ? gen_ns_name(*atts, cbv->nstab, cbv->nslst)
: newUTF8SVpv((char *) *atts, 0));
atts++;
PUSHs(sv_2mortal(attname));
if (*atts)
PUSHs(sv_2mortal(newUTF8SVpv((char*)*atts++,0)));
}
PUTBACK;
perl_call_sv(cbv->start_sv, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
}
av_push(cbv->context, elname);
if (cbv->ns) {
av_clear(cbv->new_prefix_list);
}
} /* End startElement */
static void
endElement(void *userData, const char *name)
{
dSP;
CallbackVector* cbv = (CallbackVector*) userData;
SV *elname;
elname = av_pop(cbv->context);
if (! cbv->st_serial_stackptr) {
croak("endElement: Start tag serial number stack underflow");
}
if (! cbv->skip_until && SvTRUE(cbv->end_sv))
{
ENTER;
SAVETMPS;
PUSHMARK(sp);
EXTEND(sp, 2);
PUSHs(cbv->self_sv);
PUSHs(elname);
PUTBACK;
perl_call_sv(cbv->end_sv, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
}
cbv->st_serial_stackptr--;
SvREFCNT_dec(elname);
} /* End endElement */
static void
processingInstruction(void *userData, const char *target, const char *data)
{
dSP;
CallbackVector* cbv = (CallbackVector*) userData;
ENTER;
SAVETMPS;
PUSHMARK(sp);
EXTEND(sp, 3);
PUSHs(cbv->self_sv);
PUSHs(sv_2mortal(newUTF8SVpv((char*)target,0)));
PUSHs(sv_2mortal(newUTF8SVpv((char*)data,0)));
PUTBACK;
perl_call_sv(cbv->proc_sv, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
} /* End processingInstruction */
static void
commenthandle(void *userData, const char *string)
{
dSP;
CallbackVector * cbv = (CallbackVector*) userData;
ENTER;
SAVETMPS;
PUSHMARK(sp);
EXTEND(sp, 2);
PUSHs(cbv->self_sv);
PUSHs(sv_2mortal(newUTF8SVpv((char*) string, 0)));
PUTBACK;
perl_call_sv(cbv->cmnt_sv, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
} /* End commenthandler */
static void
startCdata(void *userData)
{
dSP;
CallbackVector* cbv = (CallbackVector*) userData;
if (cbv->startcd_sv) {
ENTER;
SAVETMPS;
PUSHMARK(sp);
XPUSHs(cbv->self_sv);
PUTBACK;
perl_call_sv(cbv->startcd_sv, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
}
} /* End startCdata */
static void
endCdata(void *userData)
{
dSP;
CallbackVector* cbv = (CallbackVector*) userData;
if (cbv->endcd_sv) {
ENTER;
SAVETMPS;
PUSHMARK(sp);
XPUSHs(cbv->self_sv);
PUTBACK;
perl_call_sv(cbv->endcd_sv, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
}
} /* End endCdata */
static void
nsStart(void *userdata, const XML_Char *prefix, const XML_Char *uri){
dSP;
CallbackVector* cbv = (CallbackVector*) userdata;
ENTER;
SAVETMPS;
PUSHMARK(sp);
EXTEND(sp, 3);
PUSHs(cbv->self_sv);
PUSHs(prefix ? sv_2mortal(newUTF8SVpv((char *)prefix, 0)) : &PL_sv_undef);
PUSHs(uri ? sv_2mortal(newUTF8SVpv((char *)uri, 0)) : &PL_sv_undef);
PUTBACK;
perl_call_method("NamespaceStart", G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
} /* End nsStart */
static void
nsEnd(void *userdata, const XML_Char *prefix) {
dSP;
CallbackVector* cbv = (CallbackVector*) userdata;
ENTER;
SAVETMPS;
PUSHMARK(sp);
EXTEND(sp, 2);
PUSHs(cbv->self_sv);
PUSHs(prefix ? sv_2mortal(newUTF8SVpv((char *)prefix, 0)) : &PL_sv_undef);
PUTBACK;
perl_call_method("NamespaceEnd", G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
} /* End nsEnd */
static void
defaulthandle(void *userData, const char *string, int len)
{
dSP;
CallbackVector* cbv = (CallbackVector*) userData;
SV *handler;
/* If a Char handler is registered and this is character data (not markup),
forward to the Char handler instead of Default. Libexpat sends character
data outside the root element to the default handler, but users expect
the Char handler to be called (rt.cpan.org #46685). */
if (SvTRUE(cbv->char_sv) && len > 0
&& string[0] != '<' && string[0] != '&')
handler = cbv->char_sv;
else
handler = cbv->dflt_sv;
ENTER;
SAVETMPS;
PUSHMARK(sp);
EXTEND(sp, 2);
PUSHs(cbv->self_sv);
PUSHs(sv_2mortal(newUTF8SVpvn((char*)string, len)));
PUTBACK;
perl_call_sv(handler, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
} /* End defaulthandle */
static void
elementDecl(void *data,
const char *name,
XML_Content *model) {
dSP;
CallbackVector *cbv = (CallbackVector*) data;
SV *cmod;
ENTER;
SAVETMPS;
cmod = generate_model(model);
XML_FreeContentModel(cbv->p, model);
PUSHMARK(sp);
EXTEND(sp, 3);
PUSHs(cbv->self_sv);
PUSHs(sv_2mortal(newUTF8SVpv((char *)name, 0)));
PUSHs(sv_2mortal(cmod));
PUTBACK;
perl_call_sv(cbv->eledcl_sv, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
} /* End elementDecl */
static void
attributeDecl(void *data,
const char * elname,
const char * attname,
const char * att_type,
const char * dflt,
int reqorfix) {
dSP;
CallbackVector *cbv = (CallbackVector*) data;
SV * dfltsv;
if (dflt) {
dfltsv = newUTF8SVpv("'", 1);
sv_catpv(dfltsv, (char *) dflt);
sv_catpv(dfltsv, "'");
}
else {
dfltsv = newUTF8SVpv(reqorfix ? "#REQUIRED" : "#IMPLIED", 0);
}
ENTER;
SAVETMPS;
PUSHMARK(sp);
EXTEND(sp, 5);
PUSHs(cbv->self_sv);
PUSHs(sv_2mortal(newUTF8SVpv((char *)elname, 0)));
PUSHs(sv_2mortal(newUTF8SVpv((char *)attname, 0)));
PUSHs(sv_2mortal(newUTF8SVpv((char *)att_type, 0)));
PUSHs(sv_2mortal(dfltsv));
if (dflt && reqorfix)
XPUSHs(&PL_sv_yes);
PUTBACK;
perl_call_sv(cbv->attdcl_sv, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
} /* End attributeDecl */
static void
entityDecl(void *data,
const char *name,
int isparam,
const char *value,
int vlen,
const char *base,
const char *sysid,
const char *pubid,
const char *notation) {
dSP;
CallbackVector *cbv = (CallbackVector*) data;
ENTER;
SAVETMPS;
PUSHMARK(sp);
EXTEND(sp, 6);
PUSHs(cbv->self_sv);
PUSHs(sv_2mortal(newUTF8SVpv((char*)name, 0)));
PUSHs(value ? sv_2mortal(newUTF8SVpvn((char*)value, vlen)) : &PL_sv_undef);
PUSHs(sysid ? sv_2mortal(newUTF8SVpv((char *)sysid, 0)) : &PL_sv_undef);
PUSHs(pubid ? sv_2mortal(newUTF8SVpv((char *)pubid, 0)) : &PL_sv_undef);
PUSHs(notation ? sv_2mortal(newUTF8SVpv((char *)notation, 0)) : &PL_sv_undef);
if (isparam)
XPUSHs(&PL_sv_yes);
PUTBACK;
perl_call_sv(cbv->entdcl_sv, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
} /* End entityDecl */
static void
doctypeStart(void *userData,
const char* name,
const char* sysid,
const char* pubid,
int hasinternal) {
dSP;
CallbackVector *cbv = (CallbackVector*) userData;
ENTER;
SAVETMPS;
PUSHMARK(sp);
EXTEND(sp, 5);
PUSHs(cbv->self_sv);
PUSHs(sv_2mortal(newUTF8SVpv((char*)name, 0)));
PUSHs(sysid ? sv_2mortal(newUTF8SVpv((char*)sysid, 0)) : &PL_sv_undef);
PUSHs(pubid ? sv_2mortal(newUTF8SVpv((char*)pubid, 0)) : &PL_sv_undef);
PUSHs(hasinternal ? &PL_sv_yes : &PL_sv_no);
PUTBACK;
perl_call_sv(cbv->doctyp_sv, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
} /* End doctypeStart */
static void
doctypeEnd(void *userData) {
dSP;
CallbackVector *cbv = (CallbackVector*) userData;
ENTER;
SAVETMPS;
PUSHMARK(sp);
EXTEND(sp, 1);
PUSHs(cbv->self_sv);
PUTBACK;
perl_call_sv(cbv->doctypfin_sv, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
} /* End doctypeEnd */
static void
xmlDecl(void *userData,
const char *version,
const char *encoding,
int standalone) {
dSP;
CallbackVector *cbv = (CallbackVector*) userData;
ENTER;
SAVETMPS;
PUSHMARK(sp);
EXTEND(sp, 4);
PUSHs(cbv->self_sv);
PUSHs(version ? sv_2mortal(newUTF8SVpv((char *)version, 0))
: &PL_sv_undef);
PUSHs(encoding ? sv_2mortal(newUTF8SVpv((char *)encoding, 0))
: &PL_sv_undef);
PUSHs(standalone == -1 ? &PL_sv_undef
: sv_2mortal(standalone ? newSVpvn("yes", 3) : newSVpvn("no", 2)));
PUTBACK;
perl_call_sv(cbv->xmldec_sv, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
} /* End xmlDecl */
static void
unparsedEntityDecl(void *userData,
const char* entity,
const char* base,
const char* sysid,
const char* pubid,
const char* notation)
{
dSP;
CallbackVector* cbv = (CallbackVector*) userData;
ENTER;
SAVETMPS;
PUSHMARK(sp);
EXTEND(sp, 6);
PUSHs(cbv->self_sv);
PUSHs(sv_2mortal(newUTF8SVpv((char*) entity, 0)));
PUSHs(base ? sv_2mortal(newUTF8SVpv((char*) base, 0)) : &PL_sv_undef);
PUSHs(sv_2mortal(newUTF8SVpv((char*) sysid, 0)));
PUSHs(pubid ? sv_2mortal(newUTF8SVpv((char*) pubid, 0)) : &PL_sv_undef);
PUSHs(sv_2mortal(newUTF8SVpv((char*) notation, 0)));
PUTBACK;
perl_call_sv(cbv->unprsd_sv, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
} /* End unparsedEntityDecl */
static void
notationDecl(void *userData,
const char *name,
const char *base,
const char *sysid,
const char *pubid)
{
dSP;
CallbackVector* cbv = (CallbackVector*) userData;
ENTER;
SAVETMPS;
PUSHMARK(sp);
EXTEND(sp, 5);
PUSHs(cbv->self_sv);
PUSHs(sv_2mortal(newUTF8SVpv((char*) name, 0)));
PUSHs(base ? sv_2mortal(newUTF8SVpv((char *) base, 0)) : &PL_sv_undef);
PUSHs(sysid ? sv_2mortal(newUTF8SVpv((char *) sysid, 0)) : &PL_sv_undef);
PUSHs(pubid ? sv_2mortal(newUTF8SVpv((char *) pubid, 0)) : &PL_sv_undef);
PUTBACK;
perl_call_sv(cbv->notation_sv, G_DISCARD|G_VOID);
FREETMPS;
LEAVE;
} /* End notationDecl */
static int
externalEntityRef(XML_Parser parser,
const char* open,
const char* base,
const char* sysid,
const char* pubid)
{
dSP;
int count;
int ret = 0;
int parse_done = 0;
CallbackVector* cbv = (CallbackVector*) XML_GetUserData(parser);
/* For parameter entities and DTD (context is NULL per expat docs),
when the user did not explicitly request ParseParamEnt, silently
treat the PE as empty and let expat continue processing subsequent
DTD declarations normally. See GH #53. */
/* For parameter entities and DTD (context is NULL per expat docs),
when the user did not explicitly request ParseParamEnt, treat the
PE as empty content so expat continues dispatching subsequent DTD
declarations to their proper handlers (Attlist, Element, etc.).
We must actually create the sub-parser and feed it an empty document
rather than simply returning 1, because expat needs the sub-parser's
parse completion to finalize entity processing. Without this, expat
routes all subsequent DTD declarations to the Default handler instead
of the dedicated handlers. See GH #53 and GH #173. */
if (open == NULL && !cbv->parseparam) {
XML_Parser entpar = XML_ExternalEntityParserCreate(parser, open, 0);
if (entpar) {
XML_Parse(entpar, "", 0, 1);
XML_ParserFree(entpar);
}
return 1;
}
if (! cbv->extent_sv)
return 0;
ENTER ;
SAVETMPS ;
PUSHMARK(sp);
EXTEND(sp, pubid ? 4 : 3);
PUSHs(cbv->self_sv);
PUSHs(base ? sv_2mortal(newUTF8SVpv((char*) base, 0)) : &PL_sv_undef);
PUSHs(sysid ? sv_2mortal(newUTF8SVpv((char*) sysid, 0)) : &PL_sv_undef);
if (pubid)
PUSHs(sv_2mortal(newUTF8SVpv((char*) pubid, 0)));
PUTBACK ;
count = perl_call_sv(cbv->extent_sv, G_SCALAR);
SPAGAIN ;
if (count >= 1) {
SV * result = POPs;
int type;
if (result && (type = SvTYPE(result)) > 0) {
SV **pval = hv_fetch((HV*) SvRV(cbv->self_sv), "Parser", 6, 0);
if (! pval || ! SvIOK(*pval))
append_error(parser, "Can't find parser entry in XML::Parser object");
else {
XML_Parser entpar;
char *errmsg = (char *) 0;
entpar = XML_ExternalEntityParserCreate(parser, open, 0);
if (! entpar) {
append_error(parser,
"Couldn't create external entity sub-parser");
goto Extparse_Cleanup;
}
XML_SetBase(entpar, XML_GetBase(parser));
sv_setiv(*pval, PTR2IV(entpar));
cbv->p = entpar;
PUSHMARK(sp);
EXTEND(sp, 2);
PUSHs(*pval);
PUSHs(result);
PUTBACK;
count = perl_call_pv("XML::Parser::Expat::Do_External_Parse",
G_SCALAR | G_EVAL);
SPAGAIN;
if (SvTRUE(ERRSV)) {
char *hold;
STRLEN len;
(void)POPs;
hold = SvPV(ERRSV, len);
New(326, errmsg, len + 1, char);
if (len)
Copy(hold, errmsg, len, char);
errmsg[len] = '\0';
goto Extparse_Cleanup;
}
if (count > 0)
ret = POPi;
parse_done = 1;
Extparse_Cleanup:
cbv->p = parser;
sv_setiv(*pval, PTR2IV(parser));
XML_ParserFree(entpar);
if (cbv->extfin_sv) {
PUSHMARK(sp);
PUSHs(cbv->self_sv);
PUTBACK;
perl_call_sv(cbv->extfin_sv, G_DISCARD|G_VOID);
SPAGAIN;
}
/* Use saved error from Do_External_Parse if available,
since the extfin callback above may have cleared ERRSV */
if (errmsg)
append_error(parser, errmsg);
else if (SvTRUE(ERRSV))
append_error(parser, SvPV_nolen(ERRSV));
Safefree(errmsg);
}
}
}
if (! ret && ! parse_done)
append_error(parser, "Handler couldn't resolve external entity");
PUTBACK ;
FREETMPS ;
LEAVE ;
return ret;
} /* End externalEntityRef */
/*================================================================
** This is the function that expat calls to convert multi-byte sequences
** for external encodings. Each byte in the sequence is used to index
** into the current map to either set the next map or, in the case of
** the final byte, to get the corresponding Unicode scalar, which is
** returned.
*/
static int
convert_to_unicode(void *data, const char *seq) {
Encinfo *enc = (Encinfo *) data;
PrefixMap *curpfx;
int count;
int index = 0;
for (count = 0; count < 4; count++) {
unsigned char byte = (unsigned char) seq[count];
unsigned char bndx;
unsigned char bmsk;
int offset;
curpfx = &enc->prefixes[index];
offset = ((int) byte) - curpfx->min;
if (offset < 0)
break;
if (offset >= curpfx->len && curpfx->len != 0)
break;
bndx = byte >> 3;
bmsk = 1 << (byte & 0x7);
if (curpfx->ispfx[bndx] & bmsk) {
index = enc->bytemap[curpfx->bmap_start + offset];
}
else if (curpfx->ischar[bndx] & bmsk) {
return enc->bytemap[curpfx->bmap_start + offset];
}
else
break;
}
return -1;
} /* End convert_to_unicode */
static int
unknownEncoding(void *unused, const char *name, XML_Encoding *info)
{
SV ** encinfptr;
PERL_UNUSED_VAR(unused);
Encinfo *enc;
int namelen;
int i;
char buff[42];
namelen = strlen(name);
if (namelen > 40)
return 0;
/* Make uppercase */
for (i = 0; i < namelen; i++) {
char c = name[i];
if (c >= 'a' && c <= 'z')
c -= 'a' - 'A';
buff[i] = c;
}
if (! EncodingTable) {
EncodingTable = perl_get_hv("XML::Parser::Expat::Encoding_Table", FALSE);
if (! EncodingTable)
croak("Can't find XML::Parser::Expat::Encoding_Table");
}
encinfptr = hv_fetch(EncodingTable, buff, namelen, 0);
if (! encinfptr || ! SvOK(*encinfptr)) {
/* Not found, so try to autoload */
dSP;
ENTER;
SAVETMPS;
PUSHMARK(sp);
XPUSHs(sv_2mortal(newSVpvn(buff,namelen)));
PUTBACK;
perl_call_pv("XML::Parser::Expat::load_encoding", G_DISCARD|G_VOID);
encinfptr = hv_fetch(EncodingTable, buff, namelen, 0);
FREETMPS;
LEAVE;
if (! encinfptr || ! SvOK(*encinfptr))
return 0;
}
if (! sv_derived_from(*encinfptr, "XML::Parser::Encinfo"))
croak("Entry in XML::Parser::Expat::Encoding_Table not an Encinfo object");
enc = INT2PTR(Encinfo *, SvIV((SV*)SvRV(*encinfptr)));
Copy(enc->firstmap, info->map, 256, int);
info->release = NULL;
if (enc->prefixes_size) {
info->data = (void *) enc;
info->convert = convert_to_unicode;
}
else {
info->data = NULL;
info->convert = NULL;
}
return 1;
} /* End unknownEncoding */
static void
recString(void *userData, const char *string, int len)
{
CallbackVector *cbv = (CallbackVector*) userData;
if (cbv->recstring) {
sv_catpvn(cbv->recstring, (char *) string, len);
}
else {
cbv->recstring = newUTF8SVpvn((char *) string, len);
}
} /* End recString */
static void
suspend_callbacks(CallbackVector *cbv) {
if (SvTRUE(cbv->char_sv)) {
XML_SetCharacterDataHandler(cbv->p,
(XML_CharacterDataHandler) 0);
}
if (SvTRUE(cbv->proc_sv)) {
XML_SetProcessingInstructionHandler(cbv->p,
(XML_ProcessingInstructionHandler) 0);
}
if (SvTRUE(cbv->cmnt_sv)) {
XML_SetCommentHandler(cbv->p,
(XML_CommentHandler) 0);
}
if (SvTRUE(cbv->startcd_sv)
|| SvTRUE(cbv->endcd_sv)) {
XML_SetCdataSectionHandler(cbv->p,
(XML_StartCdataSectionHandler) 0,
(XML_EndCdataSectionHandler) 0);
}
if (SvTRUE(cbv->unprsd_sv)) {
XML_SetUnparsedEntityDeclHandler(cbv->p,
(XML_UnparsedEntityDeclHandler) 0);
}
if (SvTRUE(cbv->notation_sv)) {
XML_SetNotationDeclHandler(cbv->p,
(XML_NotationDeclHandler) 0);
}
if (SvTRUE(cbv->extent_sv)) {
XML_SetExternalEntityRefHandler(cbv->p,
(XML_ExternalEntityRefHandler) 0);
}
} /* End suspend_callbacks */
static void
resume_callbacks(CallbackVector *cbv) {
if (SvTRUE(cbv->char_sv)) {
XML_SetCharacterDataHandler(cbv->p, characterData);
}
if (SvTRUE(cbv->proc_sv)) {
XML_SetProcessingInstructionHandler(cbv->p, processingInstruction);
}
if (SvTRUE(cbv->cmnt_sv)) {
XML_SetCommentHandler(cbv->p, commenthandle);
}
if (SvTRUE(cbv->startcd_sv)
|| SvTRUE(cbv->endcd_sv)) {
XML_SetCdataSectionHandler(cbv->p, startCdata, endCdata);
}
if (SvTRUE(cbv->unprsd_sv)) {
XML_SetUnparsedEntityDeclHandler(cbv->p, unparsedEntityDecl);
}
if (SvTRUE(cbv->notation_sv)) {
XML_SetNotationDeclHandler(cbv->p, notationDecl);
}
if (SvTRUE(cbv->extent_sv)) {
XML_SetExternalEntityRefHandler(cbv->p, externalEntityRef);
}
} /* End resume_callbacks */
MODULE = XML::Parser::Expat PACKAGE = XML::Parser::Expat PREFIX = XML_
XML_Parser
XML_ParserCreate(self_sv, enc_sv, namespaces)
SV * self_sv
SV * enc_sv
int namespaces
CODE:
{
CallbackVector *cbv;
char *enc = (char *) (SvTRUE(enc_sv) ? SvPV_nolen(enc_sv) : 0);
SV ** spp;
Newz(320, cbv, 1, CallbackVector);
cbv->self_sv = SvREFCNT_inc(self_sv);
Newz(325, cbv->st_serial_stack, 1024, unsigned int);
cbv->st_serial_stacksize = 1024;
spp = hv_fetch((HV*)SvRV(cbv->self_sv), "NoExpand", 8, 0);
if (spp && SvTRUE(*spp))
cbv->no_expand = 1;
spp = hv_fetch((HV*)SvRV(cbv->self_sv), "Context", 7, 0);
if (! spp || ! *spp || !SvROK(*spp)) {
free_cbv(cbv);
croak("XML::Parser instance missing Context");
}
cbv->context = (AV*) SvRV(*spp);
cbv->ns = (unsigned) namespaces;
if (namespaces)
{
spp = hv_fetch((HV*)SvRV(cbv->self_sv), "New_Prefixes", 12, 0);
if (! spp || ! *spp || !SvROK(*spp)) {
free_cbv(cbv);
croak("XML::Parser instance missing New_Prefixes");
}
cbv->new_prefix_list = (AV *) SvRV(*spp);
spp = hv_fetch((HV*)SvRV(cbv->self_sv), "Namespace_Table",
15, FALSE);
if (! spp || ! *spp || !SvROK(*spp)) {
free_cbv(cbv);
croak("XML::Parser instance missing Namespace_Table");
}
cbv->nstab = (HV *) SvRV(*spp);
spp = hv_fetch((HV*)SvRV(cbv->self_sv), "Namespace_List",
14, FALSE);
if (! spp || ! *spp || !SvROK(*spp)) {
free_cbv(cbv);
croak("XML::Parser instance missing Namespace_List");
}
cbv->nslst = (AV *) SvRV(*spp);
RETVAL = XML_ParserCreate_MM(enc, &ms, nsdelim);
XML_SetNamespaceDeclHandler(RETVAL,nsStart, nsEnd);
}
else
{
RETVAL = XML_ParserCreate_MM(enc, &ms, NULL);
}
cbv->p = RETVAL;
XML_SetUserData(RETVAL, (void *) cbv);
XML_SetElementHandler(RETVAL, startElement, endElement);
XML_SetUnknownEncodingHandler(RETVAL, unknownEncoding, 0);
XML_SetExternalEntityRefHandler(RETVAL, externalEntityRef);
spp = hv_fetch((HV*)SvRV(cbv->self_sv), "ParseParamEnt",
13, FALSE);
if (spp && SvTRUE(*spp))
cbv->parseparam = 1;
/* Always enable parameter entity parsing so that PE references
in the internal DTD subset don't cause expat to stop calling
specific declaration handlers (Attlist, Element, Entity, etc.).
When ParseParamEnt is not explicitly set, unresolvable PEs are
silently treated as empty in externalEntityRef(). */
XML_SetParamEntityParsing(RETVAL,
XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE);
spp = hv_fetch((HV*)SvRV(cbv->self_sv), "UseForeignDTD",
13, FALSE);
if (spp && SvTRUE(*spp))
XML_UseForeignDTD(RETVAL, XML_TRUE);
}
OUTPUT:
RETVAL
void
XML_ParserRelease(parser)
XML_Parser parser
CODE:
{
CallbackVector * cbv = (CallbackVector *) XML_GetUserData(parser);
SvREFCNT_dec(cbv->self_sv);
}
void
XML_ParserFree(parser)
XML_Parser parser
CODE:
{
CallbackVector * cbv = (CallbackVector *) XML_GetUserData(parser);
Safefree(cbv->st_serial_stack);
/* Clean up any SVs that we have */
/* (Note that self_sv must already be taken care of
or we couldn't be here */
if (cbv->recstring)
SvREFCNT_dec(cbv->recstring);
if (cbv->start_sv)
SvREFCNT_dec(cbv->start_sv);
if (cbv->end_sv)
SvREFCNT_dec(cbv->end_sv);
if (cbv->char_sv)
SvREFCNT_dec(cbv->char_sv);
if (cbv->proc_sv)
SvREFCNT_dec(cbv->proc_sv);
if (cbv->cmnt_sv)
SvREFCNT_dec(cbv->cmnt_sv);
if (cbv->dflt_sv)
SvREFCNT_dec(cbv->dflt_sv);
if (cbv->entdcl_sv)
SvREFCNT_dec(cbv->entdcl_sv);
if (cbv->eledcl_sv)
SvREFCNT_dec(cbv->eledcl_sv);
if (cbv->attdcl_sv)
SvREFCNT_dec(cbv->attdcl_sv);
if (cbv->doctyp_sv)
SvREFCNT_dec(cbv->doctyp_sv);
if (cbv->doctypfin_sv)
SvREFCNT_dec(cbv->doctypfin_sv);
if (cbv->xmldec_sv)
SvREFCNT_dec(cbv->xmldec_sv);
if (cbv->unprsd_sv)
SvREFCNT_dec(cbv->unprsd_sv);
if (cbv->notation_sv)
SvREFCNT_dec(cbv->notation_sv);
if (cbv->extent_sv)
SvREFCNT_dec(cbv->extent_sv);
if (cbv->extfin_sv)
SvREFCNT_dec(cbv->extfin_sv);
if (cbv->startcd_sv)
SvREFCNT_dec(cbv->startcd_sv);
if (cbv->endcd_sv)
SvREFCNT_dec(cbv->endcd_sv);
/* ================ */
Safefree(cbv);
XML_ParserFree(parser);
}
int
XML_ParseString(parser, sv)
XML_Parser parser
SV * sv
CODE:
{
STRLEN len;
char *s = SvPV(sv, len);
RETVAL = XML_Parse(parser, s, len, 1);
SPAGAIN; /* XML_Parse might have changed stack pointer */
if (! RETVAL)
append_error(parser, NULL);
}
OUTPUT:
RETVAL
int
XML_ParseStream(parser, ioref, delim)
XML_Parser parser
SV * ioref
SV * delim
CODE:
{
CallbackVector * cbv;
cbv = (CallbackVector *) XML_GetUserData(parser);
if (SvOK(delim)) {
cbv->delim = SvPV(delim, cbv->delimlen);
}
else {
cbv->delim = (char *) 0;
}
RETVAL = parse_stream(parser, ioref);
SPAGAIN; /* parse_stream might have changed stack pointer */
}
OUTPUT:
RETVAL
int
XML_ParsePartial(parser, sv)
XML_Parser parser
SV * sv
CODE:
{
STRLEN len;
char *s = SvPV(sv, len);
RETVAL = XML_Parse(parser, s, len, 0);
if (! RETVAL)
append_error(parser, NULL);
}
OUTPUT:
RETVAL
int
XML_ParseDone(parser)
XML_Parser parser
CODE:
{
RETVAL = XML_Parse(parser, "", 0, 1);
if (! RETVAL)
append_error(parser, NULL);
}
OUTPUT:
RETVAL
SV *
XML_SetStartElementHandler(parser, start_sv)
XML_Parser parser
SV * start_sv
CODE:
{
CallbackVector * cbv = (CallbackVector*) XML_GetUserData(parser);
XMLP_UPD(start_sv);
PUSHRET;
}
SV *
XML_SetEndElementHandler(parser, end_sv)
XML_Parser parser
SV * end_sv
CODE:
{
CallbackVector *cbv = (CallbackVector*) XML_GetUserData(parser);
XMLP_UPD(end_sv);
PUSHRET;
}
SV *
XML_SetCharacterDataHandler(parser, char_sv)
XML_Parser parser
SV * char_sv
CODE:
{
XML_CharacterDataHandler charhndl = (XML_CharacterDataHandler) 0;
CallbackVector * cbv = (CallbackVector*) XML_GetUserData(parser);
XMLP_UPD(char_sv);
if (SvTRUE(char_sv))
charhndl = characterData;
XML_SetCharacterDataHandler(parser, charhndl);
PUSHRET;
}
SV *
XML_SetProcessingInstructionHandler(parser, proc_sv)
XML_Parser parser
SV * proc_sv
CODE:
{
XML_ProcessingInstructionHandler prochndl =
(XML_ProcessingInstructionHandler) 0;
CallbackVector* cbv = (CallbackVector*) XML_GetUserData(parser);
XMLP_UPD(proc_sv);
if (SvTRUE(proc_sv))
prochndl = processingInstruction;
XML_SetProcessingInstructionHandler(parser, prochndl);
PUSHRET;
}
SV *
XML_SetCommentHandler(parser, cmnt_sv)
XML_Parser parser
SV * cmnt_sv
CODE:
{
XML_CommentHandler cmnthndl = (XML_CommentHandler) 0;
CallbackVector * cbv = (CallbackVector*) XML_GetUserData(parser);
XMLP_UPD(cmnt_sv);
if (SvTRUE(cmnt_sv))
cmnthndl = commenthandle;
XML_SetCommentHandler(parser, cmnthndl);
PUSHRET;
}
SV *
XML_SetDefaultHandler(parser, dflt_sv)
XML_Parser parser
SV * dflt_sv
CODE:
{
XML_DefaultHandler dflthndl = (XML_DefaultHandler) 0;
CallbackVector * cbv = (CallbackVector*) XML_GetUserData(parser);
XMLP_UPD(dflt_sv);
if (SvTRUE(dflt_sv))
dflthndl = defaulthandle;
if (cbv->no_expand)
XML_SetDefaultHandler(parser, dflthndl);
else
XML_SetDefaultHandlerExpand(parser, dflthndl);
PUSHRET;
}
SV *
XML_SetUnparsedEntityDeclHandler(parser, unprsd_sv)
XML_Parser parser
SV * unprsd_sv
CODE:
{
XML_UnparsedEntityDeclHandler unprsdhndl =
(XML_UnparsedEntityDeclHandler) 0;
CallbackVector * cbv = (CallbackVector*) XML_GetUserData(parser);
XMLP_UPD(unprsd_sv);
if (SvTRUE(unprsd_sv))
unprsdhndl = unparsedEntityDecl;
XML_SetUnparsedEntityDeclHandler(parser, unprsdhndl);
PUSHRET;
}
SV *
XML_SetNotationDeclHandler(parser, notation_sv)
XML_Parser parser
SV * notation_sv
CODE:
{
XML_NotationDeclHandler nothndlr = (XML_NotationDeclHandler) 0;
CallbackVector * cbv = (CallbackVector*) XML_GetUserData(parser);
XMLP_UPD(notation_sv);
if (SvTRUE(notation_sv))
nothndlr = notationDecl;
XML_SetNotationDeclHandler(parser, nothndlr);
PUSHRET;
}
SV *
XML_SetExternalEntityRefHandler(parser, extent_sv)
XML_Parser parser
SV * extent_sv
CODE:
{
XML_ExternalEntityRefHandler exthndlr =
(XML_ExternalEntityRefHandler) 0;
CallbackVector * cbv = (CallbackVector*) XML_GetUserData(parser);
XMLP_UPD(extent_sv);
if (SvTRUE(extent_sv))
exthndlr = externalEntityRef;
XML_SetExternalEntityRefHandler(parser, exthndlr);
PUSHRET;
}
SV *
XML_SetExtEntFinishHandler(parser, extfin_sv)
XML_Parser parser
SV * extfin_sv
CODE:
{
CallbackVector * cbv = (CallbackVector *) XML_GetUserData(parser);
/* There is no corresponding handler for this in expat. This is
called from the externalEntityRef function above after parsing
the external entity. */
XMLP_UPD(extfin_sv);
PUSHRET;
}
SV *
XML_SetEntityDeclHandler(parser, entdcl_sv)
XML_Parser parser
SV * entdcl_sv
CODE:
{
XML_EntityDeclHandler enthndlr =
(XML_EntityDeclHandler) 0;
CallbackVector * cbv = (CallbackVector*) XML_GetUserData(parser);
XMLP_UPD(entdcl_sv);
if (SvTRUE(entdcl_sv))
enthndlr = entityDecl;
XML_SetEntityDeclHandler(parser, enthndlr);
PUSHRET;
}
SV *
XML_SetElementDeclHandler(parser, eledcl_sv)
XML_Parser parser
SV * eledcl_sv
CODE:
{
XML_ElementDeclHandler eldeclhndlr =
(XML_ElementDeclHandler) 0;
CallbackVector * cbv = (CallbackVector*) XML_GetUserData(parser);
XMLP_UPD(eledcl_sv);
if (SvTRUE(eledcl_sv))
eldeclhndlr = elementDecl;
XML_SetElementDeclHandler(parser, eldeclhndlr);
PUSHRET;
}
SV *
XML_SetAttListDeclHandler(parser, attdcl_sv)
XML_Parser parser
SV * attdcl_sv
CODE:
{
XML_AttlistDeclHandler attdeclhndlr =
(XML_AttlistDeclHandler) 0;
CallbackVector * cbv = (CallbackVector*) XML_GetUserData(parser);
XMLP_UPD(attdcl_sv);
if (SvTRUE(attdcl_sv))
attdeclhndlr = attributeDecl;
XML_SetAttlistDeclHandler(parser, attdeclhndlr);
PUSHRET;
}
SV *
XML_SetDoctypeHandler(parser, doctyp_sv)
XML_Parser parser
SV * doctyp_sv
CODE:
{
XML_StartDoctypeDeclHandler dtsthndlr =
(XML_StartDoctypeDeclHandler) 0;
CallbackVector * cbv = (CallbackVector*) XML_GetUserData(parser);
XMLP_UPD(doctyp_sv);
if (SvTRUE(doctyp_sv))
dtsthndlr = doctypeStart;
XML_SetStartDoctypeDeclHandler(parser, dtsthndlr);
PUSHRET;
}
SV *
XML_SetEndDoctypeHandler(parser, doctypfin_sv)
XML_Parser parser
SV * doctypfin_sv
CODE:
{
XML_EndDoctypeDeclHandler dtendhndlr =
(XML_EndDoctypeDeclHandler) 0;
CallbackVector * cbv = (CallbackVector*) XML_GetUserData(parser);
XMLP_UPD(doctypfin_sv);
if (SvTRUE(doctypfin_sv))
dtendhndlr = doctypeEnd;
XML_SetEndDoctypeDeclHandler(parser, dtendhndlr);
PUSHRET;
}
SV *
XML_SetXMLDeclHandler(parser, xmldec_sv)
XML_Parser parser
SV * xmldec_sv
CODE:
{
XML_XmlDeclHandler xmldechndlr =
(XML_XmlDeclHandler) 0;
CallbackVector * cbv = (CallbackVector *) XML_GetUserData(parser);
XMLP_UPD(xmldec_sv);
if (SvTRUE(xmldec_sv))
xmldechndlr = xmlDecl;
XML_SetXmlDeclHandler(parser, xmldechndlr);
PUSHRET;
}
void
XML_SetBase(parser, base)
XML_Parser parser
SV * base
CODE:
{
char * b;
if (! SvOK(base)) {
b = (char *) 0;
}
else {
b = SvPV_nolen(base);
}
XML_SetBase(parser, b);
}
void
XML_GetBase(parser)
XML_Parser parser
PPCODE:
{
const char *ret = XML_GetBase(parser);
if (ret) {
XPUSHs(sv_2mortal(newSVpv(ret, 0)));
}
else {
XPUSHs(&PL_sv_undef);
}
}
void
XML_PositionContext(parser, lines)
XML_Parser parser
int lines
PREINIT:
int parsepos;
int size;
const char *pos = XML_GetInputContext(parser, &parsepos, &size);
const char *markbeg;
const char *limit;
const char *markend;
int length, relpos;
int cnt;
PPCODE:
if (! pos)
return;
for (markbeg = &pos[parsepos], cnt = 0; markbeg >= pos; markbeg--)
{
if (*markbeg == '\n')
{
cnt++;
if (cnt > lines)
break;
}
}
markbeg++;
relpos = 0;
limit = &pos[size];
for (markend = &pos[parsepos + 1], cnt = 0;
markend < limit;
markend++)
{
if (*markend == '\n')
{
if (cnt == 0)
relpos = (markend - markbeg) + 1;
cnt++;
if (cnt > lines)
{
markend++;
break;
}
}
}
length = markend - markbeg;
if (relpos == 0)
relpos = length;
EXTEND(sp, 2);
PUSHs(sv_2mortal(newSVpvn((char *) markbeg, length)));
PUSHs(sv_2mortal(newSViv(relpos)));
SV *
GenerateNSName(name, xml_namespace, table, list)
SV * name
SV * xml_namespace
SV * table
SV * list
CODE:
{
STRLEN nmlen, nslen;
char * nmstr;
char * nsstr;
char * buff;
char * bp;
char * blim;
nmstr = SvPV(name, nmlen);
nsstr = SvPV(xml_namespace, nslen);
/* Form a namespace-name string that looks like expat's */
New(321, buff, nmlen + nslen + 2, char);
bp = buff;
blim = bp + nslen;
while (bp < blim)
*bp++ = *nsstr++;
*bp++ = NSDELIM;
blim = bp + nmlen;
while (bp < blim)
*bp++ = *nmstr++;
*bp = '\0';
RETVAL = gen_ns_name(buff, (HV *) SvRV(table), (AV *) SvRV(list));
Safefree(buff);
}
OUTPUT:
RETVAL
void
XML_DefaultCurrent(parser)
XML_Parser parser
CODE:
{
XML_DefaultCurrent(parser);
}
SV *
XML_RecognizedString(parser)
XML_Parser parser
CODE:
{
XML_DefaultHandler dflthndl = (XML_DefaultHandler) 0;
CallbackVector * cbv = (CallbackVector *) XML_GetUserData(parser);
if (cbv->dflt_sv) {
dflthndl = defaulthandle;
}
if (cbv->recstring) {
sv_setpvn(cbv->recstring, "", 0);
}
if (cbv->no_expand)
XML_SetDefaultHandler(parser, recString);
else
XML_SetDefaultHandlerExpand(parser, recString);
XML_DefaultCurrent(parser);
if (cbv->no_expand)
XML_SetDefaultHandler(parser, dflthndl);
else
XML_SetDefaultHandlerExpand(parser, dflthndl);
RETVAL = newSVsv(cbv->recstring);
}
OUTPUT:
RETVAL
int
XML_GetErrorCode(parser)
XML_Parser parser
SV *
XML_GetCurrentLineNumber(parser)
XML_Parser parser
CODE:
{
XML_Size line = XML_GetCurrentLineNumber(parser);
#if (defined(XML_LARGE_SIZE) && UVSIZE < 8)
/* XML_Size is unsigned long long but UV is 32-bit; use NV */
RETVAL = newSVnv((NV)line);
#else
RETVAL = newSVuv((UV)line);
#endif
}
OUTPUT:
RETVAL
SV *
XML_GetCurrentColumnNumber(parser)
XML_Parser parser
CODE:
{
XML_Size col = XML_GetCurrentColumnNumber(parser);
#if (defined(XML_LARGE_SIZE) && UVSIZE < 8)
/* XML_Size is unsigned long long but UV is 32-bit; use NV */
RETVAL = newSVnv((NV)col);
#else
RETVAL = newSVuv((UV)col);
#endif
}
OUTPUT:
RETVAL
SV *
XML_GetCurrentByteIndex(parser)
XML_Parser parser
CODE:
{
XML_Index byte_index = XML_GetCurrentByteIndex(parser);
#if (defined(XML_LARGE_SIZE) && IVSIZE < 8)
/* XML_Index is long long but IV is 32-bit; use NV to avoid overflow */
RETVAL = newSVnv((NV)byte_index);
#else
RETVAL = newSViv((IV)byte_index);
#endif
}
OUTPUT:
RETVAL
int
XML_GetCurrentByteCount(parser)
XML_Parser parser
int
XML_GetSpecifiedAttributeCount(parser)
XML_Parser parser
void
XML_ErrorString(code)
int code
PPCODE:
{
const char *ret = XML_ErrorString(code);
XPUSHs(sv_2mortal(newSVpv(ret, 0)));
}
void
XML_ExpatVersion()
PPCODE:
const XML_LChar *ver = XML_ExpatVersion();
XPUSHs(sv_2mortal(newSVpv((const char *)ver, 0)));
void
XML_ExpatVersionInfo()
PPCODE:
XML_Expat_Version info = XML_ExpatVersionInfo();
EXTEND(SP, 6);
PUSHs(sv_2mortal(newSVpv("major", 5)));
PUSHs(sv_2mortal(newSViv(info.major)));
PUSHs(sv_2mortal(newSVpv("minor", 5)));
PUSHs(sv_2mortal(newSViv(info.minor)));
PUSHs(sv_2mortal(newSVpv("micro", 5)));
PUSHs(sv_2mortal(newSViv(info.micro)));
SV *
XML_LoadEncoding(data, size)
char * data
int size
CODE:
{
Encmap_Header *emh = (Encmap_Header *) data;
unsigned pfxsize, bmsize;
if (size < sizeof(Encmap_Header)
|| ntohl(emh->magic) != ENCMAP_MAGIC) {
RETVAL = &PL_sv_undef;
}
else {
Encinfo *entry;
SV *sv;
PrefixMap *pfx;
unsigned short *bm;
int namelen;
int i;
pfxsize = ntohs(emh->pfsize);
bmsize = ntohs(emh->bmsize);
if (size != (sizeof(Encmap_Header)
+ pfxsize * sizeof(PrefixMap)
+ bmsize * sizeof(unsigned short))) {
RETVAL = &PL_sv_undef;
}
else {
/* Convert to uppercase and get name length */
for (i = 0; i < sizeof(emh->name); i++) {
char c = emh->name[i];
if (c == (char) 0)
break;
if (c >= 'a' && c <= 'z')
emh->name[i] -= 'a' - 'A';
}
namelen = i;
RETVAL = newSVpvn(emh->name, namelen);
New(322, entry, 1, Encinfo);
entry->prefixes_size = pfxsize;
entry->bytemap_size = bmsize;
for (i = 0; i < 256; i++) {
entry->firstmap[i] = ntohl(emh->map[i]);
}
pfx = (PrefixMap *) &data[sizeof(Encmap_Header)];
bm = (unsigned short *) (((char *) pfx)
+ sizeof(PrefixMap) * pfxsize);
New(323, entry->prefixes, pfxsize, PrefixMap);
New(324, entry->bytemap, bmsize, unsigned short);
for (i = 0; i < pfxsize; i++, pfx++) {
PrefixMap *dest = &entry->prefixes[i];
dest->min = pfx->min;
dest->len = pfx->len;
dest->bmap_start = ntohs(pfx->bmap_start);
Copy(pfx->ispfx, dest->ispfx,
sizeof(pfx->ispfx) + sizeof(pfx->ischar), unsigned char);
}
for (i = 0; i < bmsize; i++)
entry->bytemap[i] = ntohs(bm[i]);
sv = newSViv(0);
sv_setref_pv(sv, "XML::Parser::Encinfo", (void *) entry);
if (! EncodingTable) {
EncodingTable
= perl_get_hv("XML::Parser::Expat::Encoding_Table",
FALSE);
if (! EncodingTable)
croak("Can't find XML::Parser::Expat::Encoding_Table");
}
hv_store(EncodingTable, emh->name, namelen, sv, 0);
}
}
}
OUTPUT:
RETVAL
void
XML_FreeEncoding(enc)
Encinfo * enc
CODE:
Safefree(enc->bytemap);
Safefree(enc->prefixes);
Safefree(enc);
SV *
XML_OriginalString(parser)
XML_Parser parser
CODE:
{
int parsepos, size;
const char *buff = XML_GetInputContext(parser, &parsepos, &size);
if (buff) {
RETVAL = newSVpvn((char *) &buff[parsepos],
XML_GetCurrentByteCount(parser));
}
else {
RETVAL = newSVpv("", 0);
}
}
OUTPUT:
RETVAL
SV *
XML_SetStartCdataHandler(parser, startcd_sv)
XML_Parser parser
SV * startcd_sv
CODE:
{
CallbackVector * cbv = (CallbackVector *) XML_GetUserData(parser);
XML_StartCdataSectionHandler scdhndl =
(XML_StartCdataSectionHandler) 0;
XMLP_UPD(startcd_sv);
if (SvTRUE(startcd_sv))
scdhndl = startCdata;
XML_SetStartCdataSectionHandler(parser, scdhndl);
PUSHRET;
}
SV *
XML_SetEndCdataHandler(parser, endcd_sv)
XML_Parser parser
SV * endcd_sv
CODE:
{
CallbackVector * cbv = (CallbackVector *) XML_GetUserData(parser);
XML_EndCdataSectionHandler ecdhndl =
(XML_EndCdataSectionHandler) 0;
XMLP_UPD(endcd_sv);
if (SvTRUE(endcd_sv))
ecdhndl = endCdata;
XML_SetEndCdataSectionHandler(parser, ecdhndl);
PUSHRET;
}
void
XML_UnsetAllHandlers(parser)
XML_Parser parser
CODE:
{
CallbackVector * cbv = (CallbackVector *) XML_GetUserData(parser);
suspend_callbacks(cbv);
if (cbv->ns) {
XML_SetNamespaceDeclHandler(cbv->p,
(XML_StartNamespaceDeclHandler) 0,
(XML_EndNamespaceDeclHandler) 0);
}
XML_SetElementHandler(parser,
(XML_StartElementHandler) 0,
(XML_EndElementHandler) 0);
XML_SetUnknownEncodingHandler(parser,
(XML_UnknownEncodingHandler) 0,
(void *) 0);
}
int
XML_ElementIndex(parser)
XML_Parser parser
CODE:
{
CallbackVector * cbv = (CallbackVector *) XML_GetUserData(parser);
RETVAL = cbv->st_serial_stack[cbv->st_serial_stackptr];
}
OUTPUT:
RETVAL
void
XML_SkipUntil(parser, index)
XML_Parser parser
unsigned int index
CODE:
{
CallbackVector * cbv = (CallbackVector *) XML_GetUserData(parser);
if (index <= cbv->st_serial)
return;
cbv->skip_until = index;
suspend_callbacks(cbv);
}
int
XML_Do_External_Parse(parser, result)
XML_Parser parser
SV * result
CODE:
{
RETVAL = 0;
if (SvROK(result) && SvOBJECT(SvRV(result))) {
RETVAL = parse_stream(parser, result);
}
else if (SvROK(result) && isGV(SvRV(result))) {
/* Lexical filehandle (open my $fh) - a reference to a glob */
RETVAL = parse_stream(parser,
sv_2mortal(newRV((SV*) GvIOp((GV*)SvRV(result)))));
}
else if (isGV(result)) {
RETVAL = parse_stream(parser,
sv_2mortal(newRV((SV*) GvIOp(result))));
}
else if (SvPOK(result)) {
STRLEN eslen;
char *entstr = SvPV(result, eslen);
RETVAL = XML_Parse(parser, entstr, eslen, 1);
}
}
OUTPUT:
RETVAL
#if (defined(XML_DTD) || (defined(XML_GE) && XML_GE == 1)) \
&& defined(XML_MAJOR_VERSION) \
&& (XML_MAJOR_VERSION > 2 \
|| (XML_MAJOR_VERSION == 2 && XML_MINOR_VERSION >= 4))
int
XML_SetBillionLaughsAttackProtectionMaximumAmplification(parser, maxamp)
XML_Parser parser
float maxamp
CODE:
RETVAL = (int) XML_SetBillionLaughsAttackProtectionMaximumAmplification(
parser, maxamp);
OUTPUT:
RETVAL
int
XML_SetBillionLaughsAttackProtectionActivationThreshold(parser, threshold)
XML_Parser parser
unsigned long threshold
CODE:
RETVAL = (int) XML_SetBillionLaughsAttackProtectionActivationThreshold(
parser, (unsigned long long) threshold);
OUTPUT:
RETVAL
#endif
#ifdef HAVE_XML_SETALLOCTRACKER
int
XML_SetAllocTrackerMaximumAmplification(parser, maxamp)
XML_Parser parser
float maxamp
CODE:
RETVAL = (int) XML_SetAllocTrackerMaximumAmplification(
parser, maxamp);
OUTPUT:
RETVAL
int
XML_SetAllocTrackerActivationThreshold(parser, threshold)
XML_Parser parser
unsigned long threshold
CODE:
RETVAL = (int) XML_SetAllocTrackerActivationThreshold(
parser, (unsigned long long) threshold);
OUTPUT:
RETVAL
#endif
#if defined(XML_MAJOR_VERSION) \
&& (XML_MAJOR_VERSION > 2 \
|| (XML_MAJOR_VERSION == 2 \
&& (XML_MINOR_VERSION > 6 \
|| (XML_MINOR_VERSION == 6 && XML_MICRO_VERSION >= 0))))
int
XML_SetReparseDeferralEnabled(parser, enabled)
XML_Parser parser
int enabled
CODE:
RETVAL = (int) XML_SetReparseDeferralEnabled(parser,
(XML_Bool) enabled);
OUTPUT:
RETVAL
#endif