1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2024-10-27 04:55:04 +03:00
libxml2/parser.c
Daniel Veillard 9f7b84bb07 preparing for a 2.4.3 release even if it may not be ready yet redirected
* 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
2001-08-23 15:31:19 +00:00

10173 lines
274 KiB
C

/*
* parser.c : an XML 1.0 parser, namespaces and validity support are mostly
* implemented on top of the SAX interfaces
*
* References:
* The XML specification:
* http://www.w3.org/TR/REC-xml
* Original 1.0 version:
* http://www.w3.org/TR/1998/REC-xml-19980210
* XML second edition working draft
* http://www.w3.org/TR/2000/WD-xml-2e-20000814
*
* Okay this is a big file, the parser core is around 7000 lines, then it
* is followed by the progressive parser top routines, then the various
* high level APIs to call the parser and a few miscelaneous functions.
* A number of helper functions and deprecated ones have been moved to
* parserInternals.c to reduce this file size.
* As much as possible the functions are associated with their relative
* production in the XML specification. A few productions defining the
* different ranges of character are actually implanted either in
* parserInternals.h or parserInternals.c
* The DOM tree build is realized from the default SAX callbacks in
* the module SAX.c.
* The routines doing the validation checks are in valid.c and called either
* from the SAx callbacks or as standalones functions using a preparsed
* document.
*
* See Copyright for the status of this software.
*
* daniel@veillard.com
*/
#include "libxml.h"
#ifdef WIN32
#define XML_DIR_SEP '\\'
#else
#define XML_DIR_SEP '/'
#endif
#include <stdlib.h>
#include <string.h>
#include <libxml/xmlmemory.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/parserInternals.h>
#include <libxml/valid.h>
#include <libxml/entities.h>
#include <libxml/xmlerror.h>
#include <libxml/encoding.h>
#include <libxml/xmlIO.h>
#include <libxml/uri.h>
#ifdef LIBXML_CATALOG_ENABLED
#include <libxml/catalog.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.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_ZLIB_H
#include <zlib.h>
#endif
#define XML_PARSER_BIG_BUFFER_SIZE 300
#define XML_PARSER_BUFFER_SIZE 100
/*
* Various global defaults for parsing
*/
int xmlParserDebugEntities = 0;
/*
* List of XML prefixed PI allowed by W3C specs
*/
const char *xmlW3CPIs[] = {
"xml-stylesheet",
NULL
};
/* DEPR void xmlParserHandleReference(xmlParserCtxtPtr ctxt); */
void xmlParserHandlePEReference(xmlParserCtxtPtr ctxt);
xmlEntityPtr xmlParseStringPEReference(xmlParserCtxtPtr ctxt,
const xmlChar **str);
static int
xmlParseExternalEntityPrivate(xmlDocPtr doc, xmlParserCtxtPtr oldctxt,
xmlSAXHandlerPtr sax,
void *user_data, int depth, const xmlChar *URL,
const xmlChar *ID, xmlNodePtr *list);
/************************************************************************
* *
* Parser stacks related functions and macros *
* *
************************************************************************/
xmlEntityPtr xmlParseStringEntityRef(xmlParserCtxtPtr ctxt,
const xmlChar ** str);
/*
* Generic function for accessing stacks in the Parser Context
*/
#define PUSH_AND_POP(scope, type, name) \
scope int name##Push(xmlParserCtxtPtr ctxt, type value) { \
if (ctxt->name##Nr >= ctxt->name##Max) { \
ctxt->name##Max *= 2; \
ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
if (ctxt->name##Tab == NULL) { \
xmlGenericError(xmlGenericErrorContext, \
"realloc failed !\n"); \
return(0); \
} \
} \
ctxt->name##Tab[ctxt->name##Nr] = value; \
ctxt->name = value; \
return(ctxt->name##Nr++); \
} \
scope type name##Pop(xmlParserCtxtPtr ctxt) { \
type ret; \
if (ctxt->name##Nr <= 0) return(0); \
ctxt->name##Nr--; \
if (ctxt->name##Nr > 0) \
ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
else \
ctxt->name = NULL; \
ret = ctxt->name##Tab[ctxt->name##Nr]; \
ctxt->name##Tab[ctxt->name##Nr] = 0; \
return(ret); \
} \
/**
* inputPop:
* @ctxt: an XML parser context
*
* Pops the top parser input from the input stack
*
* Returns the input just removed
*/
/**
* inputPush:
* @ctxt: an XML parser context
* @input: the parser input
*
* Pushes a new parser input on top of the input stack
*/
/**
* namePop:
* @ctxt: an XML parser context
*
* Pops the top element name from the name stack
*
* Returns the name just removed
*/
/**
* namePush:
* @ctxt: an XML parser context
* @name: the element name
*
* Pushes a new element name on top of the name stack
*/
/**
* nodePop:
* @ctxt: an XML parser context
*
* Pops the top element node from the node stack
*
* Returns the node just removed
*/
/**
* nodePush:
* @ctxt: an XML parser context
* @node: the element node
*
* Pushes a new element node on top of the node stack
*/
/*
* Those macros actually generate the functions
*/
PUSH_AND_POP(extern, xmlParserInputPtr, input)
PUSH_AND_POP(extern, xmlNodePtr, node)
PUSH_AND_POP(extern, xmlChar*, name)
static int spacePush(xmlParserCtxtPtr ctxt, int val) {
if (ctxt->spaceNr >= ctxt->spaceMax) {
ctxt->spaceMax *= 2;
ctxt->spaceTab = (int *) xmlRealloc(ctxt->spaceTab,
ctxt->spaceMax * sizeof(ctxt->spaceTab[0]));
if (ctxt->spaceTab == NULL) {
xmlGenericError(xmlGenericErrorContext,
"realloc failed !\n");
return(0);
}
}
ctxt->spaceTab[ctxt->spaceNr] = val;
ctxt->space = &ctxt->spaceTab[ctxt->spaceNr];
return(ctxt->spaceNr++);
}
static int spacePop(xmlParserCtxtPtr ctxt) {
int ret;
if (ctxt->spaceNr <= 0) return(0);
ctxt->spaceNr--;
if (ctxt->spaceNr > 0)
ctxt->space = &ctxt->spaceTab[ctxt->spaceNr - 1];
else
ctxt->space = NULL;
ret = ctxt->spaceTab[ctxt->spaceNr];
ctxt->spaceTab[ctxt->spaceNr] = -1;
return(ret);
}
/*
* Macros for accessing the content. Those should be used only by the parser,
* and not exported.
*
* Dirty macros, i.e. one often need to make assumption on the context to
* use them
*
* CUR_PTR return the current pointer to the xmlChar to be parsed.
* To be used with extreme caution since operations consuming
* characters may move the input buffer to a different location !
* CUR returns the current xmlChar value, i.e. a 8 bit value if compiled
* This should be used internally by the parser
* only to compare to ASCII values otherwise it would break when
* running with UTF-8 encoding.
* RAW same as CUR but in the input buffer, bypass any token
* extraction that may have been done
* NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
* to compare on ASCII based substring.
* SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
* strings within the parser.
*
* Clean macros, not dependent of an ASCII context, expect UTF-8 encoding
*
* NEXT Skip to the next character, this does the proper decoding
* in UTF-8 mode. It also pop-up unfinished entities on the fly.
* NEXTL(l) Skip l xmlChars in the input buffer
* CUR_CHAR(l) returns the current unicode character (int), set l
* to the number of xmlChars used for the encoding [0-5].
* CUR_SCHAR same but operate on a string instead of the context
* COPY_BUF copy the current unicode char to the target buffer, increment
* the index
* GROW, SHRINK handling of input buffers
*/
#define RAW (ctxt->token ? -1 : (*ctxt->input->cur))
#define CUR (ctxt->token ? ctxt->token : (*ctxt->input->cur))
#define NXT(val) ctxt->input->cur[(val)]
#define CUR_PTR ctxt->input->cur
#define SKIP(val) do { \
ctxt->nbChars += (val),ctxt->input->cur += (val); \
if (*ctxt->input->cur == '%') xmlParserHandlePEReference(ctxt); \
if ((*ctxt->input->cur == 0) && \
(xmlParserInputGrow(ctxt->input, INPUT_CHUNK) <= 0)) \
xmlPopInput(ctxt); \
} while (0)
#define SHRINK if (ctxt->input->cur - ctxt->input->base > INPUT_CHUNK) {\
xmlParserInputShrink(ctxt->input); \
if ((*ctxt->input->cur == 0) && \
(xmlParserInputGrow(ctxt->input, INPUT_CHUNK) <= 0)) \
xmlPopInput(ctxt); \
}
#define GROW if (ctxt->input->end - ctxt->input->cur < INPUT_CHUNK) { \
xmlParserInputGrow(ctxt->input, INPUT_CHUNK); \
if ((*ctxt->input->cur == 0) && \
(xmlParserInputGrow(ctxt->input, INPUT_CHUNK) <= 0)) \
xmlPopInput(ctxt); \
}
#define SKIP_BLANKS xmlSkipBlankChars(ctxt)
#define NEXT xmlNextChar(ctxt)
#define NEXT1 { \
ctxt->input->cur++; \
ctxt->nbChars++; \
if (*ctxt->input->cur == 0) \
xmlParserInputGrow(ctxt->input, INPUT_CHUNK); \
}
#define NEXTL(l) do { \
if (*(ctxt->input->cur) == '\n') { \
ctxt->input->line++; ctxt->input->col = 1; \
} else ctxt->input->col++; \
ctxt->token = 0; ctxt->input->cur += l; \
if (*ctxt->input->cur == '%') xmlParserHandlePEReference(ctxt); \
} while (0)
#define CUR_CHAR(l) xmlCurrentChar(ctxt, &l)
#define CUR_SCHAR(s, l) xmlStringCurrentChar(ctxt, s, &l)
#define COPY_BUF(l,b,i,v) \
if (l == 1) b[i++] = (xmlChar) v; \
else i += xmlCopyCharMultiByte(&b[i],v)
/**
* xmlSkipBlankChars:
* @ctxt: the XML parser context
*
* skip all blanks character found at that point in the input streams.
* It pops up finished entities in the process if allowable at that point.
*
* Returns the number of space chars skipped
*/
int
xmlSkipBlankChars(xmlParserCtxtPtr ctxt) {
int res = 0;
if (ctxt->token != 0) {
if (!IS_BLANK(ctxt->token))
return(0);
ctxt->token = 0;
res++;
}
/*
* It's Okay to use CUR/NEXT here since all the blanks are on
* the ASCII range.
*/
if ((ctxt->inputNr == 1) && (ctxt->instate != XML_PARSER_DTD)) {
const xmlChar *cur;
/*
* if we are in the document content, go really fast
*/
cur = ctxt->input->cur;
while (IS_BLANK(*cur)) {
if (*cur == '\n') {
ctxt->input->line++; ctxt->input->col = 1;
}
cur++;
res++;
if (*cur == 0) {
ctxt->input->cur = cur;
xmlParserInputGrow(ctxt->input, INPUT_CHUNK);
cur = ctxt->input->cur;
}
}
ctxt->input->cur = cur;
} else {
int cur;
do {
cur = CUR;
while (IS_BLANK(cur)) { /* CHECKED tstblanks.xml */
NEXT;
cur = CUR;
res++;
}
while ((cur == 0) && (ctxt->inputNr > 1) &&
(ctxt->instate != XML_PARSER_COMMENT)) {
xmlPopInput(ctxt);
cur = CUR;
}
/*
* Need to handle support of entities branching here
*/
if (*ctxt->input->cur == '%') xmlParserHandlePEReference(ctxt);
} while (IS_BLANK(cur)); /* CHECKED tstblanks.xml */
}
return(res);
}
/************************************************************************
* *
* Commodity functions to handle entities *
* *
************************************************************************/
/**
* xmlPopInput:
* @ctxt: an XML parser context
*
* xmlPopInput: the current input pointed by ctxt->input came to an end
* pop it and return the next char.
*
* Returns the current xmlChar in the parser context
*/
xmlChar
xmlPopInput(xmlParserCtxtPtr ctxt) {
if (ctxt->inputNr == 1) return(0); /* End of main Input */
if (xmlParserDebugEntities)
xmlGenericError(xmlGenericErrorContext,
"Popping input %d\n", ctxt->inputNr);
xmlFreeInputStream(inputPop(ctxt));
if ((*ctxt->input->cur == 0) &&
(xmlParserInputGrow(ctxt->input, INPUT_CHUNK) <= 0))
return(xmlPopInput(ctxt));
return(CUR);
}
/**
* xmlPushInput:
* @ctxt: an XML parser context
* @input: an XML parser input fragment (entity, XML fragment ...).
*
* xmlPushInput: switch to a new input stream which is stacked on top
* of the previous one(s).
*/
void
xmlPushInput(xmlParserCtxtPtr ctxt, xmlParserInputPtr input) {
if (input == NULL) return;
if (xmlParserDebugEntities) {
if ((ctxt->input != NULL) && (ctxt->input->filename))
xmlGenericError(xmlGenericErrorContext,
"%s(%d): ", ctxt->input->filename,
ctxt->input->line);
xmlGenericError(xmlGenericErrorContext,
"Pushing input %d : %.30s\n", ctxt->inputNr+1, input->cur);
}
inputPush(ctxt, input);
GROW;
}
/**
* xmlParseCharRef:
* @ctxt: an XML parser context
*
* parse Reference declarations
*
* [66] CharRef ::= '&#' [0-9]+ ';' |
* '&#x' [0-9a-fA-F]+ ';'
*
* [ WFC: Legal Character ]
* Characters referred to using character references must match the
* production for Char.
*
* Returns the value parsed (as an int), 0 in case of error
*/
int
xmlParseCharRef(xmlParserCtxtPtr ctxt) {
unsigned int val = 0;
int count = 0;
if (ctxt->token != 0) {
val = ctxt->token;
ctxt->token = 0;
return(val);
}
/*
* Using RAW/CUR/NEXT is okay since we are working on ASCII range here
*/
if ((RAW == '&') && (NXT(1) == '#') &&
(NXT(2) == 'x')) {
SKIP(3);
GROW;
while (RAW != ';') { /* loop blocked by count */
if ((RAW >= '0') && (RAW <= '9') && (count < 20))
val = val * 16 + (CUR - '0');
else if ((RAW >= 'a') && (RAW <= 'f') && (count < 20))
val = val * 16 + (CUR - 'a') + 10;
else if ((RAW >= 'A') && (RAW <= 'F') && (count < 20))
val = val * 16 + (CUR - 'A') + 10;
else {
ctxt->errNo = XML_ERR_INVALID_HEX_CHARREF;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseCharRef: invalid hexadecimal value\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
val = 0;
break;
}
NEXT;
count++;
}
if (RAW == ';') {
/* on purpose to avoid reentrancy problems with NEXT and SKIP */
ctxt->nbChars ++;
ctxt->input->cur++;
}
} else if ((RAW == '&') && (NXT(1) == '#')) {
SKIP(2);
GROW;
while (RAW != ';') { /* loop blocked by count */
if ((RAW >= '0') && (RAW <= '9') && (count < 20))
val = val * 10 + (CUR - '0');
else {
ctxt->errNo = XML_ERR_INVALID_DEC_CHARREF;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseCharRef: invalid decimal value\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
val = 0;
break;
}
NEXT;
count++;
}
if (RAW == ';') {
/* on purpose to avoid reentrancy problems with NEXT and SKIP */
ctxt->nbChars ++;
ctxt->input->cur++;
}
} else {
ctxt->errNo = XML_ERR_INVALID_CHARREF;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseCharRef: invalid value\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
/*
* [ WFC: Legal Character ]
* Characters referred to using character references must match the
* production for Char.
*/
if (IS_CHAR(val)) {
return(val);
} else {
ctxt->errNo = XML_ERR_INVALID_CHAR;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "CharRef: invalid xmlChar value %d\n",
val);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
return(0);
}
/**
* xmlParseStringCharRef:
* @ctxt: an XML parser context
* @str: a pointer to an index in the string
*
* parse Reference declarations, variant parsing from a string rather
* than an an input flow.
*
* [66] CharRef ::= '&#' [0-9]+ ';' |
* '&#x' [0-9a-fA-F]+ ';'
*
* [ WFC: Legal Character ]
* Characters referred to using character references must match the
* production for Char.
*
* Returns the value parsed (as an int), 0 in case of error, str will be
* updated to the current value of the index
*/
static int
xmlParseStringCharRef(xmlParserCtxtPtr ctxt, const xmlChar **str) {
const xmlChar *ptr;
xmlChar cur;
int val = 0;
if ((str == NULL) || (*str == NULL)) return(0);
ptr = *str;
cur = *ptr;
if ((cur == '&') && (ptr[1] == '#') && (ptr[2] == 'x')) {
ptr += 3;
cur = *ptr;
while (cur != ';') { /* Non input consuming loop */
if ((cur >= '0') && (cur <= '9'))
val = val * 16 + (cur - '0');
else if ((cur >= 'a') && (cur <= 'f'))
val = val * 16 + (cur - 'a') + 10;
else if ((cur >= 'A') && (cur <= 'F'))
val = val * 16 + (cur - 'A') + 10;
else {
ctxt->errNo = XML_ERR_INVALID_HEX_CHARREF;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseStringCharRef: invalid hexadecimal value\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
val = 0;
break;
}
ptr++;
cur = *ptr;
}
if (cur == ';')
ptr++;
} else if ((cur == '&') && (ptr[1] == '#')){
ptr += 2;
cur = *ptr;
while (cur != ';') { /* Non input consuming loops */
if ((cur >= '0') && (cur <= '9'))
val = val * 10 + (cur - '0');
else {
ctxt->errNo = XML_ERR_INVALID_DEC_CHARREF;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseStringCharRef: invalid decimal value\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
val = 0;
break;
}
ptr++;
cur = *ptr;
}
if (cur == ';')
ptr++;
} else {
ctxt->errNo = XML_ERR_INVALID_CHARREF;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseCharRef: invalid value\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(0);
}
*str = ptr;
/*
* [ WFC: Legal Character ]
* Characters referred to using character references must match the
* production for Char.
*/
if (IS_CHAR(val)) {
return(val);
} else {
ctxt->errNo = XML_ERR_INVALID_CHAR;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"CharRef: invalid xmlChar value %d\n", val);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
return(0);
}
/**
* xmlParserHandlePEReference:
* @ctxt: the parser context
*
* [69] PEReference ::= '%' Name ';'
*
* [ WFC: No Recursion ]
* A parsed entity must not contain a recursive
* reference to itself, either directly or indirectly.
*
* [ WFC: Entity Declared ]
* In a document without any DTD, a document with only an internal DTD
* subset which contains no parameter entity references, or a document
* with "standalone='yes'", ... ... The declaration of a parameter
* entity must precede any reference to it...
*
* [ VC: Entity Declared ]
* In a document with an external subset or external parameter entities
* with "standalone='no'", ... ... The declaration of a parameter entity
* must precede any reference to it...
*
* [ WFC: In DTD ]
* Parameter-entity references may only appear in the DTD.
* NOTE: misleading but this is handled.
*
* A PEReference may have been detected in the current input stream
* the handling is done accordingly to
* http://www.w3.org/TR/REC-xml#entproc
* i.e.
* - Included in literal in entity values
* - Included as Paraemeter Entity reference within DTDs
*/
void
xmlParserHandlePEReference(xmlParserCtxtPtr ctxt) {
xmlChar *name;
xmlEntityPtr entity = NULL;
xmlParserInputPtr input;
if (ctxt->token != 0) {
return;
}
if (RAW != '%') return;
switch(ctxt->instate) {
case XML_PARSER_CDATA_SECTION:
return;
case XML_PARSER_COMMENT:
return;
case XML_PARSER_START_TAG:
return;
case XML_PARSER_END_TAG:
return;
case XML_PARSER_EOF:
ctxt->errNo = XML_ERR_PEREF_AT_EOF;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "PEReference at EOF\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return;
case XML_PARSER_PROLOG:
case XML_PARSER_START:
case XML_PARSER_MISC:
ctxt->errNo = XML_ERR_PEREF_IN_PROLOG;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "PEReference in prolog!\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return;
case XML_PARSER_ENTITY_DECL:
case XML_PARSER_CONTENT:
case XML_PARSER_ATTRIBUTE_VALUE:
case XML_PARSER_PI:
case XML_PARSER_SYSTEM_LITERAL:
/* we just ignore it there */
return;
case XML_PARSER_EPILOG:
ctxt->errNo = XML_ERR_PEREF_IN_EPILOG;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "PEReference in epilog!\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return;
case XML_PARSER_ENTITY_VALUE:
/*
* NOTE: in the case of entity values, we don't do the
* substitution here since we need the literal
* entity value to be able to save the internal
* subset of the document.
* This will be handled by xmlStringDecodeEntities
*/
return;
case XML_PARSER_DTD:
/*
* [WFC: Well-Formedness Constraint: PEs in Internal Subset]
* In the internal DTD subset, parameter-entity references
* can occur only where markup declarations can occur, not
* within markup declarations.
* In that case this is handled in xmlParseMarkupDecl
*/
if ((ctxt->external == 0) && (ctxt->inputNr == 1))
return;
break;
case XML_PARSER_IGNORE:
return;
}
NEXT;
name = xmlParseName(ctxt);
if (xmlParserDebugEntities)
xmlGenericError(xmlGenericErrorContext,
"PE Reference: %s\n", name);
if (name == NULL) {
ctxt->errNo = XML_ERR_PEREF_NO_NAME;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "xmlHandlePEReference: no name\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
if (RAW == ';') {
NEXT;
if ((ctxt->sax != NULL) && (ctxt->sax->getParameterEntity != NULL))
entity = ctxt->sax->getParameterEntity(ctxt->userData, name);
if (entity == NULL) {
/*
* [ WFC: Entity Declared ]
* In a document without any DTD, a document with only an
* internal DTD subset which contains no parameter entity
* references, or a document with "standalone='yes'", ...
* ... The declaration of a parameter entity must precede
* any reference to it...
*/
if ((ctxt->standalone == 1) ||
((ctxt->hasExternalSubset == 0) &&
(ctxt->hasPErefs == 0))) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"PEReference: %%%s; not found\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
/*
* [ VC: Entity Declared ]
* In a document with an external subset or external
* parameter entities with "standalone='no'", ...
* ... The declaration of a parameter entity must precede
* any reference to it...
*/
if ((!ctxt->disableSAX) &&
(ctxt->validate) && (ctxt->vctxt.error != NULL)) {
ctxt->vctxt.error(ctxt->vctxt.userData,
"PEReference: %%%s; not found\n", name);
} else if ((!ctxt->disableSAX) &&
(ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
ctxt->sax->warning(ctxt->userData,
"PEReference: %%%s; not found\n", name);
ctxt->valid = 0;
}
} else {
if ((entity->etype == XML_INTERNAL_PARAMETER_ENTITY) ||
(entity->etype == XML_EXTERNAL_PARAMETER_ENTITY)) {
xmlChar start[4];
xmlCharEncoding enc;
/*
* handle the extra spaces added before and after
* c.f. http://www.w3.org/TR/REC-xml#as-PE
* this is done independantly.
*/
input = xmlNewEntityInputStream(ctxt, entity);
xmlPushInput(ctxt, input);
/*
* Get the 4 first bytes and decode the charset
* if enc != XML_CHAR_ENCODING_NONE
* plug some encoding conversion routines.
*/
GROW
start[0] = RAW;
start[1] = NXT(1);
start[2] = NXT(2);
start[3] = NXT(3);
enc = xmlDetectCharEncoding(start, 4);
if (enc != XML_CHAR_ENCODING_NONE) {
xmlSwitchEncoding(ctxt, enc);
}
if ((entity->etype == XML_EXTERNAL_PARAMETER_ENTITY) &&
(RAW == '<') && (NXT(1) == '?') &&
(NXT(2) == 'x') && (NXT(3) == 'm') &&
(NXT(4) == 'l') && (IS_BLANK(NXT(5)))) {
xmlParseTextDecl(ctxt);
}
if (ctxt->token == 0)
ctxt->token = ' ';
} else {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlHandlePEReference: %s is not a parameter entity\n",
name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
}
} else {
ctxt->errNo = XML_ERR_PEREF_SEMICOL_MISSING;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlHandlePEReference: expecting ';'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
xmlFree(name);
}
}
/*
* Macro used to grow the current buffer.
*/
#define growBuffer(buffer) { \
buffer##_size *= 2; \
buffer = (xmlChar *) \
xmlRealloc(buffer, buffer##_size * sizeof(xmlChar)); \
if (buffer == NULL) { \
perror("realloc failed"); \
return(NULL); \
} \
}
/**
* xmlStringDecodeEntities:
* @ctxt: the parser context
* @str: the input string
* @what: combination of XML_SUBSTITUTE_REF and XML_SUBSTITUTE_PEREF
* @end: an end marker xmlChar, 0 if none
* @end2: an end marker xmlChar, 0 if none
* @end3: an end marker xmlChar, 0 if none
*
* Takes a entity string content and process to do the adequate subtitutions.
*
* [67] Reference ::= EntityRef | CharRef
*
* [69] PEReference ::= '%' Name ';'
*
* Returns A newly allocated string with the substitution done. The caller
* must deallocate it !
*/
xmlChar *
xmlStringDecodeEntities(xmlParserCtxtPtr ctxt, const xmlChar *str, int what,
xmlChar end, xmlChar end2, xmlChar end3) {
xmlChar *buffer = NULL;
int buffer_size = 0;
xmlChar *current = NULL;
xmlEntityPtr ent;
int c,l;
int nbchars = 0;
if (str == NULL)
return(NULL);
if (ctxt->depth > 40) {
ctxt->errNo = XML_ERR_ENTITY_LOOP;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Detected entity reference loop\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(NULL);
}
/*
* allocate a translation buffer.
*/
buffer_size = XML_PARSER_BIG_BUFFER_SIZE;
buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
if (buffer == NULL) {
perror("xmlDecodeEntities: malloc failed");
return(NULL);
}
/*
* Ok loop until we reach one of the ending char or a size limit.
* we are operating on already parsed values.
*/
c = CUR_SCHAR(str, l);
while ((c != 0) && (c != end) && /* non input consuming loop */
(c != end2) && (c != end3)) {
if (c == 0) break;
if ((c == '&') && (str[1] == '#')) {
int val = xmlParseStringCharRef(ctxt, &str);
if (val != 0) {
COPY_BUF(0,buffer,nbchars,val);
}
} else if ((c == '&') && (what & XML_SUBSTITUTE_REF)) {
if (xmlParserDebugEntities)
xmlGenericError(xmlGenericErrorContext,
"String decoding Entity Reference: %.30s\n",
str);
ent = xmlParseStringEntityRef(ctxt, &str);
if ((ent != NULL) &&
(ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) {
if (ent->content != NULL) {
COPY_BUF(0,buffer,nbchars,ent->content[0]);
} else {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"internal error entity has no content\n");
}
} else if ((ent != NULL) && (ent->content != NULL)) {
xmlChar *rep;
ctxt->depth++;
rep = xmlStringDecodeEntities(ctxt, ent->content, what,
0, 0, 0);
ctxt->depth--;
if (rep != NULL) {
current = rep;
while (*current != 0) { /* non input consuming loop */
buffer[nbchars++] = *current++;
if (nbchars >
buffer_size - XML_PARSER_BUFFER_SIZE) {
growBuffer(buffer);
}
}
xmlFree(rep);
}
} else if (ent != NULL) {
int i = xmlStrlen(ent->name);
const xmlChar *cur = ent->name;
buffer[nbchars++] = '&';
if (nbchars > buffer_size - i - XML_PARSER_BUFFER_SIZE) {
growBuffer(buffer);
}
for (;i > 0;i--)
buffer[nbchars++] = *cur++;
buffer[nbchars++] = ';';
}
} else if (c == '%' && (what & XML_SUBSTITUTE_PEREF)) {
if (xmlParserDebugEntities)
xmlGenericError(xmlGenericErrorContext,
"String decoding PE Reference: %.30s\n", str);
ent = xmlParseStringPEReference(ctxt, &str);
if (ent != NULL) {
xmlChar *rep;
ctxt->depth++;
rep = xmlStringDecodeEntities(ctxt, ent->content, what,
0, 0, 0);
ctxt->depth--;
if (rep != NULL) {
current = rep;
while (*current != 0) { /* non input consuming loop */
buffer[nbchars++] = *current++;
if (nbchars >
buffer_size - XML_PARSER_BUFFER_SIZE) {
growBuffer(buffer);
}
}
xmlFree(rep);
}
}
} else {
COPY_BUF(l,buffer,nbchars,c);
str += l;
if (nbchars > buffer_size - XML_PARSER_BUFFER_SIZE) {
growBuffer(buffer);
}
}
c = CUR_SCHAR(str, l);
}
buffer[nbchars++] = 0;
return(buffer);
}
/************************************************************************
* *
* Commodity functions to handle xmlChars *
* *
************************************************************************/
/**
* xmlStrndup:
* @cur: the input xmlChar *
* @len: the len of @cur
*
* a strndup for array of xmlChar's
*
* Returns a new xmlChar * or NULL
*/
xmlChar *
xmlStrndup(const xmlChar *cur, int len) {
xmlChar *ret;
if ((cur == NULL) || (len < 0)) return(NULL);
ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"malloc of %ld byte failed\n",
(len + 1) * (long)sizeof(xmlChar));
return(NULL);
}
memcpy(ret, cur, len * sizeof(xmlChar));
ret[len] = 0;
return(ret);
}
/**
* xmlStrdup:
* @cur: the input xmlChar *
*
* a strdup for array of xmlChar's. Since they are supposed to be
* encoded in UTF-8 or an encoding with 8bit based chars, we assume
* a termination mark of '0'.
*
* Returns a new xmlChar * or NULL
*/
xmlChar *
xmlStrdup(const xmlChar *cur) {
const xmlChar *p = cur;
if (cur == NULL) return(NULL);
while (*p != 0) p++; /* non input consuming */
return(xmlStrndup(cur, p - cur));
}
/**
* xmlCharStrndup:
* @cur: the input char *
* @len: the len of @cur
*
* a strndup for char's to xmlChar's
*
* Returns a new xmlChar * or NULL
*/
xmlChar *
xmlCharStrndup(const char *cur, int len) {
int i;
xmlChar *ret;
if ((cur == NULL) || (len < 0)) return(NULL);
ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext, "malloc of %ld byte failed\n",
(len + 1) * (long)sizeof(xmlChar));
return(NULL);
}
for (i = 0;i < len;i++)
ret[i] = (xmlChar) cur[i];
ret[len] = 0;
return(ret);
}
/**
* xmlCharStrdup:
* @cur: the input char *
* @len: the len of @cur
*
* a strdup for char's to xmlChar's
*
* Returns a new xmlChar * or NULL
*/
xmlChar *
xmlCharStrdup(const char *cur) {
const char *p = cur;
if (cur == NULL) return(NULL);
while (*p != '\0') p++; /* non input consuming */
return(xmlCharStrndup(cur, p - cur));
}
/**
* xmlStrcmp:
* @str1: the first xmlChar *
* @str2: the second xmlChar *
*
* a strcmp for xmlChar's
*
* Returns the integer result of the comparison
*/
int
xmlStrcmp(const xmlChar *str1, const xmlChar *str2) {
register int tmp;
if (str1 == str2) return(0);
if (str1 == NULL) return(-1);
if (str2 == NULL) return(1);
do {
tmp = *str1++ - *str2;
if (tmp != 0) return(tmp);
} while (*str2++ != 0);
return 0;
}
/**
* xmlStrEqual:
* @str1: the first xmlChar *
* @str2: the second xmlChar *
*
* Check if both string are equal of have same content
* Should be a bit more readable and faster than xmlStrEqual()
*
* Returns 1 if they are equal, 0 if they are different
*/
int
xmlStrEqual(const xmlChar *str1, const xmlChar *str2) {
if (str1 == str2) return(1);
if (str1 == NULL) return(0);
if (str2 == NULL) return(0);
do {
if (*str1++ != *str2) return(0);
} while (*str2++);
return(1);
}
/**
* xmlStrncmp:
* @str1: the first xmlChar *
* @str2: the second xmlChar *
* @len: the max comparison length
*
* a strncmp for xmlChar's
*
* Returns the integer result of the comparison
*/
int
xmlStrncmp(const xmlChar *str1, const xmlChar *str2, int len) {
register int tmp;
if (len <= 0) return(0);
if (str1 == str2) return(0);
if (str1 == NULL) return(-1);
if (str2 == NULL) return(1);
do {
tmp = *str1++ - *str2;
if (tmp != 0 || --len == 0) return(tmp);
} while (*str2++ != 0);
return 0;
}
static xmlChar casemap[256] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
0x78,0x79,0x7A,0x7B,0x5C,0x5D,0x5E,0x5F,
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,
0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,
0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,
0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,
0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,
0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,
0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,
0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,
0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,
0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
};
/**
* xmlStrcasecmp:
* @str1: the first xmlChar *
* @str2: the second xmlChar *
*
* a strcasecmp for xmlChar's
*
* Returns the integer result of the comparison
*/
int
xmlStrcasecmp(const xmlChar *str1, const xmlChar *str2) {
register int tmp;
if (str1 == str2) return(0);
if (str1 == NULL) return(-1);
if (str2 == NULL) return(1);
do {
tmp = casemap[*str1++] - casemap[*str2];
if (tmp != 0) return(tmp);
} while (*str2++ != 0);
return 0;
}
/**
* xmlStrncasecmp:
* @str1: the first xmlChar *
* @str2: the second xmlChar *
* @len: the max comparison length
*
* a strncasecmp for xmlChar's
*
* Returns the integer result of the comparison
*/
int
xmlStrncasecmp(const xmlChar *str1, const xmlChar *str2, int len) {
register int tmp;
if (len <= 0) return(0);
if (str1 == str2) return(0);
if (str1 == NULL) return(-1);
if (str2 == NULL) return(1);
do {
tmp = casemap[*str1++] - casemap[*str2];
if (tmp != 0 || --len == 0) return(tmp);
} while (*str2++ != 0);
return 0;
}
/**
* xmlStrchr:
* @str: the xmlChar * array
* @val: the xmlChar to search
*
* a strchr for xmlChar's
*
* Returns the xmlChar * for the first occurence or NULL.
*/
const xmlChar *
xmlStrchr(const xmlChar *str, xmlChar val) {
if (str == NULL) return(NULL);
while (*str != 0) { /* non input consuming */
if (*str == val) return((xmlChar *) str);
str++;
}
return(NULL);
}
/**
* xmlStrstr:
* @str: the xmlChar * array (haystack)
* @val: the xmlChar to search (needle)
*
* a strstr for xmlChar's
*
* Returns the xmlChar * for the first occurence or NULL.
*/
const xmlChar *
xmlStrstr(const xmlChar *str, const xmlChar *val) {
int n;
if (str == NULL) return(NULL);
if (val == NULL) return(NULL);
n = xmlStrlen(val);
if (n == 0) return(str);
while (*str != 0) { /* non input consuming */
if (*str == *val) {
if (!xmlStrncmp(str, val, n)) return((const xmlChar *) str);
}
str++;
}
return(NULL);
}
/**
* xmlStrcasestr:
* @str: the xmlChar * array (haystack)
* @val: the xmlChar to search (needle)
*
* a case-ignoring strstr for xmlChar's
*
* Returns the xmlChar * for the first occurence or NULL.
*/
const xmlChar *
xmlStrcasestr(const xmlChar *str, xmlChar *val) {
int n;
if (str == NULL) return(NULL);
if (val == NULL) return(NULL);
n = xmlStrlen(val);
if (n == 0) return(str);
while (*str != 0) { /* non input consuming */
if (casemap[*str] == casemap[*val])
if (!xmlStrncasecmp(str, val, n)) return(str);
str++;
}
return(NULL);
}
/**
* xmlStrsub:
* @str: the xmlChar * array (haystack)
* @start: the index of the first char (zero based)
* @len: the length of the substring
*
* Extract a substring of a given string
*
* Returns the xmlChar * for the first occurence or NULL.
*/
xmlChar *
xmlStrsub(const xmlChar *str, int start, int len) {
int i;
if (str == NULL) return(NULL);
if (start < 0) return(NULL);
if (len < 0) return(NULL);
for (i = 0;i < start;i++) {
if (*str == 0) return(NULL);
str++;
}
if (*str == 0) return(NULL);
return(xmlStrndup(str, len));
}
/**
* xmlStrlen:
* @str: the xmlChar * array
*
* length of a xmlChar's string
*
* Returns the number of xmlChar contained in the ARRAY.
*/
int
xmlStrlen(const xmlChar *str) {
int len = 0;
if (str == NULL) return(0);
while (*str != 0) { /* non input consuming */
str++;
len++;
}
return(len);
}
/**
* xmlStrncat:
* @cur: the original xmlChar * array
* @add: the xmlChar * array added
* @len: the length of @add
*
* a strncat for array of xmlChar's, it will extend cur with the len
* first bytes of @add.
*
* Returns a new xmlChar *, the original @cur is reallocated if needed
* and should not be freed
*/
xmlChar *
xmlStrncat(xmlChar *cur, const xmlChar *add, int len) {
int size;
xmlChar *ret;
if ((add == NULL) || (len == 0))
return(cur);
if (cur == NULL)
return(xmlStrndup(add, len));
size = xmlStrlen(cur);
ret = (xmlChar *) xmlRealloc(cur, (size + len + 1) * sizeof(xmlChar));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlStrncat: realloc of %ld byte failed\n",
(size + len + 1) * (long)sizeof(xmlChar));
return(cur);
}
memcpy(&ret[size], add, len * sizeof(xmlChar));
ret[size + len] = 0;
return(ret);
}
/**
* xmlStrcat:
* @cur: the original xmlChar * array
* @add: the xmlChar * array added
*
* a strcat for array of xmlChar's. Since they are supposed to be
* encoded in UTF-8 or an encoding with 8bit based chars, we assume
* a termination mark of '0'.
*
* Returns a new xmlChar * containing the concatenated string.
*/
xmlChar *
xmlStrcat(xmlChar *cur, const xmlChar *add) {
const xmlChar *p = add;
if (add == NULL) return(cur);
if (cur == NULL)
return(xmlStrdup(add));
while (*p != 0) p++; /* non input consuming */
return(xmlStrncat(cur, add, p - add));
}
/************************************************************************
* *
* Commodity functions, cleanup needed ? *
* *
************************************************************************/
/**
* areBlanks:
* @ctxt: an XML parser context
* @str: a xmlChar *
* @len: the size of @str
*
* Is this a sequence of blank chars that one can ignore ?
*
* Returns 1 if ignorable 0 otherwise.
*/
static int areBlanks(xmlParserCtxtPtr ctxt, const xmlChar *str, int len) {
int i, ret;
xmlNodePtr lastChild;
if (ctxt->keepBlanks)
return(0);
/*
* Check for xml:space value.
*/
if (*(ctxt->space) == 1)
return(0);
/*
* Check that the string is made of blanks
*/
for (i = 0;i < len;i++)
if (!(IS_BLANK(str[i]))) return(0);
/*
* Look if the element is mixed content in the Dtd if available
*/
if (ctxt->node == NULL) return(0);
if (ctxt->myDoc != NULL) {
ret = xmlIsMixedElement(ctxt->myDoc, ctxt->node->name);
if (ret == 0) return(1);
if (ret == 1) return(0);
}
/*
* Otherwise, heuristic :-\
*/
if (RAW != '<') return(0);
if ((ctxt->node->children == NULL) &&
(RAW == '<') && (NXT(1) == '/')) return(0);
lastChild = xmlGetLastChild(ctxt->node);
if (lastChild == NULL) {
if ((ctxt->node->type != XML_ELEMENT_NODE) &&
(ctxt->node->content != NULL)) return(0);
} else if (xmlNodeIsText(lastChild))
return(0);
else if ((ctxt->node->children != NULL) &&
(xmlNodeIsText(ctxt->node->children)))
return(0);
return(1);
}
/*
* Forward definition for recusive behaviour.
*/
void xmlParsePEReference(xmlParserCtxtPtr ctxt);
void xmlParseReference(xmlParserCtxtPtr ctxt);
/************************************************************************
* *
* Extra stuff for namespace support *
* Relates to http://www.w3.org/TR/WD-xml-names *
* *
************************************************************************/
/**
* xmlSplitQName:
* @ctxt: an XML parser context
* @name: an XML parser context
* @prefix: a xmlChar **
*
* parse an UTF8 encoded XML qualified name string
*
* [NS 5] QName ::= (Prefix ':')? LocalPart
*
* [NS 6] Prefix ::= NCName
*
* [NS 7] LocalPart ::= NCName
*
* Returns the local part, and prefix is updated
* to get the Prefix if any.
*/
xmlChar *
xmlSplitQName(xmlParserCtxtPtr ctxt, const xmlChar *name, xmlChar **prefix) {
xmlChar buf[XML_MAX_NAMELEN + 5];
xmlChar *buffer = NULL;
int len = 0;
int max = XML_MAX_NAMELEN;
xmlChar *ret = NULL;
const xmlChar *cur = name;
int c;
*prefix = NULL;
#ifndef XML_XML_NAMESPACE
/* xml: prefix is not really a namespace */
if ((cur[0] == 'x') && (cur[1] == 'm') &&
(cur[2] == 'l') && (cur[3] == ':'))
return(xmlStrdup(name));
#endif
/* nasty but valid */
if (cur[0] == ':')
return(xmlStrdup(name));
c = *cur++;
while ((c != 0) && (c != ':') && (len < max)) { /* tested bigname.xml */
buf[len++] = c;
c = *cur++;
}
if (len >= max) {
/*
* Okay someone managed to make a huge name, so he's ready to pay
* for the processing speed.
*/
max = len * 2;
buffer = (xmlChar *) xmlMalloc(max * sizeof(xmlChar));
if (buffer == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlSplitQName: out of memory\n");
return(NULL);
}
memcpy(buffer, buf, len);
while ((c != 0) && (c != ':')) { /* tested bigname.xml */
if (len + 10 > max) {
max *= 2;
buffer = (xmlChar *) xmlRealloc(buffer,
max * sizeof(xmlChar));
if (buffer == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlSplitQName: out of memory\n");
return(NULL);
}
}
buffer[len++] = c;
c = *cur++;
}
buffer[len] = 0;
}
if (buffer == NULL)
ret = xmlStrndup(buf, len);
else {
ret = buffer;
buffer = NULL;
max = XML_MAX_NAMELEN;
}
if (c == ':') {
c = *cur++;
if (c == 0) return(ret);
*prefix = ret;
len = 0;
while ((c != 0) && (len < max)) { /* tested bigname2.xml */
buf[len++] = c;
c = *cur++;
}
if (len >= max) {
/*
* Okay someone managed to make a huge name, so he's ready to pay
* for the processing speed.
*/
max = len * 2;
buffer = (xmlChar *) xmlMalloc(max * sizeof(xmlChar));
if (buffer == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlSplitQName: out of memory\n");
return(NULL);
}
memcpy(buffer, buf, len);
while (c != 0) { /* tested bigname2.xml */
if (len + 10 > max) {
max *= 2;
buffer = (xmlChar *) xmlRealloc(buffer,
max * sizeof(xmlChar));
if (buffer == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlSplitQName: out of memory\n");
return(NULL);
}
}
buffer[len++] = c;
c = *cur++;
}
buffer[len] = 0;
}
if (buffer == NULL)
ret = xmlStrndup(buf, len);
else {
ret = buffer;
}
}
return(ret);
}
/************************************************************************
* *
* The parser itself *
* Relates to http://www.w3.org/TR/REC-xml *
* *
************************************************************************/
static xmlChar * xmlParseNameComplex(xmlParserCtxtPtr ctxt);
/**
* xmlParseName:
* @ctxt: an XML parser context
*
* parse an XML name.
*
* [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
* CombiningChar | Extender
*
* [5] Name ::= (Letter | '_' | ':') (NameChar)*
*
* [6] Names ::= Name (S Name)*
*
* Returns the Name parsed or NULL
*/
xmlChar *
xmlParseName(xmlParserCtxtPtr ctxt) {
const xmlChar *in;
xmlChar *ret;
int count = 0;
GROW;
/*
* Accelerator for simple ASCII names
*/
in = ctxt->input->cur;
if (((*in >= 0x61) && (*in <= 0x7A)) ||
((*in >= 0x41) && (*in <= 0x5A)) ||
(*in == '_') || (*in == ':')) {
in++;
while (((*in >= 0x61) && (*in <= 0x7A)) ||
((*in >= 0x41) && (*in <= 0x5A)) ||
((*in >= 0x30) && (*in <= 0x39)) ||
(*in == '_') || (*in == '-') ||
(*in == ':') || (*in == '.'))
in++;
if ((*in > 0) && (*in < 0x80)) {
count = in - ctxt->input->cur;
ret = xmlStrndup(ctxt->input->cur, count);
ctxt->input->cur = in;
return(ret);
}
}
return(xmlParseNameComplex(ctxt));
}
static xmlChar *
xmlParseNameComplex(xmlParserCtxtPtr ctxt) {
xmlChar buf[XML_MAX_NAMELEN + 5];
int len = 0, l;
int c;
int count = 0;
/*
* Handler for more complex cases
*/
GROW;
c = CUR_CHAR(l);
if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
(!IS_LETTER(c) && (c != '_') &&
(c != ':'))) {
return(NULL);
}
while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
((IS_LETTER(c)) || (IS_DIGIT(c)) ||
(c == '.') || (c == '-') ||
(c == '_') || (c == ':') ||
(IS_COMBINING(c)) ||
(IS_EXTENDER(c)))) {
if (count++ > 100) {
count = 0;
GROW;
}
COPY_BUF(l,buf,len,c);
NEXTL(l);
c = CUR_CHAR(l);
if (len >= XML_MAX_NAMELEN) {
/*
* Okay someone managed to make a huge name, so he's ready to pay
* for the processing speed.
*/
xmlChar *buffer;
int max = len * 2;
buffer = (xmlChar *) xmlMalloc(max * sizeof(xmlChar));
if (buffer == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseNameComplex: out of memory\n");
return(NULL);
}
memcpy(buffer, buf, len);
while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
(c == '.') || (c == '-') ||
(c == '_') || (c == ':') ||
(IS_COMBINING(c)) ||
(IS_EXTENDER(c))) {
if (count++ > 100) {
count = 0;
GROW;
}
if (len + 10 > max) {
max *= 2;
buffer = (xmlChar *) xmlRealloc(buffer,
max * sizeof(xmlChar));
if (buffer == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseNameComplex: out of memory\n");
return(NULL);
}
}
COPY_BUF(l,buffer,len,c);
NEXTL(l);
c = CUR_CHAR(l);
}
buffer[len] = 0;
return(buffer);
}
}
return(xmlStrndup(buf, len));
}
/**
* xmlParseStringName:
* @ctxt: an XML parser context
* @str: a pointer to the string pointer (IN/OUT)
*
* parse an XML name.
*
* [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
* CombiningChar | Extender
*
* [5] Name ::= (Letter | '_' | ':') (NameChar)*
*
* [6] Names ::= Name (S Name)*
*
* Returns the Name parsed or NULL. The str pointer
* is updated to the current location in the string.
*/
static xmlChar *
xmlParseStringName(xmlParserCtxtPtr ctxt, const xmlChar** str) {
xmlChar buf[XML_MAX_NAMELEN + 5];
const xmlChar *cur = *str;
int len = 0, l;
int c;
c = CUR_SCHAR(cur, l);
if (!IS_LETTER(c) && (c != '_') &&
(c != ':')) {
return(NULL);
}
while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigentname.xml */
(c == '.') || (c == '-') ||
(c == '_') || (c == ':') ||
(IS_COMBINING(c)) ||
(IS_EXTENDER(c))) {
COPY_BUF(l,buf,len,c);
cur += l;
c = CUR_SCHAR(cur, l);
if (len >= XML_MAX_NAMELEN) { /* test bigentname.xml */
/*
* Okay someone managed to make a huge name, so he's ready to pay
* for the processing speed.
*/
xmlChar *buffer;
int max = len * 2;
buffer = (xmlChar *) xmlMalloc(max * sizeof(xmlChar));
if (buffer == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseStringName: out of memory\n");
return(NULL);
}
memcpy(buffer, buf, len);
while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigentname.xml */
(c == '.') || (c == '-') ||
(c == '_') || (c == ':') ||
(IS_COMBINING(c)) ||
(IS_EXTENDER(c))) {
if (len + 10 > max) {
max *= 2;
buffer = (xmlChar *) xmlRealloc(buffer,
max * sizeof(xmlChar));
if (buffer == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseStringName: out of memory\n");
return(NULL);
}
}
COPY_BUF(l,buffer,len,c);
cur += l;
c = CUR_SCHAR(cur, l);
}
buffer[len] = 0;
*str = cur;
return(buffer);
}
}
*str = cur;
return(xmlStrndup(buf, len));
}
/**
* xmlParseNmtoken:
* @ctxt: an XML parser context
*
* parse an XML Nmtoken.
*
* [7] Nmtoken ::= (NameChar)+
*
* [8] Nmtokens ::= Nmtoken (S Nmtoken)*
*
* Returns the Nmtoken parsed or NULL
*/
xmlChar *
xmlParseNmtoken(xmlParserCtxtPtr ctxt) {
xmlChar buf[XML_MAX_NAMELEN + 5];
int len = 0, l;
int c;
int count = 0;
GROW;
c = CUR_CHAR(l);
while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigtoken.xml */
(c == '.') || (c == '-') ||
(c == '_') || (c == ':') ||
(IS_COMBINING(c)) ||
(IS_EXTENDER(c))) {
if (count++ > 100) {
count = 0;
GROW;
}
COPY_BUF(l,buf,len,c);
NEXTL(l);
c = CUR_CHAR(l);
if (len >= XML_MAX_NAMELEN) {
/*
* Okay someone managed to make a huge token, so he's ready to pay
* for the processing speed.
*/
xmlChar *buffer;
int max = len * 2;
buffer = (xmlChar *) xmlMalloc(max * sizeof(xmlChar));
if (buffer == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseNmtoken: out of memory\n");
return(NULL);
}
memcpy(buffer, buf, len);
while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigtoken.xml */
(c == '.') || (c == '-') ||
(c == '_') || (c == ':') ||
(IS_COMBINING(c)) ||
(IS_EXTENDER(c))) {
if (count++ > 100) {
count = 0;
GROW;
}
if (len + 10 > max) {
max *= 2;
buffer = (xmlChar *) xmlRealloc(buffer,
max * sizeof(xmlChar));
if (buffer == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseNameComplex: out of memory\n");
return(NULL);
}
}
COPY_BUF(l,buffer,len,c);
NEXTL(l);
c = CUR_CHAR(l);
}
buffer[len] = 0;
return(buffer);
}
}
if (len == 0)
return(NULL);
return(xmlStrndup(buf, len));
}
/**
* xmlParseEntityValue:
* @ctxt: an XML parser context
* @orig: if non-NULL store a copy of the original entity value
*
* parse a value for ENTITY declarations
*
* [9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"' |
* "'" ([^%&'] | PEReference | Reference)* "'"
*
* Returns the EntityValue parsed with reference substitued or NULL
*/
xmlChar *
xmlParseEntityValue(xmlParserCtxtPtr ctxt, xmlChar **orig) {
xmlChar *buf = NULL;
int len = 0;
int size = XML_PARSER_BUFFER_SIZE;
int c, l;
xmlChar stop;
xmlChar *ret = NULL;
const xmlChar *cur = NULL;
xmlParserInputPtr input;
if (RAW == '"') stop = '"';
else if (RAW == '\'') stop = '\'';
else {
ctxt->errNo = XML_ERR_ENTITY_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "EntityValue: \" or ' expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(NULL);
}
buf = (xmlChar *) xmlMalloc(size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"malloc of %d byte failed\n", size);
return(NULL);
}
/*
* The content of the entity definition is copied in a buffer.
*/
ctxt->instate = XML_PARSER_ENTITY_VALUE;
input = ctxt->input;
GROW;
NEXT;
c = CUR_CHAR(l);
/*
* NOTE: 4.4.5 Included in Literal
* When a parameter entity reference appears in a literal entity
* value, ... a single or double quote character in the replacement
* text is always treated as a normal data character and will not
* terminate the literal.
* In practice it means we stop the loop only when back at parsing
* the initial entity and the quote is found
*/
while ((IS_CHAR(c)) && ((c != stop) || /* checked */
(ctxt->input != input))) {
if (len + 5 >= size) {
size *= 2;
buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"realloc of %d byte failed\n", size);
return(NULL);
}
}
COPY_BUF(l,buf,len,c);
NEXTL(l);
/*
* Pop-up of finished entities.
*/
while ((RAW == 0) && (ctxt->inputNr > 1)) /* non input consuming */
xmlPopInput(ctxt);
GROW;
c = CUR_CHAR(l);
if (c == 0) {
GROW;
c = CUR_CHAR(l);
}
}
buf[len] = 0;
/*
* Raise problem w.r.t. '&' and '%' being used in non-entities
* reference constructs. Note Charref will be handled in
* xmlStringDecodeEntities()
*/
cur = buf;
while (*cur != 0) { /* non input consuming */
if ((*cur == '%') || ((*cur == '&') && (cur[1] != '#'))) {
xmlChar *name;
xmlChar tmp = *cur;
cur++;
name = xmlParseStringName(ctxt, &cur);
if ((name == NULL) || (*cur != ';')) {
ctxt->errNo = XML_ERR_ENTITY_CHAR_ERROR;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"EntityValue: '%c' forbidden except for entities references\n",
tmp);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if ((ctxt->inSubset == 1) && (tmp == '%')) {
ctxt->errNo = XML_ERR_ENTITY_PE_INTERNAL;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"EntityValue: PEReferences forbidden in internal subset\n",
tmp);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (name != NULL)
xmlFree(name);
}
cur++;
}
/*
* Then PEReference entities are substituted.
*/
if (c != stop) {
ctxt->errNo = XML_ERR_ENTITY_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "EntityValue: \" expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
xmlFree(buf);
} else {
NEXT;
/*
* NOTE: 4.4.7 Bypassed
* When a general entity reference appears in the EntityValue in
* an entity declaration, it is bypassed and left as is.
* so XML_SUBSTITUTE_REF is not set here.
*/
ret = xmlStringDecodeEntities(ctxt, buf, XML_SUBSTITUTE_PEREF,
0, 0, 0);
if (orig != NULL)
*orig = buf;
else
xmlFree(buf);
}
return(ret);
}
/**
* xmlParseAttValue:
* @ctxt: an XML parser context
*
* parse a value for an attribute
* Note: the parser won't do substitution of entities here, this
* will be handled later in xmlStringGetNodeList
*
* [10] AttValue ::= '"' ([^<&"] | Reference)* '"' |
* "'" ([^<&'] | Reference)* "'"
*
* 3.3.3 Attribute-Value Normalization:
* Before the value of an attribute is passed to the application or
* checked for validity, the XML processor must normalize it as follows:
* - a character reference is processed by appending the referenced
* character to the attribute value
* - an entity reference is processed by recursively processing the
* replacement text of the entity
* - a whitespace character (#x20, #xD, #xA, #x9) is processed by
* appending #x20 to the normalized value, except that only a single
* #x20 is appended for a "#xD#xA" sequence that is part of an external
* parsed entity or the literal entity value of an internal parsed entity
* - other characters are processed by appending them to the normalized value
* If the declared value is not CDATA, then the XML processor must further
* process the normalized attribute value by discarding any leading and
* trailing space (#x20) characters, and by replacing sequences of space
* (#x20) characters by a single space (#x20) character.
* All attributes for which no declaration has been read should be treated
* by a non-validating parser as if declared CDATA.
*
* Returns the AttValue parsed or NULL. The value has to be freed by the caller.
*/
xmlChar *
xmlParseAttValue(xmlParserCtxtPtr ctxt) {
xmlChar limit = 0;
xmlChar *buf = NULL;
int len = 0;
int buf_size = 0;
int c, l;
xmlChar *current = NULL;
xmlEntityPtr ent;
SHRINK;
if (NXT(0) == '"') {
ctxt->instate = XML_PARSER_ATTRIBUTE_VALUE;
limit = '"';
NEXT;
} else if (NXT(0) == '\'') {
limit = '\'';
ctxt->instate = XML_PARSER_ATTRIBUTE_VALUE;
NEXT;
} else {
ctxt->errNo = XML_ERR_ATTRIBUTE_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "AttValue: \" or ' expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(NULL);
}
/*
* allocate a translation buffer.
*/
buf_size = XML_PARSER_BUFFER_SIZE;
buf = (xmlChar *) xmlMalloc(buf_size * sizeof(xmlChar));
if (buf == NULL) {
perror("xmlParseAttValue: malloc failed");
return(NULL);
}
/*
* Ok loop until we reach one of the ending char or a size limit.
*/
c = CUR_CHAR(l);
while (((NXT(0) != limit) && /* checked */
(c != '<')) || (ctxt->token != 0)) {
if (c == 0) break;
if (ctxt->token == '&') {
/*
* The reparsing will be done in xmlStringGetNodeList()
* called by the attribute() function in SAX.c
*/
static xmlChar buffer[6] = "&#38;";
if (len > buf_size - 10) {
growBuffer(buf);
}
current = &buffer[0];
while (*current != 0) { /* non input consuming */
buf[len++] = *current++;
}
ctxt->token = 0;
} else if (c == '&') {
if (NXT(1) == '#') {
int val = xmlParseCharRef(ctxt);
if (val == '&') {
/*
* The reparsing will be done in xmlStringGetNodeList()
* called by the attribute() function in SAX.c
*/
static xmlChar buffer[6] = "&#38;";
if (len > buf_size - 10) {
growBuffer(buf);
}
current = &buffer[0];
while (*current != 0) { /* non input consuming */
buf[len++] = *current++;
}
} else {
if (len > buf_size - 10) {
growBuffer(buf);
}
len += xmlCopyChar(0, &buf[len], val);
}
} else {
ent = xmlParseEntityRef(ctxt);
if ((ent != NULL) &&
(ctxt->replaceEntities != 0)) {
xmlChar *rep;
if (ent->etype != XML_INTERNAL_PREDEFINED_ENTITY) {
rep = xmlStringDecodeEntities(ctxt, ent->content,
XML_SUBSTITUTE_REF, 0, 0, 0);
if (rep != NULL) {
current = rep;
while (*current != 0) { /* non input consuming */
buf[len++] = *current++;
if (len > buf_size - 10) {
growBuffer(buf);
}
}
xmlFree(rep);
}
} else {
if (len > buf_size - 10) {
growBuffer(buf);
}
if (ent->content != NULL)
buf[len++] = ent->content[0];
}
} else if (ent != NULL) {
int i = xmlStrlen(ent->name);
const xmlChar *cur = ent->name;
/*
* This may look absurd but is needed to detect
* entities problems
*/
if ((ent->etype != XML_INTERNAL_PREDEFINED_ENTITY) &&
(ent->content != NULL)) {
xmlChar *rep;
rep = xmlStringDecodeEntities(ctxt, ent->content,
XML_SUBSTITUTE_REF, 0, 0, 0);
if (rep != NULL)
xmlFree(rep);
}
/*
* Just output the reference
*/
buf[len++] = '&';
if (len > buf_size - i - 10) {
growBuffer(buf);
}
for (;i > 0;i--)
buf[len++] = *cur++;
buf[len++] = ';';
}
}
} else {
if ((c == 0x20) || (c == 0xD) || (c == 0xA) || (c == 0x9)) {
COPY_BUF(l,buf,len,0x20);
if (len > buf_size - 10) {
growBuffer(buf);
}
} else {
COPY_BUF(l,buf,len,c);
if (len > buf_size - 10) {
growBuffer(buf);
}
}
NEXTL(l);
}
GROW;
c = CUR_CHAR(l);
}
buf[len++] = 0;
if (RAW == '<') {
ctxt->errNo = XML_ERR_LT_IN_ATTRIBUTE;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Unescaped '<' not allowed in attributes values\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else if (RAW != limit) {
ctxt->errNo = XML_ERR_ATTRIBUTE_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "AttValue: ' expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else
NEXT;
return(buf);
}
/**
* xmlParseSystemLiteral:
* @ctxt: an XML parser context
*
* parse an XML Literal
*
* [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
*
* Returns the SystemLiteral parsed or NULL
*/
xmlChar *
xmlParseSystemLiteral(xmlParserCtxtPtr ctxt) {
xmlChar *buf = NULL;
int len = 0;
int size = XML_PARSER_BUFFER_SIZE;
int cur, l;
xmlChar stop;
int state = ctxt->instate;
int count = 0;
SHRINK;
if (RAW == '"') {
NEXT;
stop = '"';
} else if (RAW == '\'') {
NEXT;
stop = '\'';
} else {
ctxt->errNo = XML_ERR_LITERAL_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"SystemLiteral \" or ' expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(NULL);
}
buf = (xmlChar *) xmlMalloc(size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"malloc of %d byte failed\n", size);
return(NULL);
}
ctxt->instate = XML_PARSER_SYSTEM_LITERAL;
cur = CUR_CHAR(l);
while ((IS_CHAR(cur)) && (cur != stop)) { /* checked */
if (len + 5 >= size) {
size *= 2;
buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"realloc of %d byte failed\n", size);
ctxt->instate = (xmlParserInputState) state;
return(NULL);
}
}
count++;
if (count > 50) {
GROW;
count = 0;
}
COPY_BUF(l,buf,len,cur);
NEXTL(l);
cur = CUR_CHAR(l);
if (cur == 0) {
GROW;
SHRINK;
cur = CUR_CHAR(l);
}
}
buf[len] = 0;
ctxt->instate = (xmlParserInputState) state;
if (!IS_CHAR(cur)) {
ctxt->errNo = XML_ERR_LITERAL_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "Unfinished SystemLiteral\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
NEXT;
}
return(buf);
}
/**
* xmlParsePubidLiteral:
* @ctxt: an XML parser context
*
* parse an XML public literal
*
* [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
*
* Returns the PubidLiteral parsed or NULL.
*/
xmlChar *
xmlParsePubidLiteral(xmlParserCtxtPtr ctxt) {
xmlChar *buf = NULL;
int len = 0;
int size = XML_PARSER_BUFFER_SIZE;
xmlChar cur;
xmlChar stop;
int count = 0;
SHRINK;
if (RAW == '"') {
NEXT;
stop = '"';
} else if (RAW == '\'') {
NEXT;
stop = '\'';
} else {
ctxt->errNo = XML_ERR_LITERAL_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"SystemLiteral \" or ' expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(NULL);
}
buf = (xmlChar *) xmlMalloc(size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"malloc of %d byte failed\n", size);
return(NULL);
}
cur = CUR;
while ((IS_PUBIDCHAR(cur)) && (cur != stop)) { /* checked */
if (len + 1 >= size) {
size *= 2;
buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"realloc of %d byte failed\n", size);
return(NULL);
}
}
buf[len++] = cur;
count++;
if (count > 50) {
GROW;
count = 0;
}
NEXT;
cur = CUR;
if (cur == 0) {
GROW;
SHRINK;
cur = CUR;
}
}
buf[len] = 0;
if (cur != stop) {
ctxt->errNo = XML_ERR_LITERAL_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "Unfinished PubidLiteral\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
NEXT;
}
return(buf);
}
void xmlParseCharDataComplex(xmlParserCtxtPtr ctxt, int cdata);
/**
* xmlParseCharData:
* @ctxt: an XML parser context
* @cdata: int indicating whether we are within a CDATA section
*
* parse a CharData section.
* if we are within a CDATA section ']]>' marks an end of section.
*
* The right angle bracket (>) may be represented using the string "&gt;",
* and must, for compatibility, be escaped using "&gt;" or a character
* reference when it appears in the string "]]>" in content, when that
* string is not marking the end of a CDATA section.
*
* [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
*/
void
xmlParseCharData(xmlParserCtxtPtr ctxt, int cdata) {
const xmlChar *in;
int nbchar = 0;
int line = ctxt->input->line;
int col = ctxt->input->col;
SHRINK;
GROW;
/*
* Accelerated common case where input don't need to be
* modified before passing it to the handler.
*/
if ((ctxt->token == 0) && (!cdata)) {
in = ctxt->input->cur;
do {
get_more:
while (((*in >= 0x20) && (*in != '<') &&
(*in != '&') && (*in <= 0x7F)) || (*in == 0x09))
in++;
if (*in == 0xA) {
ctxt->input->line++;
in++;
while (*in == 0xA) {
ctxt->input->line++;
in++;
}
goto get_more;
}
nbchar = in - ctxt->input->cur;
if (nbchar > 0) {
if (IS_BLANK(*ctxt->input->cur)) {
const xmlChar *tmp = ctxt->input->cur;
ctxt->input->cur = in;
if (areBlanks(ctxt, tmp, nbchar)) {
if (ctxt->sax->ignorableWhitespace != NULL)
ctxt->sax->ignorableWhitespace(ctxt->userData,
tmp, nbchar);
} else {
if (ctxt->sax->characters != NULL)
ctxt->sax->characters(ctxt->userData,
tmp, nbchar);
}
line = ctxt->input->line;
col = ctxt->input->col;
} else {
if (ctxt->sax->characters != NULL)
ctxt->sax->characters(ctxt->userData,
ctxt->input->cur, nbchar);
line = ctxt->input->line;
col = ctxt->input->col;
}
}
ctxt->input->cur = in;
if (*in == 0xD) {
in++;
if (*in == 0xA) {
ctxt->input->cur = in;
in++;
ctxt->input->line++;
continue; /* while */
}
in--;
}
if (*in == '<') {
return;
}
if (*in == '&') {
return;
}
SHRINK;
GROW;
in = ctxt->input->cur;
} while ((*in >= 0x20) && (*in <= 0x7F));
nbchar = 0;
}
ctxt->input->line = line;
ctxt->input->col = col;
xmlParseCharDataComplex(ctxt, cdata);
}
void
xmlParseCharDataComplex(xmlParserCtxtPtr ctxt, int cdata) {
xmlChar buf[XML_PARSER_BIG_BUFFER_SIZE + 5];
int nbchar = 0;
int cur, l;
int count = 0;
SHRINK;
GROW;
cur = CUR_CHAR(l);
while (((cur != '<') || (ctxt->token == '<')) && /* checked */
((cur != '&') || (ctxt->token == '&')) &&
(IS_CHAR(cur))) /* test also done in xmlCurrentChar() */ {
if ((cur == ']') && (NXT(1) == ']') &&
(NXT(2) == '>')) {
if (cdata) break;
else {
ctxt->errNo = XML_ERR_MISPLACED_CDATA_END;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Sequence ']]>' not allowed in content\n");
/* Should this be relaxed ??? I see a "must here */
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
}
COPY_BUF(l,buf,nbchar,cur);
if (nbchar >= XML_PARSER_BIG_BUFFER_SIZE) {
/*
* Ok the segment is to be consumed as chars.
*/
if ((ctxt->sax != NULL) && (!ctxt->disableSAX)) {
if (areBlanks(ctxt, buf, nbchar)) {
if (ctxt->sax->ignorableWhitespace != NULL)
ctxt->sax->ignorableWhitespace(ctxt->userData,
buf, nbchar);
} else {
if (ctxt->sax->characters != NULL)
ctxt->sax->characters(ctxt->userData, buf, nbchar);
}
}
nbchar = 0;
}
count++;
if (count > 50) {
GROW;
count = 0;
}
NEXTL(l);
cur = CUR_CHAR(l);
}
if (nbchar != 0) {
/*
* Ok the segment is to be consumed as chars.
*/
if ((ctxt->sax != NULL) && (!ctxt->disableSAX)) {
if (areBlanks(ctxt, buf, nbchar)) {
if (ctxt->sax->ignorableWhitespace != NULL)
ctxt->sax->ignorableWhitespace(ctxt->userData, buf, nbchar);
} else {
if (ctxt->sax->characters != NULL)
ctxt->sax->characters(ctxt->userData, buf, nbchar);
}
}
}
}
/**
* xmlParseExternalID:
* @ctxt: an XML parser context
* @publicID: a xmlChar** receiving PubidLiteral
* @strict: indicate whether we should restrict parsing to only
* production [75], see NOTE below
*
* Parse an External ID or a Public ID
*
* NOTE: Productions [75] and [83] interract badly since [75] can generate
* 'PUBLIC' S PubidLiteral S SystemLiteral
*
* [75] ExternalID ::= 'SYSTEM' S SystemLiteral
* | 'PUBLIC' S PubidLiteral S SystemLiteral
*
* [83] PublicID ::= 'PUBLIC' S PubidLiteral
*
* Returns the function returns SystemLiteral and in the second
* case publicID receives PubidLiteral, is strict is off
* it is possible to return NULL and have publicID set.
*/
xmlChar *
xmlParseExternalID(xmlParserCtxtPtr ctxt, xmlChar **publicID, int strict) {
xmlChar *URI = NULL;
SHRINK;
*publicID = NULL;
if ((RAW == 'S') && (NXT(1) == 'Y') &&
(NXT(2) == 'S') && (NXT(3) == 'T') &&
(NXT(4) == 'E') && (NXT(5) == 'M')) {
SKIP(6);
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after 'SYSTEM'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
URI = xmlParseSystemLiteral(ctxt);
if (URI == NULL) {
ctxt->errNo = XML_ERR_URI_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseExternalID: SYSTEM, no URI\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
} else if ((RAW == 'P') && (NXT(1) == 'U') &&
(NXT(2) == 'B') && (NXT(3) == 'L') &&
(NXT(4) == 'I') && (NXT(5) == 'C')) {
SKIP(6);
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after 'PUBLIC'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
*publicID = xmlParsePubidLiteral(ctxt);
if (*publicID == NULL) {
ctxt->errNo = XML_ERR_PUBID_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseExternalID: PUBLIC, no Public Identifier\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (strict) {
/*
* We don't handle [83] so "S SystemLiteral" is required.
*/
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after the Public Identifier\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
} else {
/*
* We handle [83] so we return immediately, if
* "S SystemLiteral" is not detected. From a purely parsing
* point of view that's a nice mess.
*/
const xmlChar *ptr;
GROW;
ptr = CUR_PTR;
if (!IS_BLANK(*ptr)) return(NULL);
while (IS_BLANK(*ptr)) ptr++; /* TODO: dangerous, fix ! */
if ((*ptr != '\'') && (*ptr != '"')) return(NULL);
}
SKIP_BLANKS;
URI = xmlParseSystemLiteral(ctxt);
if (URI == NULL) {
ctxt->errNo = XML_ERR_URI_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseExternalID: PUBLIC, no URI\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
}
return(URI);
}
/**
* xmlParseComment:
* @ctxt: an XML parser context
*
* Skip an XML (SGML) comment <!-- .... -->
* The spec says that "For compatibility, the string "--" (double-hyphen)
* must not occur within comments. "
*
* [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
*/
void
xmlParseComment(xmlParserCtxtPtr ctxt) {
xmlChar *buf = NULL;
int len;
int size = XML_PARSER_BUFFER_SIZE;
int q, ql;
int r, rl;
int cur, l;
xmlParserInputState state;
xmlParserInputPtr input = ctxt->input;
int count = 0;
/*
* Check that there is a comment right here.
*/
if ((RAW != '<') || (NXT(1) != '!') ||
(NXT(2) != '-') || (NXT(3) != '-')) return;
state = ctxt->instate;
ctxt->instate = XML_PARSER_COMMENT;
SHRINK;
SKIP(4);
buf = (xmlChar *) xmlMalloc(size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"malloc of %d byte failed\n", size);
ctxt->instate = state;
return;
}
q = CUR_CHAR(ql);
NEXTL(ql);
r = CUR_CHAR(rl);
NEXTL(rl);
cur = CUR_CHAR(l);
len = 0;
while (IS_CHAR(cur) && /* checked */
((cur != '>') ||
(r != '-') || (q != '-'))) {
if ((r == '-') && (q == '-') && (len > 1)) {
ctxt->errNo = XML_ERR_HYPHEN_IN_COMMENT;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Comment must not contain '--' (double-hyphen)`\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (len + 5 >= size) {
size *= 2;
buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"realloc of %d byte failed\n", size);
ctxt->instate = state;
return;
}
}
COPY_BUF(ql,buf,len,q);
q = r;
ql = rl;
r = cur;
rl = l;
count++;
if (count > 50) {
GROW;
count = 0;
}
NEXTL(l);
cur = CUR_CHAR(l);
if (cur == 0) {
SHRINK;
GROW;
cur = CUR_CHAR(l);
}
}
buf[len] = 0;
if (!IS_CHAR(cur)) {
ctxt->errNo = XML_ERR_COMMENT_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Comment not terminated \n<!--%.50s\n", buf);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
xmlFree(buf);
} else {
if (input != ctxt->input) {
ctxt->errNo = XML_ERR_ENTITY_BOUNDARY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Comment doesn't start and stop in the same entity\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
NEXT;
if ((ctxt->sax != NULL) && (ctxt->sax->comment != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->comment(ctxt->userData, buf);
xmlFree(buf);
}
ctxt->instate = state;
}
/**
* xmlParsePITarget:
* @ctxt: an XML parser context
*
* parse the name of a PI
*
* [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
*
* Returns the PITarget name or NULL
*/
xmlChar *
xmlParsePITarget(xmlParserCtxtPtr ctxt) {
xmlChar *name;
name = xmlParseName(ctxt);
if ((name != NULL) &&
((name[0] == 'x') || (name[0] == 'X')) &&
((name[1] == 'm') || (name[1] == 'M')) &&
((name[2] == 'l') || (name[2] == 'L'))) {
int i;
if ((name[0] == 'x') && (name[1] == 'm') &&
(name[2] == 'l') && (name[3] == 0)) {
ctxt->errNo = XML_ERR_RESERVED_XML_NAME;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"XML declaration allowed only at the start of the document\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(name);
} else if (name[3] == 0) {
ctxt->errNo = XML_ERR_RESERVED_XML_NAME;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "Invalid PI name\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(name);
}
for (i = 0;;i++) {
if (xmlW3CPIs[i] == NULL) break;
if (xmlStrEqual(name, (const xmlChar *)xmlW3CPIs[i]))
return(name);
}
if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL)) {
ctxt->errNo = XML_ERR_RESERVED_XML_NAME;
ctxt->sax->warning(ctxt->userData,
"xmlParsePItarget: invalid name prefix 'xml'\n");
}
}
return(name);
}
#ifdef LIBXML_CATALOG_ENABLED
/**
* xmlParseCatalogPI:
* @ctxt: an XML parser context
* @catalog: the PI value string
*
* parse an XML Catalog Processing Instruction.
*
* <?oasis-xml-catalog catalog="http://example.com/catalog.xml"?>
*
* Occurs only if allowed by the user and if happening in the Misc
* part of the document before any doctype informations
* This will add the given catalog to the parsing context in order
* to be used if there is a resolution need further down in the document
*/
static void
xmlParseCatalogPI(xmlParserCtxtPtr ctxt, const xmlChar *catalog) {
xmlChar *URL = NULL;
const xmlChar *tmp, *base;
xmlChar marker;
tmp = catalog;
while (IS_BLANK(*tmp)) tmp++;
if (xmlStrncmp(tmp, BAD_CAST"catalog", 7))
goto error;
tmp += 7;
while (IS_BLANK(*tmp)) tmp++;
if (*tmp != '=') {
return;
}
tmp++;
while (IS_BLANK(*tmp)) tmp++;
marker = *tmp;
if ((marker != '\'') && (marker != '"'))
goto error;
tmp++;
base = tmp;
while ((*tmp != 0) && (*tmp != marker)) tmp++;
if (*tmp == 0)
goto error;
URL = xmlStrndup(base, tmp - base);
tmp++;
while (IS_BLANK(*tmp)) tmp++;
if (*tmp != 0)
goto error;
if (URL != NULL) {
ctxt->catalogs = xmlCatalogAddLocal(ctxt->catalogs, URL);
xmlFree(URL);
}
return;
error:
ctxt->errNo = XML_WAR_CATALOG_PI;
if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
ctxt->sax->warning(ctxt->userData,
"Catalog PI syntax error: %s\n", catalog);
if (URL != NULL)
xmlFree(URL);
}
#endif
/**
* xmlParsePI:
* @ctxt: an XML parser context
*
* parse an XML Processing Instruction.
*
* [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
*
* The processing is transfered to SAX once parsed.
*/
void
xmlParsePI(xmlParserCtxtPtr ctxt) {
xmlChar *buf = NULL;
int len = 0;
int size = XML_PARSER_BUFFER_SIZE;
int cur, l;
xmlChar *target;
xmlParserInputState state;
int count = 0;
if ((RAW == '<') && (NXT(1) == '?')) {
xmlParserInputPtr input = ctxt->input;
state = ctxt->instate;
ctxt->instate = XML_PARSER_PI;
/*
* this is a Processing Instruction.
*/
SKIP(2);
SHRINK;
/*
* Parse the target name and check for special support like
* namespace.
*/
target = xmlParsePITarget(ctxt);
if (target != NULL) {
if ((RAW == '?') && (NXT(1) == '>')) {
if (input != ctxt->input) {
ctxt->errNo = XML_ERR_ENTITY_BOUNDARY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"PI declaration doesn't start and stop in the same entity\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP(2);
/*
* SAX: PI detected.
*/
if ((ctxt->sax) && (!ctxt->disableSAX) &&
(ctxt->sax->processingInstruction != NULL))
ctxt->sax->processingInstruction(ctxt->userData,
target, NULL);
ctxt->instate = state;
xmlFree(target);
return;
}
buf = (xmlChar *) xmlMalloc(size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"malloc of %d byte failed\n", size);
ctxt->instate = state;
return;
}
cur = CUR;
if (!IS_BLANK(cur)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParsePI: PI %s space expected\n", target);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
cur = CUR_CHAR(l);
while (IS_CHAR(cur) && /* checked */
((cur != '?') || (NXT(1) != '>'))) {
if (len + 5 >= size) {
size *= 2;
buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"realloc of %d byte failed\n", size);
ctxt->instate = state;
return;
}
}
count++;
if (count > 50) {
GROW;
count = 0;
}
COPY_BUF(l,buf,len,cur);
NEXTL(l);
cur = CUR_CHAR(l);
if (cur == 0) {
SHRINK;
GROW;
cur = CUR_CHAR(l);
}
}
buf[len] = 0;
if (cur != '?') {
ctxt->errNo = XML_ERR_PI_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParsePI: PI %s never end ...\n", target);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
if (input != ctxt->input) {
ctxt->errNo = XML_ERR_ENTITY_BOUNDARY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"PI declaration doesn't start and stop in the same entity\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP(2);
#ifdef LIBXML_CATALOG_ENABLED
if (((state == XML_PARSER_MISC) ||
(state == XML_PARSER_START)) &&
(xmlStrEqual(target, XML_CATALOG_PI))) {
xmlCatalogAllow allow = xmlCatalogGetDefaults();
if ((allow == XML_CATA_ALLOW_DOCUMENT) ||
(allow == XML_CATA_ALLOW_ALL))
xmlParseCatalogPI(ctxt, buf);
}
#endif
/*
* SAX: PI detected.
*/
if ((ctxt->sax) && (!ctxt->disableSAX) &&
(ctxt->sax->processingInstruction != NULL))
ctxt->sax->processingInstruction(ctxt->userData,
target, buf);
}
xmlFree(buf);
xmlFree(target);
} else {
ctxt->errNo = XML_ERR_PI_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParsePI : no target name\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
ctxt->instate = state;
}
}
/**
* xmlParseNotationDecl:
* @ctxt: an XML parser context
*
* parse a notation declaration
*
* [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID | PublicID) S? '>'
*
* Hence there is actually 3 choices:
* 'PUBLIC' S PubidLiteral
* 'PUBLIC' S PubidLiteral S SystemLiteral
* and 'SYSTEM' S SystemLiteral
*
* See the NOTE on xmlParseExternalID().
*/
void
xmlParseNotationDecl(xmlParserCtxtPtr ctxt) {
xmlChar *name;
xmlChar *Pubid;
xmlChar *Systemid;
if ((RAW == '<') && (NXT(1) == '!') &&
(NXT(2) == 'N') && (NXT(3) == 'O') &&
(NXT(4) == 'T') && (NXT(5) == 'A') &&
(NXT(6) == 'T') && (NXT(7) == 'I') &&
(NXT(8) == 'O') && (NXT(9) == 'N')) {
xmlParserInputPtr input = ctxt->input;
SHRINK;
SKIP(10);
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after '<!NOTATION'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return;
}
SKIP_BLANKS;
name = xmlParseName(ctxt);
if (name == NULL) {
ctxt->errNo = XML_ERR_NOTATION_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"NOTATION: Name expected here\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return;
}
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after the NOTATION name'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return;
}
SKIP_BLANKS;
/*
* Parse the IDs.
*/
Systemid = xmlParseExternalID(ctxt, &Pubid, 0);
SKIP_BLANKS;
if (RAW == '>') {
if (input != ctxt->input) {
ctxt->errNo = XML_ERR_ENTITY_BOUNDARY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Notation declaration doesn't start and stop in the same entity\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
NEXT;
if ((ctxt->sax != NULL) && (!ctxt->disableSAX) &&
(ctxt->sax->notationDecl != NULL))
ctxt->sax->notationDecl(ctxt->userData, name, Pubid, Systemid);
} else {
ctxt->errNo = XML_ERR_NOTATION_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"'>' required to close NOTATION declaration\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
xmlFree(name);
if (Systemid != NULL) xmlFree(Systemid);
if (Pubid != NULL) xmlFree(Pubid);
}
}
/**
* xmlParseEntityDecl:
* @ctxt: an XML parser context
*
* parse <!ENTITY declarations
*
* [70] EntityDecl ::= GEDecl | PEDecl
*
* [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>'
*
* [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>'
*
* [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?)
*
* [74] PEDef ::= EntityValue | ExternalID
*
* [76] NDataDecl ::= S 'NDATA' S Name
*
* [ VC: Notation Declared ]
* The Name must match the declared name of a notation.
*/
void
xmlParseEntityDecl(xmlParserCtxtPtr ctxt) {
xmlChar *name = NULL;
xmlChar *value = NULL;
xmlChar *URI = NULL, *literal = NULL;
xmlChar *ndata = NULL;
int isParameter = 0;
xmlChar *orig = NULL;
GROW;
if ((RAW == '<') && (NXT(1) == '!') &&
(NXT(2) == 'E') && (NXT(3) == 'N') &&
(NXT(4) == 'T') && (NXT(5) == 'I') &&
(NXT(6) == 'T') && (NXT(7) == 'Y')) {
xmlParserInputPtr input = ctxt->input;
ctxt->instate = XML_PARSER_ENTITY_DECL;
SHRINK;
SKIP(8);
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after '<!ENTITY'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
if (RAW == '%') {
NEXT;
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after '%'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
isParameter = 1;
}
name = xmlParseName(ctxt);
if (name == NULL) {
ctxt->errNo = XML_ERR_NAME_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "xmlParseEntityDecl: no name\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return;
}
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after the entity name\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
/*
* handle the various case of definitions...
*/
if (isParameter) {
if ((RAW == '"') || (RAW == '\'')) {
value = xmlParseEntityValue(ctxt, &orig);
if (value) {
if ((ctxt->sax != NULL) &&
(!ctxt->disableSAX) && (ctxt->sax->entityDecl != NULL))
ctxt->sax->entityDecl(ctxt->userData, name,
XML_INTERNAL_PARAMETER_ENTITY,
NULL, NULL, value);
}
} else {
URI = xmlParseExternalID(ctxt, &literal, 1);
if ((URI == NULL) && (literal == NULL)) {
ctxt->errNo = XML_ERR_VALUE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Entity value required\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (URI) {
xmlURIPtr uri;
uri = xmlParseURI((const char *) URI);
if (uri == NULL) {
ctxt->errNo = XML_ERR_INVALID_URI;
if ((ctxt->sax != NULL) &&
(!ctxt->disableSAX) &&
(ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Invalid URI: %s\n", URI);
ctxt->wellFormed = 0;
} else {
if (uri->fragment != NULL) {
ctxt->errNo = XML_ERR_URI_FRAGMENT;
if ((ctxt->sax != NULL) &&
(!ctxt->disableSAX) &&
(ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Fragment not allowed: %s\n", URI);
ctxt->wellFormed = 0;
} else {
if ((ctxt->sax != NULL) &&
(!ctxt->disableSAX) &&
(ctxt->sax->entityDecl != NULL))
ctxt->sax->entityDecl(ctxt->userData, name,
XML_EXTERNAL_PARAMETER_ENTITY,
literal, URI, NULL);
}
xmlFreeURI(uri);
}
}
}
} else {
if ((RAW == '"') || (RAW == '\'')) {
value = xmlParseEntityValue(ctxt, &orig);
if ((ctxt->sax != NULL) &&
(!ctxt->disableSAX) && (ctxt->sax->entityDecl != NULL))
ctxt->sax->entityDecl(ctxt->userData, name,
XML_INTERNAL_GENERAL_ENTITY,
NULL, NULL, value);
} else {
URI = xmlParseExternalID(ctxt, &literal, 1);
if ((URI == NULL) && (literal == NULL)) {
ctxt->errNo = XML_ERR_VALUE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Entity value required\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (URI) {
xmlURIPtr uri;
uri = xmlParseURI((const char *)URI);
if (uri == NULL) {
ctxt->errNo = XML_ERR_INVALID_URI;
if ((ctxt->sax != NULL) &&
(!ctxt->disableSAX) &&
(ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Invalid URI: %s\n", URI);
ctxt->wellFormed = 0;
} else {
if (uri->fragment != NULL) {
ctxt->errNo = XML_ERR_URI_FRAGMENT;
if ((ctxt->sax != NULL) &&
(!ctxt->disableSAX) &&
(ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Fragment not allowed: %s\n", URI);
ctxt->wellFormed = 0;
}
xmlFreeURI(uri);
}
}
if ((RAW != '>') && (!IS_BLANK(CUR))) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required before 'NDATA'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
if ((RAW == 'N') && (NXT(1) == 'D') &&
(NXT(2) == 'A') && (NXT(3) == 'T') &&
(NXT(4) == 'A')) {
SKIP(5);
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after 'NDATA'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
ndata = xmlParseName(ctxt);
if ((ctxt->sax != NULL) && (!ctxt->disableSAX) &&
(ctxt->sax->unparsedEntityDecl != NULL))
ctxt->sax->unparsedEntityDecl(ctxt->userData, name,
literal, URI, ndata);
} else {
if ((ctxt->sax != NULL) &&
(!ctxt->disableSAX) && (ctxt->sax->entityDecl != NULL))
ctxt->sax->entityDecl(ctxt->userData, name,
XML_EXTERNAL_GENERAL_PARSED_ENTITY,
literal, URI, NULL);
}
}
}
SKIP_BLANKS;
if (RAW != '>') {
ctxt->errNo = XML_ERR_ENTITY_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseEntityDecl: entity %s not terminated\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
if (input != ctxt->input) {
ctxt->errNo = XML_ERR_ENTITY_BOUNDARY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Entity declaration doesn't start and stop in the same entity\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
NEXT;
}
if (orig != NULL) {
/*
* Ugly mechanism to save the raw entity value.
*/
xmlEntityPtr cur = NULL;
if (isParameter) {
if ((ctxt->sax != NULL) &&
(ctxt->sax->getParameterEntity != NULL))
cur = ctxt->sax->getParameterEntity(ctxt->userData, name);
} else {
if ((ctxt->sax != NULL) &&
(ctxt->sax->getEntity != NULL))
cur = ctxt->sax->getEntity(ctxt->userData, name);
}
if (cur != NULL) {
if (cur->orig != NULL)
xmlFree(orig);
else
cur->orig = orig;
} else
xmlFree(orig);
}
if (name != NULL) xmlFree(name);
if (value != NULL) xmlFree(value);
if (URI != NULL) xmlFree(URI);
if (literal != NULL) xmlFree(literal);
if (ndata != NULL) xmlFree(ndata);
}
}
/**
* xmlParseDefaultDecl:
* @ctxt: an XML parser context
* @value: Receive a possible fixed default value for the attribute
*
* Parse an attribute default declaration
*
* [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
*
* [ VC: Required Attribute ]
* if the default declaration is the keyword #REQUIRED, then the
* attribute must be specified for all elements of the type in the
* attribute-list declaration.
*
* [ VC: Attribute Default Legal ]
* The declared default value must meet the lexical constraints of
* the declared attribute type c.f. xmlValidateAttributeDecl()
*
* [ VC: Fixed Attribute Default ]
* if an attribute has a default value declared with the #FIXED
* keyword, instances of that attribute must match the default value.
*
* [ WFC: No < in Attribute Values ]
* handled in xmlParseAttValue()
*
* returns: XML_ATTRIBUTE_NONE, XML_ATTRIBUTE_REQUIRED, XML_ATTRIBUTE_IMPLIED
* or XML_ATTRIBUTE_FIXED.
*/
int
xmlParseDefaultDecl(xmlParserCtxtPtr ctxt, xmlChar **value) {
int val;
xmlChar *ret;
*value = NULL;
if ((RAW == '#') && (NXT(1) == 'R') &&
(NXT(2) == 'E') && (NXT(3) == 'Q') &&
(NXT(4) == 'U') && (NXT(5) == 'I') &&
(NXT(6) == 'R') && (NXT(7) == 'E') &&
(NXT(8) == 'D')) {
SKIP(9);
return(XML_ATTRIBUTE_REQUIRED);
}
if ((RAW == '#') && (NXT(1) == 'I') &&
(NXT(2) == 'M') && (NXT(3) == 'P') &&
(NXT(4) == 'L') && (NXT(5) == 'I') &&
(NXT(6) == 'E') && (NXT(7) == 'D')) {
SKIP(8);
return(XML_ATTRIBUTE_IMPLIED);
}
val = XML_ATTRIBUTE_NONE;
if ((RAW == '#') && (NXT(1) == 'F') &&
(NXT(2) == 'I') && (NXT(3) == 'X') &&
(NXT(4) == 'E') && (NXT(5) == 'D')) {
SKIP(6);
val = XML_ATTRIBUTE_FIXED;
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after '#FIXED'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
}
ret = xmlParseAttValue(ctxt);
ctxt->instate = XML_PARSER_DTD;
if (ret == NULL) {
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Attribute default value declaration error\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else
*value = ret;
return(val);
}
/**
* xmlParseNotationType:
* @ctxt: an XML parser context
*
* parse an Notation attribute type.
*
* Note: the leading 'NOTATION' S part has already being parsed...
*
* [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')'
*
* [ VC: Notation Attributes ]
* Values of this type must match one of the notation names included
* in the declaration; all notation names in the declaration must be declared.
*
* Returns: the notation attribute tree built while parsing
*/
xmlEnumerationPtr
xmlParseNotationType(xmlParserCtxtPtr ctxt) {
xmlChar *name;
xmlEnumerationPtr ret = NULL, last = NULL, cur;
if (RAW != '(') {
ctxt->errNo = XML_ERR_NOTATION_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"'(' required to start 'NOTATION'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(NULL);
}
SHRINK;
do {
NEXT;
SKIP_BLANKS;
name = xmlParseName(ctxt);
if (name == NULL) {
ctxt->errNo = XML_ERR_NAME_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Name expected in NOTATION declaration\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(ret);
}
cur = xmlCreateEnumeration(name);
xmlFree(name);
if (cur == NULL) return(ret);
if (last == NULL) ret = last = cur;
else {
last->next = cur;
last = cur;
}
SKIP_BLANKS;
} while (RAW == '|');
if (RAW != ')') {
ctxt->errNo = XML_ERR_NOTATION_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"')' required to finish NOTATION declaration\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
if ((last != NULL) && (last != ret))
xmlFreeEnumeration(last);
return(ret);
}
NEXT;
return(ret);
}
/**
* xmlParseEnumerationType:
* @ctxt: an XML parser context
*
* parse an Enumeration attribute type.
*
* [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
*
* [ VC: Enumeration ]
* Values of this type must match one of the Nmtoken tokens in
* the declaration
*
* Returns: the enumeration attribute tree built while parsing
*/
xmlEnumerationPtr
xmlParseEnumerationType(xmlParserCtxtPtr ctxt) {
xmlChar *name;
xmlEnumerationPtr ret = NULL, last = NULL, cur;
if (RAW != '(') {
ctxt->errNo = XML_ERR_ATTLIST_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"'(' required to start ATTLIST enumeration\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(NULL);
}
SHRINK;
do {
NEXT;
SKIP_BLANKS;
name = xmlParseNmtoken(ctxt);
if (name == NULL) {
ctxt->errNo = XML_ERR_NMTOKEN_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"NmToken expected in ATTLIST enumeration\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(ret);
}
cur = xmlCreateEnumeration(name);
xmlFree(name);
if (cur == NULL) return(ret);
if (last == NULL) ret = last = cur;
else {
last->next = cur;
last = cur;
}
SKIP_BLANKS;
} while (RAW == '|');
if (RAW != ')') {
ctxt->errNo = XML_ERR_ATTLIST_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"')' required to finish ATTLIST enumeration\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(ret);
}
NEXT;
return(ret);
}
/**
* xmlParseEnumeratedType:
* @ctxt: an XML parser context
* @tree: the enumeration tree built while parsing
*
* parse an Enumerated attribute type.
*
* [57] EnumeratedType ::= NotationType | Enumeration
*
* [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')'
*
*
* Returns: XML_ATTRIBUTE_ENUMERATION or XML_ATTRIBUTE_NOTATION
*/
int
xmlParseEnumeratedType(xmlParserCtxtPtr ctxt, xmlEnumerationPtr *tree) {
if ((RAW == 'N') && (NXT(1) == 'O') &&
(NXT(2) == 'T') && (NXT(3) == 'A') &&
(NXT(4) == 'T') && (NXT(5) == 'I') &&
(NXT(6) == 'O') && (NXT(7) == 'N')) {
SKIP(8);
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after 'NOTATION'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(0);
}
SKIP_BLANKS;
*tree = xmlParseNotationType(ctxt);
if (*tree == NULL) return(0);
return(XML_ATTRIBUTE_NOTATION);
}
*tree = xmlParseEnumerationType(ctxt);
if (*tree == NULL) return(0);
return(XML_ATTRIBUTE_ENUMERATION);
}
/**
* xmlParseAttributeType:
* @ctxt: an XML parser context
* @tree: the enumeration tree built while parsing
*
* parse the Attribute list def for an element
*
* [54] AttType ::= StringType | TokenizedType | EnumeratedType
*
* [55] StringType ::= 'CDATA'
*
* [56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' |
* 'ENTITIES' | 'NMTOKEN' | 'NMTOKENS'
*
* Validity constraints for attribute values syntax are checked in
* xmlValidateAttributeValue()
*
* [ VC: ID ]
* Values of type ID must match the Name production. A name must not
* appear more than once in an XML document as a value of this type;
* i.e., ID values must uniquely identify the elements which bear them.
*
* [ VC: One ID per Element Type ]
* No element type may have more than one ID attribute specified.
*
* [ VC: ID Attribute Default ]
* An ID attribute must have a declared default of #IMPLIED or #REQUIRED.
*
* [ VC: IDREF ]
* Values of type IDREF must match the Name production, and values
* of type IDREFS must match Names; each IDREF Name must match the value
* of an ID attribute on some element in the XML document; i.e. IDREF
* values must match the value of some ID attribute.
*
* [ VC: Entity Name ]
* Values of type ENTITY must match the Name production, values
* of type ENTITIES must match Names; each Entity Name must match the
* name of an unparsed entity declared in the DTD.
*
* [ VC: Name Token ]
* Values of type NMTOKEN must match the Nmtoken production; values
* of type NMTOKENS must match Nmtokens.
*
* Returns the attribute type
*/
int
xmlParseAttributeType(xmlParserCtxtPtr ctxt, xmlEnumerationPtr *tree) {
SHRINK;
if ((RAW == 'C') && (NXT(1) == 'D') &&
(NXT(2) == 'A') && (NXT(3) == 'T') &&
(NXT(4) == 'A')) {
SKIP(5);
return(XML_ATTRIBUTE_CDATA);
} else if ((RAW == 'I') && (NXT(1) == 'D') &&
(NXT(2) == 'R') && (NXT(3) == 'E') &&
(NXT(4) == 'F') && (NXT(5) == 'S')) {
SKIP(6);
return(XML_ATTRIBUTE_IDREFS);
} else if ((RAW == 'I') && (NXT(1) == 'D') &&
(NXT(2) == 'R') && (NXT(3) == 'E') &&
(NXT(4) == 'F')) {
SKIP(5);
return(XML_ATTRIBUTE_IDREF);
} else if ((RAW == 'I') && (NXT(1) == 'D')) {
SKIP(2);
return(XML_ATTRIBUTE_ID);
} else if ((RAW == 'E') && (NXT(1) == 'N') &&
(NXT(2) == 'T') && (NXT(3) == 'I') &&
(NXT(4) == 'T') && (NXT(5) == 'Y')) {
SKIP(6);
return(XML_ATTRIBUTE_ENTITY);
} else if ((RAW == 'E') && (NXT(1) == 'N') &&
(NXT(2) == 'T') && (NXT(3) == 'I') &&
(NXT(4) == 'T') && (NXT(5) == 'I') &&
(NXT(6) == 'E') && (NXT(7) == 'S')) {
SKIP(8);
return(XML_ATTRIBUTE_ENTITIES);
} else if ((RAW == 'N') && (NXT(1) == 'M') &&
(NXT(2) == 'T') && (NXT(3) == 'O') &&
(NXT(4) == 'K') && (NXT(5) == 'E') &&
(NXT(6) == 'N') && (NXT(7) == 'S')) {
SKIP(8);
return(XML_ATTRIBUTE_NMTOKENS);
} else if ((RAW == 'N') && (NXT(1) == 'M') &&
(NXT(2) == 'T') && (NXT(3) == 'O') &&
(NXT(4) == 'K') && (NXT(5) == 'E') &&
(NXT(6) == 'N')) {
SKIP(7);
return(XML_ATTRIBUTE_NMTOKEN);
}
return(xmlParseEnumeratedType(ctxt, tree));
}
/**
* xmlParseAttributeListDecl:
* @ctxt: an XML parser context
*
* : parse the Attribute list def for an element
*
* [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>'
*
* [53] AttDef ::= S Name S AttType S DefaultDecl
*
*/
void
xmlParseAttributeListDecl(xmlParserCtxtPtr ctxt) {
xmlChar *elemName;
xmlChar *attrName;
xmlEnumerationPtr tree;
if ((RAW == '<') && (NXT(1) == '!') &&
(NXT(2) == 'A') && (NXT(3) == 'T') &&
(NXT(4) == 'T') && (NXT(5) == 'L') &&
(NXT(6) == 'I') && (NXT(7) == 'S') &&
(NXT(8) == 'T')) {
xmlParserInputPtr input = ctxt->input;
SKIP(9);
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after '<!ATTLIST'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
elemName = xmlParseName(ctxt);
if (elemName == NULL) {
ctxt->errNo = XML_ERR_NAME_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"ATTLIST: no name for Element\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return;
}
SKIP_BLANKS;
GROW;
while (RAW != '>') {
const xmlChar *check = CUR_PTR;
int type;
int def;
xmlChar *defaultValue = NULL;
GROW;
tree = NULL;
attrName = xmlParseName(ctxt);
if (attrName == NULL) {
ctxt->errNo = XML_ERR_NAME_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"ATTLIST: no name for Attribute\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
break;
}
GROW;
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after the attribute name\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
if (attrName != NULL)
xmlFree(attrName);
if (defaultValue != NULL)
xmlFree(defaultValue);
break;
}
SKIP_BLANKS;
type = xmlParseAttributeType(ctxt, &tree);
if (type <= 0) {
if (attrName != NULL)
xmlFree(attrName);
if (defaultValue != NULL)
xmlFree(defaultValue);
break;
}
GROW;
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after the attribute type\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
if (attrName != NULL)
xmlFree(attrName);
if (defaultValue != NULL)
xmlFree(defaultValue);
if (tree != NULL)
xmlFreeEnumeration(tree);
break;
}
SKIP_BLANKS;
def = xmlParseDefaultDecl(ctxt, &defaultValue);
if (def <= 0) {
if (attrName != NULL)
xmlFree(attrName);
if (defaultValue != NULL)
xmlFree(defaultValue);
if (tree != NULL)
xmlFreeEnumeration(tree);
break;
}
GROW;
if (RAW != '>') {
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after the attribute default value\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
if (attrName != NULL)
xmlFree(attrName);
if (defaultValue != NULL)
xmlFree(defaultValue);
if (tree != NULL)
xmlFreeEnumeration(tree);
break;
}
SKIP_BLANKS;
}
if (check == CUR_PTR) {
ctxt->errNo = XML_ERR_INTERNAL_ERROR;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseAttributeListDecl: detected internal error\n");
if (attrName != NULL)
xmlFree(attrName);
if (defaultValue != NULL)
xmlFree(defaultValue);
if (tree != NULL)
xmlFreeEnumeration(tree);
break;
}
if ((ctxt->sax != NULL) && (!ctxt->disableSAX) &&
(ctxt->sax->attributeDecl != NULL))
ctxt->sax->attributeDecl(ctxt->userData, elemName, attrName,
type, def, defaultValue, tree);
if (attrName != NULL)
xmlFree(attrName);
if (defaultValue != NULL)
xmlFree(defaultValue);
GROW;
}
if (RAW == '>') {
if (input != ctxt->input) {
ctxt->errNo = XML_ERR_ENTITY_BOUNDARY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Attribute list declaration doesn't start and stop in the same entity\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
NEXT;
}
xmlFree(elemName);
}
}
/**
* xmlParseElementMixedContentDecl:
* @ctxt: an XML parser context
*
* parse the declaration for a Mixed Element content
* The leading '(' and spaces have been skipped in xmlParseElementContentDecl
*
* [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*' |
* '(' S? '#PCDATA' S? ')'
*
* [ VC: Proper Group/PE Nesting ] applies to [51] too (see [49])
*
* [ VC: No Duplicate Types ]
* The same name must not appear more than once in a single
* mixed-content declaration.
*
* returns: the list of the xmlElementContentPtr describing the element choices
*/
xmlElementContentPtr
xmlParseElementMixedContentDecl(xmlParserCtxtPtr ctxt) {
xmlElementContentPtr ret = NULL, cur = NULL, n;
xmlChar *elem = NULL;
GROW;
if ((RAW == '#') && (NXT(1) == 'P') &&
(NXT(2) == 'C') && (NXT(3) == 'D') &&
(NXT(4) == 'A') && (NXT(5) == 'T') &&
(NXT(6) == 'A')) {
SKIP(7);
SKIP_BLANKS;
SHRINK;
if (RAW == ')') {
ctxt->entity = ctxt->input;
NEXT;
ret = xmlNewElementContent(NULL, XML_ELEMENT_CONTENT_PCDATA);
if (RAW == '*') {
ret->ocur = XML_ELEMENT_CONTENT_MULT;
NEXT;
}
return(ret);
}
if ((RAW == '(') || (RAW == '|')) {
ret = cur = xmlNewElementContent(NULL, XML_ELEMENT_CONTENT_PCDATA);
if (ret == NULL) return(NULL);
}
while (RAW == '|') {
NEXT;
if (elem == NULL) {
ret = xmlNewElementContent(NULL, XML_ELEMENT_CONTENT_OR);
if (ret == NULL) return(NULL);
ret->c1 = cur;
if (cur != NULL)
cur->parent = ret;
cur = ret;
} else {
n = xmlNewElementContent(NULL, XML_ELEMENT_CONTENT_OR);
if (n == NULL) return(NULL);
n->c1 = xmlNewElementContent(elem, XML_ELEMENT_CONTENT_ELEMENT);
if (n->c1 != NULL)
n->c1->parent = n;
cur->c2 = n;
if (n != NULL)
n->parent = cur;
cur = n;
xmlFree(elem);
}
SKIP_BLANKS;
elem = xmlParseName(ctxt);
if (elem == NULL) {
ctxt->errNo = XML_ERR_NAME_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseElementMixedContentDecl : Name expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
xmlFreeElementContent(cur);
return(NULL);
}
SKIP_BLANKS;
GROW;
}
if ((RAW == ')') && (NXT(1) == '*')) {
if (elem != NULL) {
cur->c2 = xmlNewElementContent(elem,
XML_ELEMENT_CONTENT_ELEMENT);
if (cur->c2 != NULL)
cur->c2->parent = cur;
xmlFree(elem);
}
ret->ocur = XML_ELEMENT_CONTENT_MULT;
ctxt->entity = ctxt->input;
SKIP(2);
} else {
if (elem != NULL) xmlFree(elem);
xmlFreeElementContent(ret);
ctxt->errNo = XML_ERR_MIXED_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseElementMixedContentDecl : '|' or ')*' expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(NULL);
}
} else {
ctxt->errNo = XML_ERR_PCDATA_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseElementMixedContentDecl : '#PCDATA' expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
return(ret);
}
/**
* xmlParseElementChildrenContentD:
* @ctxt: an XML parser context
*
* VMS version of xmlParseElementChildrenContentDecl()
*
* Returns the tree of xmlElementContentPtr describing the element
* hierarchy.
*/
/**
* xmlParseElementChildrenContentDecl:
* @ctxt: an XML parser context
*
* parse the declaration for a Mixed Element content
* The leading '(' and spaces have been skipped in xmlParseElementContentDecl
*
*
* [47] children ::= (choice | seq) ('?' | '*' | '+')?
*
* [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')?
*
* [49] choice ::= '(' S? cp ( S? '|' S? cp )* S? ')'
*
* [50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')'
*
* [ VC: Proper Group/PE Nesting ] applies to [49] and [50]
* TODO Parameter-entity replacement text must be properly nested
* with parenthetized groups. That is to say, if either of the
* opening or closing parentheses in a choice, seq, or Mixed
* construct is contained in the replacement text for a parameter
* entity, both must be contained in the same replacement text. For
* interoperability, if a parameter-entity reference appears in a
* choice, seq, or Mixed construct, its replacement text should not
* be empty, and neither the first nor last non-blank character of
* the replacement text should be a connector (| or ,).
*
* Returns the tree of xmlElementContentPtr describing the element
* hierarchy.
*/
xmlElementContentPtr
#ifdef VMS
xmlParseElementChildrenContentD
#else
xmlParseElementChildrenContentDecl
#endif
(xmlParserCtxtPtr ctxt) {
xmlElementContentPtr ret = NULL, cur = NULL, last = NULL, op = NULL;
xmlChar *elem;
xmlChar type = 0;
SKIP_BLANKS;
GROW;
if (RAW == '(') {
/* Recurse on first child */
NEXT;
SKIP_BLANKS;
cur = ret = xmlParseElementChildrenContentDecl(ctxt);
SKIP_BLANKS;
GROW;
} else {
elem = xmlParseName(ctxt);
if (elem == NULL) {
ctxt->errNo = XML_ERR_ELEMCONTENT_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseElementChildrenContentDecl : Name or '(' expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(NULL);
}
cur = ret = xmlNewElementContent(elem, XML_ELEMENT_CONTENT_ELEMENT);
GROW;
if (RAW == '?') {
cur->ocur = XML_ELEMENT_CONTENT_OPT;
NEXT;
} else if (RAW == '*') {
cur->ocur = XML_ELEMENT_CONTENT_MULT;
NEXT;
} else if (RAW == '+') {
cur->ocur = XML_ELEMENT_CONTENT_PLUS;
NEXT;
} else {
cur->ocur = XML_ELEMENT_CONTENT_ONCE;
}
xmlFree(elem);
GROW;
}
SKIP_BLANKS;
SHRINK;
while (RAW != ')') {
/*
* Each loop we parse one separator and one element.
*/
if (RAW == ',') {
if (type == 0) type = CUR;
/*
* Detect "Name | Name , Name" error
*/
else if (type != CUR) {
ctxt->errNo = XML_ERR_SEPARATOR_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseElementChildrenContentDecl : '%c' expected\n",
type);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
if ((op != NULL) && (op != ret))
xmlFreeElementContent(op);
if ((last != NULL) && (last != ret) &&
(last != ret->c1) && (last != ret->c2))
xmlFreeElementContent(last);
if (ret != NULL)
xmlFreeElementContent(ret);
return(NULL);
}
NEXT;
op = xmlNewElementContent(NULL, XML_ELEMENT_CONTENT_SEQ);
if (op == NULL) {
xmlFreeElementContent(ret);
return(NULL);
}
if (last == NULL) {
op->c1 = ret;
if (ret != NULL)
ret->parent = op;
ret = cur = op;
} else {
cur->c2 = op;
if (op != NULL)
op->parent = cur;
op->c1 = last;
if (last != NULL)
last->parent = op;
cur =op;
last = NULL;
}
} else if (RAW == '|') {
if (type == 0) type = CUR;
/*
* Detect "Name , Name | Name" error
*/
else if (type != CUR) {
ctxt->errNo = XML_ERR_SEPARATOR_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseElementChildrenContentDecl : '%c' expected\n",
type);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
if ((op != NULL) && (op != ret) && (op != last))
xmlFreeElementContent(op);
if ((last != NULL) && (last != ret) &&
(last != ret->c1) && (last != ret->c2))
xmlFreeElementContent(last);
if (ret != NULL)
xmlFreeElementContent(ret);
return(NULL);
}
NEXT;
op = xmlNewElementContent(NULL, XML_ELEMENT_CONTENT_OR);
if (op == NULL) {
if ((op != NULL) && (op != ret))
xmlFreeElementContent(op);
if ((last != NULL) && (last != ret) &&
(last != ret->c1) && (last != ret->c2))
xmlFreeElementContent(last);
if (ret != NULL)
xmlFreeElementContent(ret);
return(NULL);
}
if (last == NULL) {
op->c1 = ret;
if (ret != NULL)
ret->parent = op;
ret = cur = op;
} else {
cur->c2 = op;
if (op != NULL)
op->parent = cur;
op->c1 = last;
if (last != NULL)
last->parent = op;
cur =op;
last = NULL;
}
} else {
ctxt->errNo = XML_ERR_ELEMCONTENT_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseElementChildrenContentDecl : ',' '|' or ')' expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
if ((op != NULL) && (op != ret))
xmlFreeElementContent(op);
if ((last != NULL) && (last != ret) &&
(last != ret->c1) && (last != ret->c2))
xmlFreeElementContent(last);
if (ret != NULL)
xmlFreeElementContent(ret);
return(NULL);
}
GROW;
SKIP_BLANKS;
GROW;
if (RAW == '(') {
/* Recurse on second child */
NEXT;
SKIP_BLANKS;
last = xmlParseElementChildrenContentDecl(ctxt);
SKIP_BLANKS;
} else {
elem = xmlParseName(ctxt);
if (elem == NULL) {
ctxt->errNo = XML_ERR_ELEMCONTENT_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseElementChildrenContentDecl : Name or '(' expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
if ((op != NULL) && (op != ret))
xmlFreeElementContent(op);
if ((last != NULL) && (last != ret) &&
(last != ret->c1) && (last != ret->c2))
xmlFreeElementContent(last);
if (ret != NULL)
xmlFreeElementContent(ret);
return(NULL);
}
last = xmlNewElementContent(elem, XML_ELEMENT_CONTENT_ELEMENT);
xmlFree(elem);
if (RAW == '?') {
last->ocur = XML_ELEMENT_CONTENT_OPT;
NEXT;
} else if (RAW == '*') {
last->ocur = XML_ELEMENT_CONTENT_MULT;
NEXT;
} else if (RAW == '+') {
last->ocur = XML_ELEMENT_CONTENT_PLUS;
NEXT;
} else {
last->ocur = XML_ELEMENT_CONTENT_ONCE;
}
}
SKIP_BLANKS;
GROW;
}
if ((cur != NULL) && (last != NULL)) {
cur->c2 = last;
if (last != NULL)
last->parent = cur;
}
ctxt->entity = ctxt->input;
NEXT;
if (RAW == '?') {
if (ret != NULL)
ret->ocur = XML_ELEMENT_CONTENT_OPT;
NEXT;
} else if (RAW == '*') {
if (ret != NULL)
ret->ocur = XML_ELEMENT_CONTENT_MULT;
NEXT;
} else if (RAW == '+') {
if (ret != NULL)
ret->ocur = XML_ELEMENT_CONTENT_PLUS;
NEXT;
}
return(ret);
}
/**
* xmlParseElementContentDecl:
* @ctxt: an XML parser context
* @name: the name of the element being defined.
* @result: the Element Content pointer will be stored here if any
*
* parse the declaration for an Element content either Mixed or Children,
* the cases EMPTY and ANY are handled directly in xmlParseElementDecl
*
* [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children
*
* returns: the type of element content XML_ELEMENT_TYPE_xxx
*/
int
xmlParseElementContentDecl(xmlParserCtxtPtr ctxt, xmlChar *name,
xmlElementContentPtr *result) {
xmlElementContentPtr tree = NULL;
xmlParserInputPtr input = ctxt->input;
int res;
*result = NULL;
if (RAW != '(') {
ctxt->errNo = XML_ERR_ELEMCONTENT_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseElementContentDecl : %s '(' expected\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(-1);
}
NEXT;
GROW;
SKIP_BLANKS;
if ((RAW == '#') && (NXT(1) == 'P') &&
(NXT(2) == 'C') && (NXT(3) == 'D') &&
(NXT(4) == 'A') && (NXT(5) == 'T') &&
(NXT(6) == 'A')) {
tree = xmlParseElementMixedContentDecl(ctxt);
res = XML_ELEMENT_TYPE_MIXED;
} else {
tree = xmlParseElementChildrenContentDecl(ctxt);
res = XML_ELEMENT_TYPE_ELEMENT;
}
if ((ctxt->entity != NULL) && (input != ctxt->entity)) {
ctxt->errNo = XML_ERR_ENTITY_BOUNDARY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Element content declaration doesn't start and stop in the same entity\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
*result = tree;
return(res);
}
/**
* xmlParseElementDecl:
* @ctxt: an XML parser context
*
* parse an Element declaration.
*
* [45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>'
*
* [ VC: Unique Element Type Declaration ]
* No element type may be declared more than once
*
* Returns the type of the element, or -1 in case of error
*/
int
xmlParseElementDecl(xmlParserCtxtPtr ctxt) {
xmlChar *name;
int ret = -1;
xmlElementContentPtr content = NULL;
GROW;
if ((RAW == '<') && (NXT(1) == '!') &&
(NXT(2) == 'E') && (NXT(3) == 'L') &&
(NXT(4) == 'E') && (NXT(5) == 'M') &&
(NXT(6) == 'E') && (NXT(7) == 'N') &&
(NXT(8) == 'T')) {
xmlParserInputPtr input = ctxt->input;
SKIP(9);
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after 'ELEMENT'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
name = xmlParseName(ctxt);
if (name == NULL) {
ctxt->errNo = XML_ERR_NAME_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseElementDecl: no name for Element\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(-1);
}
while ((RAW == 0) && (ctxt->inputNr > 1))
xmlPopInput(ctxt);
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space required after the element name\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
if ((RAW == 'E') && (NXT(1) == 'M') &&
(NXT(2) == 'P') && (NXT(3) == 'T') &&
(NXT(4) == 'Y')) {
SKIP(5);
/*
* Element must always be empty.
*/
ret = XML_ELEMENT_TYPE_EMPTY;
} else if ((RAW == 'A') && (NXT(1) == 'N') &&
(NXT(2) == 'Y')) {
SKIP(3);
/*
* Element is a generic container.
*/
ret = XML_ELEMENT_TYPE_ANY;
} else if (RAW == '(') {
ret = xmlParseElementContentDecl(ctxt, name, &content);
} else {
/*
* [ WFC: PEs in Internal Subset ] error handling.
*/
if ((RAW == '%') && (ctxt->external == 0) &&
(ctxt->inputNr == 1)) {
ctxt->errNo = XML_ERR_PEREF_IN_INT_SUBSET;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"PEReference: forbidden within markup decl in internal subset\n");
} else {
ctxt->errNo = XML_ERR_ELEMCONTENT_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseElementDecl: 'EMPTY', 'ANY' or '(' expected\n");
}
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
if (name != NULL) xmlFree(name);
return(-1);
}
SKIP_BLANKS;
/*
* Pop-up of finished entities.
*/
while ((RAW == 0) && (ctxt->inputNr > 1))
xmlPopInput(ctxt);
SKIP_BLANKS;
if (RAW != '>') {
ctxt->errNo = XML_ERR_GT_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseElementDecl: expected '>' at the end\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
if (input != ctxt->input) {
ctxt->errNo = XML_ERR_ENTITY_BOUNDARY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Element declaration doesn't start and stop in the same entity\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
NEXT;
if ((ctxt->sax != NULL) && (!ctxt->disableSAX) &&
(ctxt->sax->elementDecl != NULL))
ctxt->sax->elementDecl(ctxt->userData, name, ret,
content);
}
if (content != NULL) {
xmlFreeElementContent(content);
}
if (name != NULL) {
xmlFree(name);
}
}
return(ret);
}
/**
* xmlParseConditionalSections
* @ctxt: an XML parser context
*
* [61] conditionalSect ::= includeSect | ignoreSect
* [62] includeSect ::= '<![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>'
* [63] ignoreSect ::= '<![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>'
* [64] ignoreSectContents ::= Ignore ('<![' ignoreSectContents ']]>' Ignore)*
* [65] Ignore ::= Char* - (Char* ('<![' | ']]>') Char*)
*/
static void
xmlParseConditionalSections(xmlParserCtxtPtr ctxt) {
SKIP(3);
SKIP_BLANKS;
if ((RAW == 'I') && (NXT(1) == 'N') && (NXT(2) == 'C') &&
(NXT(3) == 'L') && (NXT(4) == 'U') && (NXT(5) == 'D') &&
(NXT(6) == 'E')) {
SKIP(7);
SKIP_BLANKS;
if (RAW != '[') {
ctxt->errNo = XML_ERR_CONDSEC_INVALID;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"XML conditional section '[' expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
NEXT;
}
if (xmlParserDebugEntities) {
if ((ctxt->input != NULL) && (ctxt->input->filename))
xmlGenericError(xmlGenericErrorContext,
"%s(%d): ", ctxt->input->filename,
ctxt->input->line);
xmlGenericError(xmlGenericErrorContext,
"Entering INCLUDE Conditional Section\n");
}
while ((RAW != 0) && ((RAW != ']') || (NXT(1) != ']') ||
(NXT(2) != '>'))) {
const xmlChar *check = CUR_PTR;
int cons = ctxt->input->consumed;
int tok = ctxt->token;
if ((RAW == '<') && (NXT(1) == '!') && (NXT(2) == '[')) {
xmlParseConditionalSections(ctxt);
} else if (IS_BLANK(CUR)) {
NEXT;
} else if (RAW == '%') {
xmlParsePEReference(ctxt);
} else
xmlParseMarkupDecl(ctxt);
/*
* Pop-up of finished entities.
*/
while ((RAW == 0) && (ctxt->inputNr > 1))
xmlPopInput(ctxt);
if ((CUR_PTR == check) && (cons == ctxt->input->consumed) &&
(tok == ctxt->token)) {
ctxt->errNo = XML_ERR_EXT_SUBSET_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Content error in the external subset\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
break;
}
}
if (xmlParserDebugEntities) {
if ((ctxt->input != NULL) && (ctxt->input->filename))
xmlGenericError(xmlGenericErrorContext,
"%s(%d): ", ctxt->input->filename,
ctxt->input->line);
xmlGenericError(xmlGenericErrorContext,
"Leaving INCLUDE Conditional Section\n");
}
} else if ((RAW == 'I') && (NXT(1) == 'G') && (NXT(2) == 'N') &&
(NXT(3) == 'O') && (NXT(4) == 'R') && (NXT(5) == 'E')) {
int state;
int instate;
int depth = 0;
SKIP(6);
SKIP_BLANKS;
if (RAW != '[') {
ctxt->errNo = XML_ERR_CONDSEC_INVALID;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"XML conditional section '[' expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
NEXT;
}
if (xmlParserDebugEntities) {
if ((ctxt->input != NULL) && (ctxt->input->filename))
xmlGenericError(xmlGenericErrorContext,
"%s(%d): ", ctxt->input->filename,
ctxt->input->line);
xmlGenericError(xmlGenericErrorContext,
"Entering IGNORE Conditional Section\n");
}
/*
* Parse up to the end of the conditionnal section
* But disable SAX event generating DTD building in the meantime
*/
state = ctxt->disableSAX;
instate = ctxt->instate;
ctxt->disableSAX = 1;
ctxt->instate = XML_PARSER_IGNORE;
while (depth >= 0) {
if ((RAW == '<') && (NXT(1) == '!') && (NXT(2) == '[')) {
depth++;
SKIP(3);
continue;
}
if ((RAW == ']') && (NXT(1) == ']') && (NXT(2) == '>')) {
if (--depth >= 0) SKIP(3);
continue;
}
NEXT;
continue;
}
ctxt->disableSAX = state;
ctxt->instate = instate;
if (xmlParserDebugEntities) {
if ((ctxt->input != NULL) && (ctxt->input->filename))
xmlGenericError(xmlGenericErrorContext,
"%s(%d): ", ctxt->input->filename,
ctxt->input->line);
xmlGenericError(xmlGenericErrorContext,
"Leaving IGNORE Conditional Section\n");
}
} else {
ctxt->errNo = XML_ERR_CONDSEC_INVALID;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"XML conditional section INCLUDE or IGNORE keyword expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (RAW == 0)
SHRINK;
if (RAW == 0) {
ctxt->errNo = XML_ERR_CONDSEC_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"XML conditional section not closed\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
SKIP(3);
}
}
/**
* xmlParseMarkupDecl:
* @ctxt: an XML parser context
*
* parse Markup declarations
*
* [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl |
* NotationDecl | PI | Comment
*
* [ VC: Proper Declaration/PE Nesting ]
* Parameter-entity replacement text must be properly nested with
* markup declarations. That is to say, if either the first character
* or the last character of a markup declaration (markupdecl above) is
* contained in the replacement text for a parameter-entity reference,
* both must be contained in the same replacement text.
*
* [ WFC: PEs in Internal Subset ]
* In the internal DTD subset, parameter-entity references can occur
* only where markup declarations can occur, not within markup declarations.
* (This does not apply to references that occur in external parameter
* entities or to the external subset.)
*/
void
xmlParseMarkupDecl(xmlParserCtxtPtr ctxt) {
GROW;
xmlParseElementDecl(ctxt);
xmlParseAttributeListDecl(ctxt);
xmlParseEntityDecl(ctxt);
xmlParseNotationDecl(ctxt);
xmlParsePI(ctxt);
xmlParseComment(ctxt);
/*
* This is only for internal subset. On external entities,
* the replacement is done before parsing stage
*/
if ((ctxt->external == 0) && (ctxt->inputNr == 1))
xmlParsePEReference(ctxt);
/*
* Conditional sections are allowed from entities included
* by PE References in the internal subset.
*/
if ((ctxt->external == 0) && (ctxt->inputNr > 1)) {
if ((RAW == '<') && (NXT(1) == '!') && (NXT(2) == '[')) {
xmlParseConditionalSections(ctxt);
}
}
ctxt->instate = XML_PARSER_DTD;
}
/**
* xmlParseTextDecl:
* @ctxt: an XML parser context
*
* parse an XML declaration header for external entities
*
* [77] TextDecl ::= '<?xml' VersionInfo? EncodingDecl S? '?>'
*
* Question: Seems that EncodingDecl is mandatory ? Is that a typo ?
*/
void
xmlParseTextDecl(xmlParserCtxtPtr ctxt) {
xmlChar *version;
/*
* We know that '<?xml' is here.
*/
if ((RAW == '<') && (NXT(1) == '?') &&
(NXT(2) == 'x') && (NXT(3) == 'm') &&
(NXT(4) == 'l') && (IS_BLANK(NXT(5)))) {
SKIP(5);
} else {
ctxt->errNo = XML_ERR_XMLDECL_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Text declaration '<?xml' required\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return;
}
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Space needed after '<?xml'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
/*
* We may have the VersionInfo here.
*/
version = xmlParseVersionInfo(ctxt);
if (version == NULL)
version = xmlCharStrdup(XML_DEFAULT_VERSION);
ctxt->input->version = version;
/*
* We must have the encoding declaration
*/
if (!IS_BLANK(CUR)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "Space needed here\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
xmlParseEncodingDecl(ctxt);
if (ctxt->errNo == XML_ERR_UNSUPPORTED_ENCODING) {
/*
* The XML REC instructs us to stop parsing right here
*/
return;
}
SKIP_BLANKS;
if ((RAW == '?') && (NXT(1) == '>')) {
SKIP(2);
} else if (RAW == '>') {
/* Deprecated old WD ... */
ctxt->errNo = XML_ERR_XMLDECL_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"XML declaration must end-up with '?>'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
NEXT;
} else {
ctxt->errNo = XML_ERR_XMLDECL_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"parsing XML declaration: '?>' expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
MOVETO_ENDTAG(CUR_PTR);
NEXT;
}
}
/**
* xmlParseExternalSubset:
* @ctxt: an XML parser context
* @ExternalID: the external identifier
* @SystemID: the system identifier (or URL)
*
* parse Markup declarations from an external subset
*
* [30] extSubset ::= textDecl? extSubsetDecl
*
* [31] extSubsetDecl ::= (markupdecl | conditionalSect | PEReference | S) *
*/
void
xmlParseExternalSubset(xmlParserCtxtPtr ctxt, const xmlChar *ExternalID,
const xmlChar *SystemID) {
GROW;
if ((RAW == '<') && (NXT(1) == '?') &&
(NXT(2) == 'x') && (NXT(3) == 'm') &&
(NXT(4) == 'l')) {
xmlParseTextDecl(ctxt);
if (ctxt->errNo == XML_ERR_UNSUPPORTED_ENCODING) {
/*
* The XML REC instructs us to stop parsing right here
*/
ctxt->instate = XML_PARSER_EOF;
return;
}
}
if (ctxt->myDoc == NULL) {
ctxt->myDoc = xmlNewDoc(BAD_CAST "1.0");
}
if ((ctxt->myDoc != NULL) && (ctxt->myDoc->intSubset == NULL))
xmlCreateIntSubset(ctxt->myDoc, NULL, ExternalID, SystemID);
ctxt->instate = XML_PARSER_DTD;
ctxt->external = 1;
while (((RAW == '<') && (NXT(1) == '?')) ||
((RAW == '<') && (NXT(1) == '!')) ||
(RAW == '%') || IS_BLANK(CUR)) {
const xmlChar *check = CUR_PTR;
int cons = ctxt->input->consumed;
int tok = ctxt->token;
GROW;
if ((RAW == '<') && (NXT(1) == '!') && (NXT(2) == '[')) {
xmlParseConditionalSections(ctxt);
} else if (IS_BLANK(CUR)) {
NEXT;
} else if (RAW == '%') {
xmlParsePEReference(ctxt);
} else
xmlParseMarkupDecl(ctxt);
/*
* Pop-up of finished entities.
*/
while ((RAW == 0) && (ctxt->inputNr > 1))
xmlPopInput(ctxt);
if ((CUR_PTR == check) && (cons == ctxt->input->consumed) &&
(tok == ctxt->token)) {
ctxt->errNo = XML_ERR_EXT_SUBSET_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Content error in the external subset\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
break;
}
}
if (RAW != 0) {
ctxt->errNo = XML_ERR_EXT_SUBSET_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Extra content at the end of the document\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
}
/**
* xmlParseReference:
* @ctxt: an XML parser context
*
* parse and handle entity references in content, depending on the SAX
* interface, this may end-up in a call to character() if this is a
* CharRef, a predefined entity, if there is no reference() callback.
* or if the parser was asked to switch to that mode.
*
* [67] Reference ::= EntityRef | CharRef
*/
void
xmlParseReference(xmlParserCtxtPtr ctxt) {
xmlEntityPtr ent;
xmlChar *val;
if (RAW != '&') return;
if (NXT(1) == '#') {
int i = 0;
xmlChar out[10];
int hex = NXT(2);
int value = xmlParseCharRef(ctxt);
if (ctxt->charset != XML_CHAR_ENCODING_UTF8) {
/*
* So we are using non-UTF-8 buffers
* Check that the char fit on 8bits, if not
* generate a CharRef.
*/
if (value <= 0xFF) {
out[0] = value;
out[1] = 0;
if ((ctxt->sax != NULL) && (ctxt->sax->characters != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->characters(ctxt->userData, out, 1);
} else {
if ((hex == 'x') || (hex == 'X'))
sprintf((char *)out, "#x%X", value);
else
sprintf((char *)out, "#%d", value);
if ((ctxt->sax != NULL) && (ctxt->sax->reference != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->reference(ctxt->userData, out);
}
} else {
/*
* Just encode the value in UTF-8
*/
COPY_BUF(0 ,out, i, value);
out[i] = 0;
if ((ctxt->sax != NULL) && (ctxt->sax->characters != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->characters(ctxt->userData, out, i);
}
} else {
ent = xmlParseEntityRef(ctxt);
if (ent == NULL) return;
if ((ent->name != NULL) &&
(ent->etype != XML_INTERNAL_PREDEFINED_ENTITY)) {
xmlNodePtr list = NULL;
int ret;
/*
* The first reference to the entity trigger a parsing phase
* where the ent->children is filled with the result from
* the parsing.
*/
if (ent->children == NULL) {
xmlChar *value;
value = ent->content;
/*
* Check that this entity is well formed
*/
if ((value != NULL) &&
(value[1] == 0) && (value[0] == '<') &&
(xmlStrEqual(ent->name, BAD_CAST "lt"))) {
/*
* DONE: get definite answer on this !!!
* Lots of entity decls are used to declare a single
* char
* <!ENTITY lt "<">
* Which seems to be valid since
* 2.4: The ampersand character (&) and the left angle
* bracket (<) may appear in their literal form only
* when used ... They are also legal within the literal
* entity value of an internal entity declaration;i
* see "4.3.2 Well-Formed Parsed Entities".
* IMHO 2.4 and 4.3.2 are directly in contradiction.
* Looking at the OASIS test suite and James Clark
* tests, this is broken. However the XML REC uses
* it. Is the XML REC not well-formed ????
* This is a hack to avoid this problem
*
* ANSWER: since lt gt amp .. are already defined,
* this is a redefinition and hence the fact that the
* contentis not well balanced is not a Wf error, this
* is lousy but acceptable.
*/
list = xmlNewDocText(ctxt->myDoc, value);
if (list != NULL) {
if ((ent->etype == XML_INTERNAL_GENERAL_ENTITY) &&
(ent->children == NULL)) {
ent->children = list;
ent->last = list;
list->parent = (xmlNodePtr) ent;
} else {
xmlFreeNodeList(list);
}
} else if (list != NULL) {
xmlFreeNodeList(list);
}
} else {
/*
* 4.3.2: An internal general parsed entity is well-formed
* if its replacement text matches the production labeled
* content.
*/
if (ent->etype == XML_INTERNAL_GENERAL_ENTITY) {
ctxt->depth++;
ret = xmlParseBalancedChunkMemory(ctxt->myDoc,
ctxt->sax, NULL, ctxt->depth,
value, &list);
ctxt->depth--;
} else if (ent->etype ==
XML_EXTERNAL_GENERAL_PARSED_ENTITY) {
ctxt->depth++;
ret = xmlParseExternalEntityPrivate(ctxt->myDoc, ctxt,
ctxt->sax, NULL, ctxt->depth,
ent->URI, ent->ExternalID, &list);
ctxt->depth--;
} else {
ret = -1;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Internal: invalid entity type\n");
}
if (ret == XML_ERR_ENTITY_LOOP) {
ctxt->errNo = XML_ERR_ENTITY_LOOP;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Detected entity reference loop\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else if ((ret == 0) && (list != NULL)) {
if (((ent->etype == XML_INTERNAL_GENERAL_ENTITY) ||
(ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY))&&
(ent->children == NULL)) {
ent->children = list;
if (ctxt->replaceEntities) {
/*
* Prune it directly in the generated document
* except for single text nodes.
*/
if ((list->type == XML_TEXT_NODE) &&
(list->next == NULL)) {
list->parent = (xmlNodePtr) ent;
list = NULL;
} else {
while (list != NULL) {
list->parent = (xmlNodePtr) ctxt->node;
if (list->next == NULL)
ent->last = list;
list = list->next;
}
list = ent->children;
}
} else {
while (list != NULL) {
list->parent = (xmlNodePtr) ent;
if (list->next == NULL)
ent->last = list;
list = list->next;
}
}
} else {
xmlFreeNodeList(list);
list = NULL;
}
} else if (ret > 0) {
ctxt->errNo = ret;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Entity value required\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else if (list != NULL) {
xmlFreeNodeList(list);
list = NULL;
}
}
}
if ((ctxt->sax != NULL) && (ctxt->sax->reference != NULL) &&
(ctxt->replaceEntities == 0) && (!ctxt->disableSAX)) {
/*
* Create a node.
*/
ctxt->sax->reference(ctxt->userData, ent->name);
return;
} else if (ctxt->replaceEntities) {
if ((ctxt->node != NULL) && (ent->children != NULL)) {
/*
* Seems we are generating the DOM content, do
* a simple tree copy for all references except the first
* In the first occurence list contains the replacement
*/
if (list == NULL) {
xmlNodePtr new, cur;
cur = ent->children;
while (cur != NULL) {
new = xmlCopyNode(cur, 1);
xmlAddChild(ctxt->node, new);
if (cur == ent->last)
break;
cur = cur->next;
}
} else {
/*
* the name change is to avoid coalescing of the
* node with a prossible previous text one which
* would make ent->children a dandling pointer
*/
if (ent->children->type == XML_TEXT_NODE)
ent->children->name = xmlStrdup(BAD_CAST "nbktext");
if ((ent->last != ent->children) &&
(ent->last->type == XML_TEXT_NODE))
ent->last->name = xmlStrdup(BAD_CAST "nbktext");
xmlAddChildList(ctxt->node, ent->children);
}
/*
* This is to avoid a nasty side effect, see
* characters() in SAX.c
*/
ctxt->nodemem = 0;
ctxt->nodelen = 0;
return;
} else {
/*
* Probably running in SAX mode
*/
xmlParserInputPtr input;
input = xmlNewEntityInputStream(ctxt, ent);
xmlPushInput(ctxt, input);
if ((ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY) &&
(RAW == '<') && (NXT(1) == '?') &&
(NXT(2) == 'x') && (NXT(3) == 'm') &&
(NXT(4) == 'l') && (IS_BLANK(NXT(5)))) {
xmlParseTextDecl(ctxt);
if (ctxt->errNo == XML_ERR_UNSUPPORTED_ENCODING) {
/*
* The XML REC instructs us to stop parsing right here
*/
ctxt->instate = XML_PARSER_EOF;
return;
}
if (input->standalone == 1) {
ctxt->errNo = XML_ERR_EXT_ENTITY_STANDALONE;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"external parsed entities cannot be standalone\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
}
return;
}
}
} else {
val = ent->content;
if (val == NULL) return;
/*
* inline the entity.
*/
if ((ctxt->sax != NULL) && (ctxt->sax->characters != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->characters(ctxt->userData, val, xmlStrlen(val));
}
}
}
/**
* xmlParseEntityRef:
* @ctxt: an XML parser context
*
* parse ENTITY references declarations
*
* [68] EntityRef ::= '&' Name ';'
*
* [ WFC: Entity Declared ]
* In a document without any DTD, a document with only an internal DTD
* subset which contains no parameter entity references, or a document
* with "standalone='yes'", the Name given in the entity reference
* must match that in an entity declaration, except that well-formed
* documents need not declare any of the following entities: amp, lt,
* gt, apos, quot. The declaration of a parameter entity must precede
* any reference to it. Similarly, the declaration of a general entity
* must precede any reference to it which appears in a default value in an
* attribute-list declaration. Note that if entities are declared in the
* external subset or in external parameter entities, a non-validating
* processor is not obligated to read and process their declarations;
* for such documents, the rule that an entity must be declared is a
* well-formedness constraint only if standalone='yes'.
*
* [ WFC: Parsed Entity ]
* An entity reference must not contain the name of an unparsed entity
*
* Returns the xmlEntityPtr if found, or NULL otherwise.
*/
xmlEntityPtr
xmlParseEntityRef(xmlParserCtxtPtr ctxt) {
xmlChar *name;
xmlEntityPtr ent = NULL;
GROW;
if (RAW == '&') {
NEXT;
name = xmlParseName(ctxt);
if (name == NULL) {
ctxt->errNo = XML_ERR_NAME_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseEntityRef: no name\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
if (RAW == ';') {
NEXT;
/*
* Ask first SAX for entity resolution, otherwise try the
* predefined set.
*/
if (ctxt->sax != NULL) {
if (ctxt->sax->getEntity != NULL)
ent = ctxt->sax->getEntity(ctxt->userData, name);
if (ent == NULL)
ent = xmlGetPredefinedEntity(name);
}
/*
* [ WFC: Entity Declared ]
* In a document without any DTD, a document with only an
* internal DTD subset which contains no parameter entity
* references, or a document with "standalone='yes'", the
* Name given in the entity reference must match that in an
* entity declaration, except that well-formed documents
* need not declare any of the following entities: amp, lt,
* gt, apos, quot.
* The declaration of a parameter entity must precede any
* reference to it.
* Similarly, the declaration of a general entity must
* precede any reference to it which appears in a default
* value in an attribute-list declaration. Note that if
* entities are declared in the external subset or in
* external parameter entities, a non-validating processor
* is not obligated to read and process their declarations;
* for such documents, the rule that an entity must be
* declared is a well-formedness constraint only if
* standalone='yes'.
*/
if (ent == NULL) {
if ((ctxt->standalone == 1) ||
((ctxt->hasExternalSubset == 0) &&
(ctxt->hasPErefs == 0))) {
ctxt->errNo = XML_ERR_UNDECLARED_ENTITY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Entity '%s' not defined\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
ctxt->errNo = XML_WAR_UNDECLARED_ENTITY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Entity '%s' not defined\n", name);
}
}
/*
* [ WFC: Parsed Entity ]
* An entity reference must not contain the name of an
* unparsed entity
*/
else if (ent->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
ctxt->errNo = XML_ERR_UNPARSED_ENTITY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Entity reference to unparsed entity %s\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
/*
* [ WFC: No External Entity References ]
* Attribute values cannot contain direct or indirect
* entity references to external entities.
*/
else if ((ctxt->instate == XML_PARSER_ATTRIBUTE_VALUE) &&
(ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY)) {
ctxt->errNo = XML_ERR_ENTITY_IS_EXTERNAL;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Attribute references external entity '%s'\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
/*
* [ WFC: No < in Attribute Values ]
* The replacement text of any entity referred to directly or
* indirectly in an attribute value (other than "&lt;") must
* not contain a <.
*/
else if ((ctxt->instate == XML_PARSER_ATTRIBUTE_VALUE) &&
(ent != NULL) &&
(!xmlStrEqual(ent->name, BAD_CAST "lt")) &&
(ent->content != NULL) &&
(xmlStrchr(ent->content, '<'))) {
ctxt->errNo = XML_ERR_LT_IN_ATTRIBUTE;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"'<' in entity '%s' is not allowed in attributes values\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
/*
* Internal check, no parameter entities here ...
*/
else {
switch (ent->etype) {
case XML_INTERNAL_PARAMETER_ENTITY:
case XML_EXTERNAL_PARAMETER_ENTITY:
ctxt->errNo = XML_ERR_ENTITY_IS_PARAMETER;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Attempt to reference the parameter entity '%s'\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
break;
default:
break;
}
}
/*
* [ WFC: No Recursion ]
* A parsed entity must not contain a recursive reference
* to itself, either directly or indirectly.
* Done somewhere else
*/
} else {
ctxt->errNo = XML_ERR_ENTITYREF_SEMICOL_MISSING;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseEntityRef: expecting ';'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
xmlFree(name);
}
}
return(ent);
}
/**
* xmlParseStringEntityRef:
* @ctxt: an XML parser context
* @str: a pointer to an index in the string
*
* parse ENTITY references declarations, but this version parses it from
* a string value.
*
* [68] EntityRef ::= '&' Name ';'
*
* [ WFC: Entity Declared ]
* In a document without any DTD, a document with only an internal DTD
* subset which contains no parameter entity references, or a document
* with "standalone='yes'", the Name given in the entity reference
* must match that in an entity declaration, except that well-formed
* documents need not declare any of the following entities: amp, lt,
* gt, apos, quot. The declaration of a parameter entity must precede
* any reference to it. Similarly, the declaration of a general entity
* must precede any reference to it which appears in a default value in an
* attribute-list declaration. Note that if entities are declared in the
* external subset or in external parameter entities, a non-validating
* processor is not obligated to read and process their declarations;
* for such documents, the rule that an entity must be declared is a
* well-formedness constraint only if standalone='yes'.
*
* [ WFC: Parsed Entity ]
* An entity reference must not contain the name of an unparsed entity
*
* Returns the xmlEntityPtr if found, or NULL otherwise. The str pointer
* is updated to the current location in the string.
*/
xmlEntityPtr
xmlParseStringEntityRef(xmlParserCtxtPtr ctxt, const xmlChar ** str) {
xmlChar *name;
const xmlChar *ptr;
xmlChar cur;
xmlEntityPtr ent = NULL;
if ((str == NULL) || (*str == NULL))
return(NULL);
ptr = *str;
cur = *ptr;
if (cur == '&') {
ptr++;
cur = *ptr;
name = xmlParseStringName(ctxt, &ptr);
if (name == NULL) {
ctxt->errNo = XML_ERR_NAME_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseStringEntityRef: no name\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
if (*ptr == ';') {
ptr++;
/*
* Ask first SAX for entity resolution, otherwise try the
* predefined set.
*/
if (ctxt->sax != NULL) {
if (ctxt->sax->getEntity != NULL)
ent = ctxt->sax->getEntity(ctxt->userData, name);
if (ent == NULL)
ent = xmlGetPredefinedEntity(name);
}
/*
* [ WFC: Entity Declared ]
* In a document without any DTD, a document with only an
* internal DTD subset which contains no parameter entity
* references, or a document with "standalone='yes'", the
* Name given in the entity reference must match that in an
* entity declaration, except that well-formed documents
* need not declare any of the following entities: amp, lt,
* gt, apos, quot.
* The declaration of a parameter entity must precede any
* reference to it.
* Similarly, the declaration of a general entity must
* precede any reference to it which appears in a default
* value in an attribute-list declaration. Note that if
* entities are declared in the external subset or in
* external parameter entities, a non-validating processor
* is not obligated to read and process their declarations;
* for such documents, the rule that an entity must be
* declared is a well-formedness constraint only if
* standalone='yes'.
*/
if (ent == NULL) {
if ((ctxt->standalone == 1) ||
((ctxt->hasExternalSubset == 0) &&
(ctxt->hasPErefs == 0))) {
ctxt->errNo = XML_ERR_UNDECLARED_ENTITY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Entity '%s' not defined\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
ctxt->errNo = XML_WAR_UNDECLARED_ENTITY;
if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
ctxt->sax->warning(ctxt->userData,
"Entity '%s' not defined\n", name);
}
}
/*
* [ WFC: Parsed Entity ]
* An entity reference must not contain the name of an
* unparsed entity
*/
else if (ent->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
ctxt->errNo = XML_ERR_UNPARSED_ENTITY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Entity reference to unparsed entity %s\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
/*
* [ WFC: No External Entity References ]
* Attribute values cannot contain direct or indirect
* entity references to external entities.
*/
else if ((ctxt->instate == XML_PARSER_ATTRIBUTE_VALUE) &&
(ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY)) {
ctxt->errNo = XML_ERR_ENTITY_IS_EXTERNAL;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Attribute references external entity '%s'\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
/*
* [ WFC: No < in Attribute Values ]
* The replacement text of any entity referred to directly or
* indirectly in an attribute value (other than "&lt;") must
* not contain a <.
*/
else if ((ctxt->instate == XML_PARSER_ATTRIBUTE_VALUE) &&
(ent != NULL) &&
(!xmlStrEqual(ent->name, BAD_CAST "lt")) &&
(ent->content != NULL) &&
(xmlStrchr(ent->content, '<'))) {
ctxt->errNo = XML_ERR_LT_IN_ATTRIBUTE;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"'<' in entity '%s' is not allowed in attributes values\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
/*
* Internal check, no parameter entities here ...
*/
else {
switch (ent->etype) {
case XML_INTERNAL_PARAMETER_ENTITY:
case XML_EXTERNAL_PARAMETER_ENTITY:
ctxt->errNo = XML_ERR_ENTITY_IS_PARAMETER;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Attempt to reference the parameter entity '%s'\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
break;
default:
break;
}
}
/*
* [ WFC: No Recursion ]
* A parsed entity must not contain a recursive reference
* to itself, either directly or indirectly.
* Done somewhwere else
*/
} else {
ctxt->errNo = XML_ERR_ENTITYREF_SEMICOL_MISSING;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseStringEntityRef: expecting ';'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
xmlFree(name);
}
}
*str = ptr;
return(ent);
}
/**
* xmlParsePEReference:
* @ctxt: an XML parser context
*
* parse PEReference declarations
* The entity content is handled directly by pushing it's content as
* a new input stream.
*
* [69] PEReference ::= '%' Name ';'
*
* [ WFC: No Recursion ]
* A parsed entity must not contain a recursive
* reference to itself, either directly or indirectly.
*
* [ WFC: Entity Declared ]
* In a document without any DTD, a document with only an internal DTD
* subset which contains no parameter entity references, or a document
* with "standalone='yes'", ... ... The declaration of a parameter
* entity must precede any reference to it...
*
* [ VC: Entity Declared ]
* In a document with an external subset or external parameter entities
* with "standalone='no'", ... ... The declaration of a parameter entity
* must precede any reference to it...
*
* [ WFC: In DTD ]
* Parameter-entity references may only appear in the DTD.
* NOTE: misleading but this is handled.
*/
void
xmlParsePEReference(xmlParserCtxtPtr ctxt) {
xmlChar *name;
xmlEntityPtr entity = NULL;
xmlParserInputPtr input;
if (RAW == '%') {
NEXT;
name = xmlParseName(ctxt);
if (name == NULL) {
ctxt->errNo = XML_ERR_NAME_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParsePEReference: no name\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
if (RAW == ';') {
NEXT;
if ((ctxt->sax != NULL) &&
(ctxt->sax->getParameterEntity != NULL))
entity = ctxt->sax->getParameterEntity(ctxt->userData,
name);
if (entity == NULL) {
/*
* [ WFC: Entity Declared ]
* In a document without any DTD, a document with only an
* internal DTD subset which contains no parameter entity
* references, or a document with "standalone='yes'", ...
* ... The declaration of a parameter entity must precede
* any reference to it...
*/
if ((ctxt->standalone == 1) ||
((ctxt->hasExternalSubset == 0) &&
(ctxt->hasPErefs == 0))) {
ctxt->errNo = XML_ERR_UNDECLARED_ENTITY;
if ((!ctxt->disableSAX) &&
(ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"PEReference: %%%s; not found\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
/*
* [ VC: Entity Declared ]
* In a document with an external subset or external
* parameter entities with "standalone='no'", ...
* ... The declaration of a parameter entity must precede
* any reference to it...
*/
if ((!ctxt->disableSAX) &&
(ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
ctxt->sax->warning(ctxt->userData,
"PEReference: %%%s; not found\n", name);
ctxt->valid = 0;
}
} else {
/*
* Internal checking in case the entity quest barfed
*/
if ((entity->etype != XML_INTERNAL_PARAMETER_ENTITY) &&
(entity->etype != XML_EXTERNAL_PARAMETER_ENTITY)) {
if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
ctxt->sax->warning(ctxt->userData,
"Internal: %%%s; is not a parameter entity\n", name);
} else {
/*
* TODO !!!
* handle the extra spaces added before and after
* c.f. http://www.w3.org/TR/REC-xml#as-PE
*/
input = xmlNewEntityInputStream(ctxt, entity);
xmlPushInput(ctxt, input);
if ((entity->etype == XML_EXTERNAL_PARAMETER_ENTITY) &&
(RAW == '<') && (NXT(1) == '?') &&
(NXT(2) == 'x') && (NXT(3) == 'm') &&
(NXT(4) == 'l') && (IS_BLANK(NXT(5)))) {
xmlParseTextDecl(ctxt);
if (ctxt->errNo == XML_ERR_UNSUPPORTED_ENCODING) {
/*
* The XML REC instructs us to stop parsing
* right here
*/
ctxt->instate = XML_PARSER_EOF;
xmlFree(name);
return;
}
}
if (ctxt->token == 0)
ctxt->token = ' ';
}
}
ctxt->hasPErefs = 1;
} else {
ctxt->errNo = XML_ERR_ENTITYREF_SEMICOL_MISSING;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParsePEReference: expecting ';'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
xmlFree(name);
}
}
}
/**
* xmlParseStringPEReference:
* @ctxt: an XML parser context
* @str: a pointer to an index in the string
*
* parse PEReference declarations
*
* [69] PEReference ::= '%' Name ';'
*
* [ WFC: No Recursion ]
* A parsed entity must not contain a recursive
* reference to itself, either directly or indirectly.
*
* [ WFC: Entity Declared ]
* In a document without any DTD, a document with only an internal DTD
* subset which contains no parameter entity references, or a document
* with "standalone='yes'", ... ... The declaration of a parameter
* entity must precede any reference to it...
*
* [ VC: Entity Declared ]
* In a document with an external subset or external parameter entities
* with "standalone='no'", ... ... The declaration of a parameter entity
* must precede any reference to it...
*
* [ WFC: In DTD ]
* Parameter-entity references may only appear in the DTD.
* NOTE: misleading but this is handled.
*
* Returns the string of the entity content.
* str is updated to the current value of the index
*/
xmlEntityPtr
xmlParseStringPEReference(xmlParserCtxtPtr ctxt, const xmlChar **str) {
const xmlChar *ptr;
xmlChar cur;
xmlChar *name;
xmlEntityPtr entity = NULL;
if ((str == NULL) || (*str == NULL)) return(NULL);
ptr = *str;
cur = *ptr;
if (cur == '%') {
ptr++;
cur = *ptr;
name = xmlParseStringName(ctxt, &ptr);
if (name == NULL) {
ctxt->errNo = XML_ERR_NAME_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseStringPEReference: no name\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
cur = *ptr;
if (cur == ';') {
ptr++;
cur = *ptr;
if ((ctxt->sax != NULL) &&
(ctxt->sax->getParameterEntity != NULL))
entity = ctxt->sax->getParameterEntity(ctxt->userData,
name);
if (entity == NULL) {
/*
* [ WFC: Entity Declared ]
* In a document without any DTD, a document with only an
* internal DTD subset which contains no parameter entity
* references, or a document with "standalone='yes'", ...
* ... The declaration of a parameter entity must precede
* any reference to it...
*/
if ((ctxt->standalone == 1) ||
((ctxt->hasExternalSubset == 0) &&
(ctxt->hasPErefs == 0))) {
ctxt->errNo = XML_ERR_UNDECLARED_ENTITY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"PEReference: %%%s; not found\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else {
/*
* [ VC: Entity Declared ]
* In a document with an external subset or external
* parameter entities with "standalone='no'", ...
* ... The declaration of a parameter entity must
* precede any reference to it...
*/
if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
ctxt->sax->warning(ctxt->userData,
"PEReference: %%%s; not found\n", name);
ctxt->valid = 0;
}
} else {
/*
* Internal checking in case the entity quest barfed
*/
if ((entity->etype != XML_INTERNAL_PARAMETER_ENTITY) &&
(entity->etype != XML_EXTERNAL_PARAMETER_ENTITY)) {
if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
ctxt->sax->warning(ctxt->userData,
"Internal: %%%s; is not a parameter entity\n", name);
}
}
ctxt->hasPErefs = 1;
} else {
ctxt->errNo = XML_ERR_ENTITYREF_SEMICOL_MISSING;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseStringPEReference: expecting ';'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
xmlFree(name);
}
}
*str = ptr;
return(entity);
}
/**
* xmlParseDocTypeDecl:
* @ctxt: an XML parser context
*
* parse a DOCTYPE declaration
*
* [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S?
* ('[' (markupdecl | PEReference | S)* ']' S?)? '>'
*
* [ VC: Root Element Type ]
* The Name in the document type declaration must match the element
* type of the root element.
*/
void
xmlParseDocTypeDecl(xmlParserCtxtPtr ctxt) {
xmlChar *name = NULL;
xmlChar *ExternalID = NULL;
xmlChar *URI = NULL;
/*
* We know that '<!DOCTYPE' has been detected.
*/
SKIP(9);
SKIP_BLANKS;
/*
* Parse the DOCTYPE name.
*/
name = xmlParseName(ctxt);
if (name == NULL) {
ctxt->errNo = XML_ERR_NAME_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseDocTypeDecl : no DOCTYPE name !\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
ctxt->intSubName = name;
SKIP_BLANKS;
/*
* Check for SystemID and ExternalID
*/
URI = xmlParseExternalID(ctxt, &ExternalID, 1);
if ((URI != NULL) || (ExternalID != NULL)) {
ctxt->hasExternalSubset = 1;
}
ctxt->extSubURI = URI;
ctxt->extSubSystem = ExternalID;
SKIP_BLANKS;
/*
* Create and update the internal subset.
*/
if ((ctxt->sax != NULL) && (ctxt->sax->internalSubset != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->internalSubset(ctxt->userData, name, ExternalID, URI);
/*
* Is there any internal subset declarations ?
* they are handled separately in xmlParseInternalSubset()
*/
if (RAW == '[')
return;
/*
* We should be at the end of the DOCTYPE declaration.
*/
if (RAW != '>') {
ctxt->errNo = XML_ERR_DOCTYPE_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "DOCTYPE unproperly terminated\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
NEXT;
}
/**
* xmlParseInternalsubset:
* @ctxt: an XML parser context
*
* parse the internal subset declaration
*
* [28 end] ('[' (markupdecl | PEReference | S)* ']' S?)? '>'
*/
static void
xmlParseInternalSubset(xmlParserCtxtPtr ctxt) {
/*
* Is there any DTD definition ?
*/
if (RAW == '[') {
ctxt->instate = XML_PARSER_DTD;
NEXT;
/*
* Parse the succession of Markup declarations and
* PEReferences.
* Subsequence (markupdecl | PEReference | S)*
*/
while (RAW != ']') {
const xmlChar *check = CUR_PTR;
int cons = ctxt->input->consumed;
SKIP_BLANKS;
xmlParseMarkupDecl(ctxt);
xmlParsePEReference(ctxt);
/*
* Pop-up of finished entities.
*/
while ((RAW == 0) && (ctxt->inputNr > 1))
xmlPopInput(ctxt);
if ((CUR_PTR == check) && (cons == ctxt->input->consumed)) {
ctxt->errNo = XML_ERR_INTERNAL_ERROR;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseInternalSubset: error detected in Markup declaration\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
break;
}
}
if (RAW == ']') {
NEXT;
SKIP_BLANKS;
}
}
/*
* We should be at the end of the DOCTYPE declaration.
*/
if (RAW != '>') {
ctxt->errNo = XML_ERR_DOCTYPE_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "DOCTYPE unproperly terminated\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
NEXT;
}
/**
* xmlParseAttribute:
* @ctxt: an XML parser context
* @value: a xmlChar ** used to store the value of the attribute
*
* parse an attribute
*
* [41] Attribute ::= Name Eq AttValue
*
* [ WFC: No External Entity References ]
* Attribute values cannot contain direct or indirect entity references
* to external entities.
*
* [ WFC: No < in Attribute Values ]
* The replacement text of any entity referred to directly or indirectly in
* an attribute value (other than "&lt;") must not contain a <.
*
* [ VC: Attribute Value Type ]
* The attribute must have been declared; the value must be of the type
* declared for it.
*
* [25] Eq ::= S? '=' S?
*
* With namespace:
*
* [NS 11] Attribute ::= QName Eq AttValue
*
* Also the case QName == xmlns:??? is handled independently as a namespace
* definition.
*
* Returns the attribute name, and the value in *value.
*/
xmlChar *
xmlParseAttribute(xmlParserCtxtPtr ctxt, xmlChar **value) {
xmlChar *name, *val;
*value = NULL;
name = xmlParseName(ctxt);
if (name == NULL) {
ctxt->errNo = XML_ERR_NAME_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "error parsing attribute name\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(NULL);
}
/*
* read the value
*/
SKIP_BLANKS;
if (RAW == '=') {
NEXT;
SKIP_BLANKS;
val = xmlParseAttValue(ctxt);
ctxt->instate = XML_PARSER_CONTENT;
} else {
ctxt->errNo = XML_ERR_ATTRIBUTE_WITHOUT_VALUE;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Specification mandate value for attribute %s\n", name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
xmlFree(name);
return(NULL);
}
/*
* Check that xml:lang conforms to the specification
* No more registered as an error, just generate a warning now
* since this was deprecated in XML second edition
*/
if ((ctxt->pedantic) && (xmlStrEqual(name, BAD_CAST "xml:lang"))) {
if (!xmlCheckLanguageID(val)) {
if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
ctxt->sax->warning(ctxt->userData,
"Malformed value for xml:lang : %s\n", val);
}
}
/*
* Check that xml:space conforms to the specification
*/
if (xmlStrEqual(name, BAD_CAST "xml:space")) {
if (xmlStrEqual(val, BAD_CAST "default"))
*(ctxt->space) = 0;
else if (xmlStrEqual(val, BAD_CAST "preserve"))
*(ctxt->space) = 1;
else {
ctxt->errNo = XML_ERR_ATTRIBUTE_WITHOUT_VALUE;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Invalid value for xml:space : \"%s\", \"default\" or \"preserve\" expected\n",
val);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
}
*value = val;
return(name);
}
/**
* xmlParseStartTag:
* @ctxt: an XML parser context
*
* parse a start of tag either for rule element or
* EmptyElement. In both case we don't parse the tag closing chars.
*
* [40] STag ::= '<' Name (S Attribute)* S? '>'
*
* [ WFC: Unique Att Spec ]
* No attribute name may appear more than once in the same start-tag or
* empty-element tag.
*
* [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
*
* [ WFC: Unique Att Spec ]
* No attribute name may appear more than once in the same start-tag or
* empty-element tag.
*
* With namespace:
*
* [NS 8] STag ::= '<' QName (S Attribute)* S? '>'
*
* [NS 10] EmptyElement ::= '<' QName (S Attribute)* S? '/>'
*
* Returns the element name parsed
*/
xmlChar *
xmlParseStartTag(xmlParserCtxtPtr ctxt) {
xmlChar *name;
xmlChar *attname;
xmlChar *attvalue;
const xmlChar **atts = NULL;
int nbatts = 0;
int maxatts = 0;
int i;
if (RAW != '<') return(NULL);
NEXT1;
name = xmlParseName(ctxt);
if (name == NULL) {
ctxt->errNo = XML_ERR_NAME_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseStartTag: invalid element name\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(NULL);
}
/*
* Now parse the attributes, it ends up with the ending
*
* (S Attribute)* S?
*/
SKIP_BLANKS;
GROW;
while ((RAW != '>') &&
((RAW != '/') || (NXT(1) != '>')) &&
(IS_CHAR(RAW))) {
const xmlChar *q = CUR_PTR;
int cons = ctxt->input->consumed;
attname = xmlParseAttribute(ctxt, &attvalue);
if ((attname != NULL) && (attvalue != NULL)) {
/*
* [ WFC: Unique Att Spec ]
* No attribute name may appear more than once in the same
* start-tag or empty-element tag.
*/
for (i = 0; i < nbatts;i += 2) {
if (xmlStrEqual(atts[i], attname)) {
ctxt->errNo = XML_ERR_ATTRIBUTE_REDEFINED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Attribute %s redefined\n",
attname);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
xmlFree(attname);
xmlFree(attvalue);
goto failed;
}
}
/*
* Add the pair to atts
*/
if (atts == NULL) {
maxatts = 10;
atts = (const xmlChar **) xmlMalloc(maxatts * sizeof(xmlChar *));
if (atts == NULL) {
xmlGenericError(xmlGenericErrorContext,
"malloc of %ld byte failed\n",
maxatts * (long)sizeof(xmlChar *));
return(NULL);
}
} else if (nbatts + 4 > maxatts) {
maxatts *= 2;
atts = (const xmlChar **) xmlRealloc((void *) atts,
maxatts * sizeof(xmlChar *));
if (atts == NULL) {
xmlGenericError(xmlGenericErrorContext,
"realloc of %ld byte failed\n",
maxatts * (long)sizeof(xmlChar *));
return(NULL);
}
}
atts[nbatts++] = attname;
atts[nbatts++] = attvalue;
atts[nbatts] = NULL;
atts[nbatts + 1] = NULL;
} else {
if (attname != NULL)
xmlFree(attname);
if (attvalue != NULL)
xmlFree(attvalue);
}
failed:
if ((RAW == '>') || (((RAW == '/') && (NXT(1) == '>'))))
break;
if (!IS_BLANK(RAW)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"attributes construct error\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
if ((cons == ctxt->input->consumed) && (q == CUR_PTR)) {
ctxt->errNo = XML_ERR_INTERNAL_ERROR;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseStartTag: problem parsing attributes\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
break;
}
GROW;
}
/*
* SAX: Start of Element !
*/
if ((ctxt->sax != NULL) && (ctxt->sax->startElement != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->startElement(ctxt->userData, name, atts);
if (atts != NULL) {
for (i = 0;i < nbatts;i++) xmlFree((xmlChar *) atts[i]);
xmlFree((void *) atts);
}
return(name);
}
/**
* xmlParseEndTag:
* @ctxt: an XML parser context
*
* parse an end of tag
*
* [42] ETag ::= '</' Name S? '>'
*
* With namespace
*
* [NS 9] ETag ::= '</' QName S? '>'
*/
void
xmlParseEndTag(xmlParserCtxtPtr ctxt) {
xmlChar *name;
xmlChar *oldname;
GROW;
if ((RAW != '<') || (NXT(1) != '/')) {
ctxt->errNo = XML_ERR_LTSLASH_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "xmlParseEndTag: '</' not found\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return;
}
SKIP(2);
name = xmlParseName(ctxt);
/*
* We should definitely be at the ending "S? '>'" part
*/
GROW;
SKIP_BLANKS;
if ((!IS_CHAR(RAW)) || (RAW != '>')) {
ctxt->errNo = XML_ERR_GT_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "End tag : expected '>'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else
NEXT1;
/*
* [ WFC: Element Type Match ]
* The Name in an element's end-tag must match the element type in the
* start-tag.
*
*/
if ((name == NULL) || (ctxt->name == NULL) ||
(!xmlStrEqual(name, ctxt->name))) {
ctxt->errNo = XML_ERR_TAG_NAME_MISMATCH;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) {
if ((name != NULL) && (ctxt->name != NULL)) {
ctxt->sax->error(ctxt->userData,
"Opening and ending tag mismatch: %s and %s\n",
ctxt->name, name);
} else if (ctxt->name != NULL) {
ctxt->sax->error(ctxt->userData,
"Ending tag eror for: %s\n", ctxt->name);
} else {
ctxt->sax->error(ctxt->userData,
"Ending tag error: internal error ???\n");
}
}
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
/*
* SAX: End of Tag
*/
if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->endElement(ctxt->userData, name);
if (name != NULL)
xmlFree(name);
oldname = namePop(ctxt);
spacePop(ctxt);
if (oldname != NULL) {
#ifdef DEBUG_STACK
xmlGenericError(xmlGenericErrorContext,"Close: popped %s\n", oldname);
#endif
xmlFree(oldname);
}
return;
}
/**
* xmlParseCDSect:
* @ctxt: an XML parser context
*
* Parse escaped pure raw content.
*
* [18] CDSect ::= CDStart CData CDEnd
*
* [19] CDStart ::= '<![CDATA['
*
* [20] Data ::= (Char* - (Char* ']]>' Char*))
*
* [21] CDEnd ::= ']]>'
*/
void
xmlParseCDSect(xmlParserCtxtPtr ctxt) {
xmlChar *buf = NULL;
int len = 0;
int size = XML_PARSER_BUFFER_SIZE;
int r, rl;
int s, sl;
int cur, l;
int count = 0;
if ((NXT(0) == '<') && (NXT(1) == '!') &&
(NXT(2) == '[') && (NXT(3) == 'C') &&
(NXT(4) == 'D') && (NXT(5) == 'A') &&
(NXT(6) == 'T') && (NXT(7) == 'A') &&
(NXT(8) == '[')) {
SKIP(9);
} else
return;
ctxt->instate = XML_PARSER_CDATA_SECTION;
r = CUR_CHAR(rl);
if (!IS_CHAR(r)) {
ctxt->errNo = XML_ERR_CDATA_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"CData section not finished\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
ctxt->instate = XML_PARSER_CONTENT;
return;
}
NEXTL(rl);
s = CUR_CHAR(sl);
if (!IS_CHAR(s)) {
ctxt->errNo = XML_ERR_CDATA_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"CData section not finished\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
ctxt->instate = XML_PARSER_CONTENT;
return;
}
NEXTL(sl);
cur = CUR_CHAR(l);
buf = (xmlChar *) xmlMalloc(size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"malloc of %d byte failed\n", size);
return;
}
while (IS_CHAR(cur) &&
((r != ']') || (s != ']') || (cur != '>'))) {
if (len + 5 >= size) {
size *= 2;
buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"realloc of %d byte failed\n", size);
return;
}
}
COPY_BUF(rl,buf,len,r);
r = s;
rl = sl;
s = cur;
sl = l;
count++;
if (count > 50) {
GROW;
count = 0;
}
NEXTL(l);
cur = CUR_CHAR(l);
}
buf[len] = 0;
ctxt->instate = XML_PARSER_CONTENT;
if (cur != '>') {
ctxt->errNo = XML_ERR_CDATA_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"CData section not finished\n%.50s\n", buf);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
xmlFree(buf);
return;
}
NEXTL(l);
/*
* Ok the buffer is to be consumed as cdata.
*/
if ((ctxt->sax != NULL) && (!ctxt->disableSAX)) {
if (ctxt->sax->cdataBlock != NULL)
ctxt->sax->cdataBlock(ctxt->userData, buf, len);
else if (ctxt->sax->characters != NULL)
ctxt->sax->characters(ctxt->userData, buf, len);
}
xmlFree(buf);
}
/**
* xmlParseContent:
* @ctxt: an XML parser context
*
* Parse a content:
*
* [43] content ::= (element | CharData | Reference | CDSect | PI | Comment)*
*/
void
xmlParseContent(xmlParserCtxtPtr ctxt) {
GROW;
while (((RAW != 0) || (ctxt->token != 0)) &&
((RAW != '<') || (NXT(1) != '/'))) {
const xmlChar *test = CUR_PTR;
int cons = ctxt->input->consumed;
int tok = ctxt->token;
const xmlChar *cur = ctxt->input->cur;
/*
* Handle possible processed charrefs.
*/
if (ctxt->token != 0) {
xmlParseCharData(ctxt, 0);
}
/*
* First case : a Processing Instruction.
*/
else if ((*cur == '<') && (cur[1] == '?')) {
xmlParsePI(ctxt);
}
/*
* Second case : a CDSection
*/
else if ((*cur == '<') && (NXT(1) == '!') &&
(NXT(2) == '[') && (NXT(3) == 'C') &&
(NXT(4) == 'D') && (NXT(5) == 'A') &&
(NXT(6) == 'T') && (NXT(7) == 'A') &&
(NXT(8) == '[')) {
xmlParseCDSect(ctxt);
}
/*
* Third case : a comment
*/
else if ((*cur == '<') && (NXT(1) == '!') &&
(NXT(2) == '-') && (NXT(3) == '-')) {
xmlParseComment(ctxt);
ctxt->instate = XML_PARSER_CONTENT;
}
/*
* Fourth case : a sub-element.
*/
else if (*cur == '<') {
xmlParseElement(ctxt);
}
/*
* Fifth case : a reference. If if has not been resolved,
* parsing returns it's Name, create the node
*/
else if (*cur == '&') {
xmlParseReference(ctxt);
}
/*
* Last case, text. Note that References are handled directly.
*/
else {
xmlParseCharData(ctxt, 0);
}
GROW;
/*
* Pop-up of finished entities.
*/
while ((RAW == 0) && (ctxt->inputNr > 1))
xmlPopInput(ctxt);
SHRINK;
if ((cons == ctxt->input->consumed) && (test == CUR_PTR) &&
(tok == ctxt->token)) {
ctxt->errNo = XML_ERR_INTERNAL_ERROR;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"detected an error in element content\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
ctxt->instate = XML_PARSER_EOF;
break;
}
}
}
/**
* xmlParseElement:
* @ctxt: an XML parser context
*
* parse an XML element, this is highly recursive
*
* [39] element ::= EmptyElemTag | STag content ETag
*
* [ WFC: Element Type Match ]
* The Name in an element's end-tag must match the element type in the
* start-tag.
*
* [ VC: Element Valid ]
* An element is valid if there is a declaration matching elementdecl
* where the Name matches the element type and one of the following holds:
* - The declaration matches EMPTY and the element has no content.
* - The declaration matches children and the sequence of child elements
* belongs to the language generated by the regular expression in the
* content model, with optional white space (characters matching the
* nonterminal S) between each pair of child elements.
* - The declaration matches Mixed and the content consists of character
* data and child elements whose types match names in the content model.
* - The declaration matches ANY, and the types of any child elements have
* been declared.
*/
void
xmlParseElement(xmlParserCtxtPtr ctxt) {
const xmlChar *openTag = CUR_PTR;
xmlChar *name;
xmlChar *oldname;
xmlParserNodeInfo node_info;
xmlNodePtr ret;
/* Capture start position */
if (ctxt->record_info) {
node_info.begin_pos = ctxt->input->consumed +
(CUR_PTR - ctxt->input->base);
node_info.begin_line = ctxt->input->line;
}
if (ctxt->spaceNr == 0)
spacePush(ctxt, -1);
else
spacePush(ctxt, *ctxt->space);
name = xmlParseStartTag(ctxt);
if (name == NULL) {
spacePop(ctxt);
return;
}
namePush(ctxt, name);
ret = ctxt->node;
/*
* [ VC: Root Element Type ]
* The Name in the document type declaration must match the element
* type of the root element.
*/
if (ctxt->validate && ctxt->wellFormed && ctxt->myDoc &&
ctxt->node && (ctxt->node == ctxt->myDoc->children))
ctxt->valid &= xmlValidateRoot(&ctxt->vctxt, ctxt->myDoc);
/*
* Check for an Empty Element.
*/
if ((RAW == '/') && (NXT(1) == '>')) {
SKIP(2);
if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->endElement(ctxt->userData, name);
oldname = namePop(ctxt);
spacePop(ctxt);
if (oldname != NULL) {
#ifdef DEBUG_STACK
xmlGenericError(xmlGenericErrorContext,"Close: popped %s\n", oldname);
#endif
xmlFree(oldname);
}
if ( ret != NULL && ctxt->record_info ) {
node_info.end_pos = ctxt->input->consumed +
(CUR_PTR - ctxt->input->base);
node_info.end_line = ctxt->input->line;
node_info.node = ret;
xmlParserAddNodeInfo(ctxt, &node_info);
}
return;
}
if (RAW == '>') {
NEXT1;
} else {
ctxt->errNo = XML_ERR_GT_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Couldn't find end of Start Tag\n%.30s\n",
openTag);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
/*
* end of parsing of this node.
*/
nodePop(ctxt);
oldname = namePop(ctxt);
spacePop(ctxt);
if (oldname != NULL) {
#ifdef DEBUG_STACK
xmlGenericError(xmlGenericErrorContext,"Close: popped %s\n", oldname);
#endif
xmlFree(oldname);
}
/*
* Capture end position and add node
*/
if ( ret != NULL && ctxt->record_info ) {
node_info.end_pos = ctxt->input->consumed +
(CUR_PTR - ctxt->input->base);
node_info.end_line = ctxt->input->line;
node_info.node = ret;
xmlParserAddNodeInfo(ctxt, &node_info);
}
return;
}
/*
* Parse the content of the element:
*/
xmlParseContent(ctxt);
if (!IS_CHAR(RAW)) {
ctxt->errNo = XML_ERR_TAG_NOT_FINISED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Premature end of data in tag %.30s\n", openTag);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
/*
* end of parsing of this node.
*/
nodePop(ctxt);
oldname = namePop(ctxt);
spacePop(ctxt);
if (oldname != NULL) {
#ifdef DEBUG_STACK
xmlGenericError(xmlGenericErrorContext,"Close: popped %s\n", oldname);
#endif
xmlFree(oldname);
}
return;
}
/*
* parse the end of tag: '</' should be here.
*/
xmlParseEndTag(ctxt);
/*
* Capture end position and add node
*/
if ( ret != NULL && ctxt->record_info ) {
node_info.end_pos = ctxt->input->consumed +
(CUR_PTR - ctxt->input->base);
node_info.end_line = ctxt->input->line;
node_info.node = ret;
xmlParserAddNodeInfo(ctxt, &node_info);
}
}
/**
* xmlParseVersionNum:
* @ctxt: an XML parser context
*
* parse the XML version value.
*
* [26] VersionNum ::= ([a-zA-Z0-9_.:] | '-')+
*
* Returns the string giving the XML version number, or NULL
*/
xmlChar *
xmlParseVersionNum(xmlParserCtxtPtr ctxt) {
xmlChar *buf = NULL;
int len = 0;
int size = 10;
xmlChar cur;
buf = (xmlChar *) xmlMalloc(size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"malloc of %d byte failed\n", size);
return(NULL);
}
cur = CUR;
while (((cur >= 'a') && (cur <= 'z')) ||
((cur >= 'A') && (cur <= 'Z')) ||
((cur >= '0') && (cur <= '9')) ||
(cur == '_') || (cur == '.') ||
(cur == ':') || (cur == '-')) {
if (len + 1 >= size) {
size *= 2;
buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"realloc of %d byte failed\n", size);
return(NULL);
}
}
buf[len++] = cur;
NEXT;
cur=CUR;
}
buf[len] = 0;
return(buf);
}
/**
* xmlParseVersionInfo:
* @ctxt: an XML parser context
*
* parse the XML version.
*
* [24] VersionInfo ::= S 'version' Eq (' VersionNum ' | " VersionNum ")
*
* [25] Eq ::= S? '=' S?
*
* Returns the version string, e.g. "1.0"
*/
xmlChar *
xmlParseVersionInfo(xmlParserCtxtPtr ctxt) {
xmlChar *version = NULL;
const xmlChar *q;
if ((RAW == 'v') && (NXT(1) == 'e') &&
(NXT(2) == 'r') && (NXT(3) == 's') &&
(NXT(4) == 'i') && (NXT(5) == 'o') &&
(NXT(6) == 'n')) {
SKIP(7);
SKIP_BLANKS;
if (RAW != '=') {
ctxt->errNo = XML_ERR_EQUAL_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseVersionInfo : expected '='\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(NULL);
}
NEXT;
SKIP_BLANKS;
if (RAW == '"') {
NEXT;
q = CUR_PTR;
version = xmlParseVersionNum(ctxt);
if (RAW != '"') {
ctxt->errNo = XML_ERR_STRING_NOT_CLOSED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"String not closed\n%.50s\n", q);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else
NEXT;
} else if (RAW == '\''){
NEXT;
q = CUR_PTR;
version = xmlParseVersionNum(ctxt);
if (RAW != '\'') {
ctxt->errNo = XML_ERR_STRING_NOT_CLOSED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"String not closed\n%.50s\n", q);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else
NEXT;
} else {
ctxt->errNo = XML_ERR_STRING_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseVersionInfo : expected ' or \"\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
}
return(version);
}
/**
* xmlParseEncName:
* @ctxt: an XML parser context
*
* parse the XML encoding name
*
* [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
*
* Returns the encoding name value or NULL
*/
xmlChar *
xmlParseEncName(xmlParserCtxtPtr ctxt) {
xmlChar *buf = NULL;
int len = 0;
int size = 10;
xmlChar cur;
cur = CUR;
if (((cur >= 'a') && (cur <= 'z')) ||
((cur >= 'A') && (cur <= 'Z'))) {
buf = (xmlChar *) xmlMalloc(size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"malloc of %d byte failed\n", size);
return(NULL);
}
buf[len++] = cur;
NEXT;
cur = CUR;
while (((cur >= 'a') && (cur <= 'z')) ||
((cur >= 'A') && (cur <= 'Z')) ||
((cur >= '0') && (cur <= '9')) ||
(cur == '.') || (cur == '_') ||
(cur == '-')) {
if (len + 1 >= size) {
size *= 2;
buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"realloc of %d byte failed\n", size);
return(NULL);
}
}
buf[len++] = cur;
NEXT;
cur = CUR;
if (cur == 0) {
SHRINK;
GROW;
cur = CUR;
}
}
buf[len] = 0;
} else {
ctxt->errNo = XML_ERR_ENCODING_NAME;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "Invalid XML encoding name\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
return(buf);
}
/**
* xmlParseEncodingDecl:
* @ctxt: an XML parser context
*
* parse the XML encoding declaration
*
* [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'")
*
* this setups the conversion filters.
*
* Returns the encoding value or NULL
*/
xmlChar *
xmlParseEncodingDecl(xmlParserCtxtPtr ctxt) {
xmlChar *encoding = NULL;
const xmlChar *q;
SKIP_BLANKS;
if ((RAW == 'e') && (NXT(1) == 'n') &&
(NXT(2) == 'c') && (NXT(3) == 'o') &&
(NXT(4) == 'd') && (NXT(5) == 'i') &&
(NXT(6) == 'n') && (NXT(7) == 'g')) {
SKIP(8);
SKIP_BLANKS;
if (RAW != '=') {
ctxt->errNo = XML_ERR_EQUAL_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseEncodingDecl : expected '='\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(NULL);
}
NEXT;
SKIP_BLANKS;
if (RAW == '"') {
NEXT;
q = CUR_PTR;
encoding = xmlParseEncName(ctxt);
if (RAW != '"') {
ctxt->errNo = XML_ERR_STRING_NOT_CLOSED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"String not closed\n%.50s\n", q);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else
NEXT;
} else if (RAW == '\''){
NEXT;
q = CUR_PTR;
encoding = xmlParseEncName(ctxt);
if (RAW != '\'') {
ctxt->errNo = XML_ERR_STRING_NOT_CLOSED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"String not closed\n%.50s\n", q);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else
NEXT;
} else if (RAW == '"'){
ctxt->errNo = XML_ERR_STRING_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"xmlParseEncodingDecl : expected ' or \"\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (encoding != NULL) {
xmlCharEncoding enc;
xmlCharEncodingHandlerPtr handler;
if (ctxt->input->encoding != NULL)
xmlFree((xmlChar *) ctxt->input->encoding);
ctxt->input->encoding = encoding;
enc = xmlParseCharEncoding((const char *) encoding);
/*
* registered set of known encodings
*/
if (enc != XML_CHAR_ENCODING_ERROR) {
xmlSwitchEncoding(ctxt, enc);
if (ctxt->errNo == XML_ERR_UNSUPPORTED_ENCODING) {
xmlFree(encoding);
return(NULL);
}
} else {
/*
* fallback for unknown encodings
*/
handler = xmlFindCharEncodingHandler((const char *) encoding);
if (handler != NULL) {
xmlSwitchToEncoding(ctxt, handler);
} else {
ctxt->errNo = XML_ERR_UNSUPPORTED_ENCODING;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Unsupported encoding %s\n", encoding);
return(NULL);
}
}
}
}
return(encoding);
}
/**
* xmlParseSDDecl:
* @ctxt: an XML parser context
*
* parse the XML standalone declaration
*
* [32] SDDecl ::= S 'standalone' Eq
* (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no')'"'))
*
* [ VC: Standalone Document Declaration ]
* TODO The standalone document declaration must have the value "no"
* if any external markup declarations contain declarations of:
* - attributes with default values, if elements to which these
* attributes apply appear in the document without specifications
* of values for these attributes, or
* - entities (other than amp, lt, gt, apos, quot), if references
* to those entities appear in the document, or
* - attributes with values subject to normalization, where the
* attribute appears in the document with a value which will change
* as a result of normalization, or
* - element types with element content, if white space occurs directly
* within any instance of those types.
*
* Returns 1 if standalone, 0 otherwise
*/
int
xmlParseSDDecl(xmlParserCtxtPtr ctxt) {
int standalone = -1;
SKIP_BLANKS;
if ((RAW == 's') && (NXT(1) == 't') &&
(NXT(2) == 'a') && (NXT(3) == 'n') &&
(NXT(4) == 'd') && (NXT(5) == 'a') &&
(NXT(6) == 'l') && (NXT(7) == 'o') &&
(NXT(8) == 'n') && (NXT(9) == 'e')) {
SKIP(10);
SKIP_BLANKS;
if (RAW != '=') {
ctxt->errNo = XML_ERR_EQUAL_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"XML standalone declaration : expected '='\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
return(standalone);
}
NEXT;
SKIP_BLANKS;
if (RAW == '\''){
NEXT;
if ((RAW == 'n') && (NXT(1) == 'o')) {
standalone = 0;
SKIP(2);
} else if ((RAW == 'y') && (NXT(1) == 'e') &&
(NXT(2) == 's')) {
standalone = 1;
SKIP(3);
} else {
ctxt->errNo = XML_ERR_STANDALONE_VALUE;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"standalone accepts only 'yes' or 'no'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (RAW != '\'') {
ctxt->errNo = XML_ERR_STRING_NOT_CLOSED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "String not closed\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else
NEXT;
} else if (RAW == '"'){
NEXT;
if ((RAW == 'n') && (NXT(1) == 'o')) {
standalone = 0;
SKIP(2);
} else if ((RAW == 'y') && (NXT(1) == 'e') &&
(NXT(2) == 's')) {
standalone = 1;
SKIP(3);
} else {
ctxt->errNo = XML_ERR_STANDALONE_VALUE;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"standalone accepts only 'yes' or 'no'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (RAW != '"') {
ctxt->errNo = XML_ERR_STRING_NOT_CLOSED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "String not closed\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else
NEXT;
} else {
ctxt->errNo = XML_ERR_STRING_NOT_STARTED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Standalone value not found\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
}
return(standalone);
}
/**
* xmlParseXMLDecl:
* @ctxt: an XML parser context
*
* parse an XML declaration header
*
* [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
*/
void
xmlParseXMLDecl(xmlParserCtxtPtr ctxt) {
xmlChar *version;
/*
* We know that '<?xml' is here.
*/
SKIP(5);
if (!IS_BLANK(RAW)) {
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "Blank needed after '<?xml'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
/*
* We should have the VersionInfo here.
*/
version = xmlParseVersionInfo(ctxt);
if (version == NULL)
version = xmlCharStrdup(XML_DEFAULT_VERSION);
ctxt->version = xmlStrdup(version);
xmlFree(version);
/*
* We may have the encoding declaration
*/
if (!IS_BLANK(RAW)) {
if ((RAW == '?') && (NXT(1) == '>')) {
SKIP(2);
return;
}
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "Blank needed here\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
xmlParseEncodingDecl(ctxt);
if (ctxt->errNo == XML_ERR_UNSUPPORTED_ENCODING) {
/*
* The XML REC instructs us to stop parsing right here
*/
return;
}
/*
* We may have the standalone status.
*/
if ((ctxt->input->encoding != NULL) && (!IS_BLANK(RAW))) {
if ((RAW == '?') && (NXT(1) == '>')) {
SKIP(2);
return;
}
ctxt->errNo = XML_ERR_SPACE_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "Blank needed here\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
SKIP_BLANKS;
ctxt->input->standalone = xmlParseSDDecl(ctxt);
SKIP_BLANKS;
if ((RAW == '?') && (NXT(1) == '>')) {
SKIP(2);
} else if (RAW == '>') {
/* Deprecated old WD ... */
ctxt->errNo = XML_ERR_XMLDECL_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"XML declaration must end-up with '?>'\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
NEXT;
} else {
ctxt->errNo = XML_ERR_XMLDECL_NOT_FINISHED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"parsing XML declaration: '?>' expected\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
MOVETO_ENDTAG(CUR_PTR);
NEXT;
}
}
/**
* xmlParseMisc:
* @ctxt: an XML parser context
*
* parse an XML Misc* optionnal field.
*
* [27] Misc ::= Comment | PI | S
*/
void
xmlParseMisc(xmlParserCtxtPtr ctxt) {
while (((RAW == '<') && (NXT(1) == '?')) ||
((RAW == '<') && (NXT(1) == '!') &&
(NXT(2) == '-') && (NXT(3) == '-')) ||
IS_BLANK(CUR)) {
if ((RAW == '<') && (NXT(1) == '?')) {
xmlParsePI(ctxt);
} else if (IS_BLANK(CUR)) {
NEXT;
} else
xmlParseComment(ctxt);
}
}
/**
* xmlParseDocument:
* @ctxt: an XML parser context
*
* parse an XML document (and build a tree if using the standard SAX
* interface).
*
* [1] document ::= prolog element Misc*
*
* [22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)?
*
* Returns 0, -1 in case of error. the parser context is augmented
* as a result of the parsing.
*/
int
xmlParseDocument(xmlParserCtxtPtr ctxt) {
xmlChar start[4];
xmlCharEncoding enc;
xmlInitParser();
GROW;
/*
* SAX: beginning of the document processing.
*/
if ((ctxt->sax) && (ctxt->sax->setDocumentLocator))
ctxt->sax->setDocumentLocator(ctxt->userData, &xmlDefaultSAXLocator);
if (ctxt->encoding == (const xmlChar *)XML_CHAR_ENCODING_NONE) {
/*
* Get the 4 first bytes and decode the charset
* if enc != XML_CHAR_ENCODING_NONE
* plug some encoding conversion routines.
*/
start[0] = RAW;
start[1] = NXT(1);
start[2] = NXT(2);
start[3] = NXT(3);
enc = xmlDetectCharEncoding(start, 4);
if (enc != XML_CHAR_ENCODING_NONE) {
xmlSwitchEncoding(ctxt, enc);
}
}
if (CUR == 0) {
ctxt->errNo = XML_ERR_DOCUMENT_EMPTY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "Document is empty\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
/*
* Check for the XMLDecl in the Prolog.
*/
GROW;
if ((RAW == '<') && (NXT(1) == '?') &&
(NXT(2) == 'x') && (NXT(3) == 'm') &&
(NXT(4) == 'l') && (IS_BLANK(NXT(5)))) {
/*
* Note that we will switch encoding on the fly.
*/
xmlParseXMLDecl(ctxt);
if (ctxt->errNo == XML_ERR_UNSUPPORTED_ENCODING) {
/*
* The XML REC instructs us to stop parsing right here
*/
return(-1);
}
ctxt->standalone = ctxt->input->standalone;
SKIP_BLANKS;
} else {
ctxt->version = xmlCharStrdup(XML_DEFAULT_VERSION);
}
if ((ctxt->sax) && (ctxt->sax->startDocument) && (!ctxt->disableSAX))
ctxt->sax->startDocument(ctxt->userData);
/*
* The Misc part of the Prolog
*/
GROW;
xmlParseMisc(ctxt);
/*
* Then possibly doc type declaration(s) and more Misc
* (doctypedecl Misc*)?
*/
GROW;
if ((RAW == '<') && (NXT(1) == '!') &&
(NXT(2) == 'D') && (NXT(3) == 'O') &&
(NXT(4) == 'C') && (NXT(5) == 'T') &&
(NXT(6) == 'Y') && (NXT(7) == 'P') &&
(NXT(8) == 'E')) {
ctxt->inSubset = 1;
xmlParseDocTypeDecl(ctxt);
if (RAW == '[') {
ctxt->instate = XML_PARSER_DTD;
xmlParseInternalSubset(ctxt);
}
/*
* Create and update the external subset.
*/
ctxt->inSubset = 2;
if ((ctxt->sax != NULL) && (ctxt->sax->externalSubset != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->externalSubset(ctxt->userData, ctxt->intSubName,
ctxt->extSubSystem, ctxt->extSubURI);
ctxt->inSubset = 0;
ctxt->instate = XML_PARSER_PROLOG;
xmlParseMisc(ctxt);
}
/*
* Time to start parsing the tree itself
*/
GROW;
if (RAW != '<') {
ctxt->errNo = XML_ERR_DOCUMENT_EMPTY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Start tag expected, '<' not found\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
ctxt->instate = XML_PARSER_EOF;
} else {
ctxt->instate = XML_PARSER_CONTENT;
xmlParseElement(ctxt);
ctxt->instate = XML_PARSER_EPILOG;
/*
* The Misc part at the end
*/
xmlParseMisc(ctxt);
if (RAW != 0) {
ctxt->errNo = XML_ERR_DOCUMENT_END;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Extra content at the end of the document\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
ctxt->instate = XML_PARSER_EOF;
}
/*
* SAX: end of the document processing.
*/
if ((ctxt->sax) && (ctxt->sax->endDocument != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->endDocument(ctxt->userData);
if (! ctxt->wellFormed) return(-1);
return(0);
}
/**
* xmlParseExtParsedEnt:
* @ctxt: an XML parser context
*
* parse a genreral parsed entity
* An external general parsed entity is well-formed if it matches the
* production labeled extParsedEnt.
*
* [78] extParsedEnt ::= TextDecl? content
*
* Returns 0, -1 in case of error. the parser context is augmented
* as a result of the parsing.
*/
int
xmlParseExtParsedEnt(xmlParserCtxtPtr ctxt) {
xmlChar start[4];
xmlCharEncoding enc;
xmlDefaultSAXHandlerInit();
GROW;
/*
* SAX: beginning of the document processing.
*/
if ((ctxt->sax) && (ctxt->sax->setDocumentLocator))
ctxt->sax->setDocumentLocator(ctxt->userData, &xmlDefaultSAXLocator);
/*
* Get the 4 first bytes and decode the charset
* if enc != XML_CHAR_ENCODING_NONE
* plug some encoding conversion routines.
*/
start[0] = RAW;
start[1] = NXT(1);
start[2] = NXT(2);
start[3] = NXT(3);
enc = xmlDetectCharEncoding(start, 4);
if (enc != XML_CHAR_ENCODING_NONE) {
xmlSwitchEncoding(ctxt, enc);
}
if (CUR == 0) {
ctxt->errNo = XML_ERR_DOCUMENT_EMPTY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "Document is empty\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
/*
* Check for the XMLDecl in the Prolog.
*/
GROW;
if ((RAW == '<') && (NXT(1) == '?') &&
(NXT(2) == 'x') && (NXT(3) == 'm') &&
(NXT(4) == 'l') && (IS_BLANK(NXT(5)))) {
/*
* Note that we will switch encoding on the fly.
*/
xmlParseXMLDecl(ctxt);
if (ctxt->errNo == XML_ERR_UNSUPPORTED_ENCODING) {
/*
* The XML REC instructs us to stop parsing right here
*/
return(-1);
}
SKIP_BLANKS;
} else {
ctxt->version = xmlCharStrdup(XML_DEFAULT_VERSION);
}
if ((ctxt->sax) && (ctxt->sax->startDocument) && (!ctxt->disableSAX))
ctxt->sax->startDocument(ctxt->userData);
/*
* Doing validity checking on chunk doesn't make sense
*/
ctxt->instate = XML_PARSER_CONTENT;
ctxt->validate = 0;
ctxt->loadsubset = 0;
ctxt->depth = 0;
xmlParseContent(ctxt);
if ((RAW == '<') && (NXT(1) == '/')) {
ctxt->errNo = XML_ERR_NOT_WELL_BALANCED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"chunk is not well balanced\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else if (RAW != 0) {
ctxt->errNo = XML_ERR_EXTRA_CONTENT;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"extra content at the end of well balanced chunk\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
/*
* SAX: end of the document processing.
*/
if ((ctxt->sax) && (ctxt->sax->endDocument != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->endDocument(ctxt->userData);
if (! ctxt->wellFormed) return(-1);
return(0);
}
/************************************************************************
* *
* Progressive parsing interfaces *
* *
************************************************************************/
/**
* xmlParseLookupSequence:
* @ctxt: an XML parser context
* @first: the first char to lookup
* @next: the next char to lookup or zero
* @third: the next char to lookup or zero
*
* Try to find if a sequence (first, next, third) or just (first next) or
* (first) is available in the input stream.
* This function has a side effect of (possibly) incrementing ctxt->checkIndex
* to avoid rescanning sequences of bytes, it DOES change the state of the
* parser, do not use liberally.
*
* Returns the index to the current parsing point if the full sequence
* is available, -1 otherwise.
*/
static int
xmlParseLookupSequence(xmlParserCtxtPtr ctxt, xmlChar first,
xmlChar next, xmlChar third) {
int base, len;
xmlParserInputPtr in;
const xmlChar *buf;
in = ctxt->input;
if (in == NULL) return(-1);
base = in->cur - in->base;
if (base < 0) return(-1);
if (ctxt->checkIndex > base)
base = ctxt->checkIndex;
if (in->buf == NULL) {
buf = in->base;
len = in->length;
} else {
buf = in->buf->buffer->content;
len = in->buf->buffer->use;
}
/* take into account the sequence length */
if (third) len -= 2;
else if (next) len --;
for (;base < len;base++) {
if (buf[base] == first) {
if (third != 0) {
if ((buf[base + 1] != next) ||
(buf[base + 2] != third)) continue;
} else if (next != 0) {
if (buf[base + 1] != next) continue;
}
ctxt->checkIndex = 0;
#ifdef DEBUG_PUSH
if (next == 0)
xmlGenericError(xmlGenericErrorContext,
"PP: lookup '%c' found at %d\n",
first, base);
else if (third == 0)
xmlGenericError(xmlGenericErrorContext,
"PP: lookup '%c%c' found at %d\n",
first, next, base);
else
xmlGenericError(xmlGenericErrorContext,
"PP: lookup '%c%c%c' found at %d\n",
first, next, third, base);
#endif
return(base - (in->cur - in->base));
}
}
ctxt->checkIndex = base;
#ifdef DEBUG_PUSH
if (next == 0)
xmlGenericError(xmlGenericErrorContext,
"PP: lookup '%c' failed\n", first);
else if (third == 0)
xmlGenericError(xmlGenericErrorContext,
"PP: lookup '%c%c' failed\n", first, next);
else
xmlGenericError(xmlGenericErrorContext,
"PP: lookup '%c%c%c' failed\n", first, next, third);
#endif
return(-1);
}
/**
* xmlParseTryOrFinish:
* @ctxt: an XML parser context
* @terminate: last chunk indicator
*
* Try to progress on parsing
*
* Returns zero if no parsing was possible
*/
static int
xmlParseTryOrFinish(xmlParserCtxtPtr ctxt, int terminate) {
int ret = 0;
int avail;
xmlChar cur, next;
#ifdef DEBUG_PUSH
switch (ctxt->instate) {
case XML_PARSER_EOF:
xmlGenericError(xmlGenericErrorContext,
"PP: try EOF\n"); break;
case XML_PARSER_START:
xmlGenericError(xmlGenericErrorContext,
"PP: try START\n"); break;
case XML_PARSER_MISC:
xmlGenericError(xmlGenericErrorContext,
"PP: try MISC\n");break;
case XML_PARSER_COMMENT:
xmlGenericError(xmlGenericErrorContext,
"PP: try COMMENT\n");break;
case XML_PARSER_PROLOG:
xmlGenericError(xmlGenericErrorContext,
"PP: try PROLOG\n");break;
case XML_PARSER_START_TAG:
xmlGenericError(xmlGenericErrorContext,
"PP: try START_TAG\n");break;
case XML_PARSER_CONTENT:
xmlGenericError(xmlGenericErrorContext,
"PP: try CONTENT\n");break;
case XML_PARSER_CDATA_SECTION:
xmlGenericError(xmlGenericErrorContext,
"PP: try CDATA_SECTION\n");break;
case XML_PARSER_END_TAG:
xmlGenericError(xmlGenericErrorContext,
"PP: try END_TAG\n");break;
case XML_PARSER_ENTITY_DECL:
xmlGenericError(xmlGenericErrorContext,
"PP: try ENTITY_DECL\n");break;
case XML_PARSER_ENTITY_VALUE:
xmlGenericError(xmlGenericErrorContext,
"PP: try ENTITY_VALUE\n");break;
case XML_PARSER_ATTRIBUTE_VALUE:
xmlGenericError(xmlGenericErrorContext,
"PP: try ATTRIBUTE_VALUE\n");break;
case XML_PARSER_DTD:
xmlGenericError(xmlGenericErrorContext,
"PP: try DTD\n");break;
case XML_PARSER_EPILOG:
xmlGenericError(xmlGenericErrorContext,
"PP: try EPILOG\n");break;
case XML_PARSER_PI:
xmlGenericError(xmlGenericErrorContext,
"PP: try PI\n");break;
case XML_PARSER_IGNORE:
xmlGenericError(xmlGenericErrorContext,
"PP: try IGNORE\n");break;
}
#endif
while (1) {
/*
* Pop-up of finished entities.
*/
while ((RAW == 0) && (ctxt->inputNr > 1))
xmlPopInput(ctxt);
if (ctxt->input ==NULL) break;
if (ctxt->input->buf == NULL)
avail = ctxt->input->length - (ctxt->input->cur - ctxt->input->base);
else
avail = ctxt->input->buf->buffer->use - (ctxt->input->cur - ctxt->input->base);
if (avail < 1)
goto done;
switch (ctxt->instate) {
case XML_PARSER_EOF:
/*
* Document parsing is done !
*/
goto done;
case XML_PARSER_START:
if (ctxt->charset == XML_CHAR_ENCODING_NONE) {
xmlChar start[4];
xmlCharEncoding enc;
/*
* Very first chars read from the document flow.
*/
if (avail < 4)
goto done;
/*
* Get the 4 first bytes and decode the charset
* if enc != XML_CHAR_ENCODING_NONE
* plug some encoding conversion routines.
*/
start[0] = RAW;
start[1] = NXT(1);
start[2] = NXT(2);
start[3] = NXT(3);
enc = xmlDetectCharEncoding(start, 4);
if (enc != XML_CHAR_ENCODING_NONE) {
xmlSwitchEncoding(ctxt, enc);
}
break;
}
cur = ctxt->input->cur[0];
next = ctxt->input->cur[1];
if (cur == 0) {
if ((ctxt->sax) && (ctxt->sax->setDocumentLocator))
ctxt->sax->setDocumentLocator(ctxt->userData,
&xmlDefaultSAXLocator);
ctxt->errNo = XML_ERR_DOCUMENT_EMPTY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData, "Document is empty\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
ctxt->instate = XML_PARSER_EOF;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering EOF\n");
#endif
if ((ctxt->sax) && (ctxt->sax->endDocument != NULL))
ctxt->sax->endDocument(ctxt->userData);
goto done;
}
if ((cur == '<') && (next == '?')) {
/* PI or XML decl */
if (avail < 5) return(ret);
if ((!terminate) &&
(xmlParseLookupSequence(ctxt, '?', '>', 0) < 0))
return(ret);
if ((ctxt->sax) && (ctxt->sax->setDocumentLocator))
ctxt->sax->setDocumentLocator(ctxt->userData,
&xmlDefaultSAXLocator);
if ((ctxt->input->cur[2] == 'x') &&
(ctxt->input->cur[3] == 'm') &&
(ctxt->input->cur[4] == 'l') &&
(IS_BLANK(ctxt->input->cur[5]))) {
ret += 5;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: Parsing XML Decl\n");
#endif
xmlParseXMLDecl(ctxt);
if (ctxt->errNo == XML_ERR_UNSUPPORTED_ENCODING) {
/*
* The XML REC instructs us to stop parsing right
* here
*/
ctxt->instate = XML_PARSER_EOF;
return(0);
}
ctxt->standalone = ctxt->input->standalone;
if ((ctxt->encoding == NULL) &&
(ctxt->input->encoding != NULL))
ctxt->encoding = xmlStrdup(ctxt->input->encoding);
if ((ctxt->sax) && (ctxt->sax->startDocument) &&
(!ctxt->disableSAX))
ctxt->sax->startDocument(ctxt->userData);
ctxt->instate = XML_PARSER_MISC;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering MISC\n");
#endif
} else {
ctxt->version = xmlCharStrdup(XML_DEFAULT_VERSION);
if ((ctxt->sax) && (ctxt->sax->startDocument) &&
(!ctxt->disableSAX))
ctxt->sax->startDocument(ctxt->userData);
ctxt->instate = XML_PARSER_MISC;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering MISC\n");
#endif
}
} else {
if ((ctxt->sax) && (ctxt->sax->setDocumentLocator))
ctxt->sax->setDocumentLocator(ctxt->userData,
&xmlDefaultSAXLocator);
ctxt->version = xmlCharStrdup(XML_DEFAULT_VERSION);
if ((ctxt->sax) && (ctxt->sax->startDocument) &&
(!ctxt->disableSAX))
ctxt->sax->startDocument(ctxt->userData);
ctxt->instate = XML_PARSER_MISC;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering MISC\n");
#endif
}
break;
case XML_PARSER_MISC:
SKIP_BLANKS;
if (ctxt->input->buf == NULL)
avail = ctxt->input->length - (ctxt->input->cur - ctxt->input->base);
else
avail = ctxt->input->buf->buffer->use - (ctxt->input->cur - ctxt->input->base);
if (avail < 2)
goto done;
cur = ctxt->input->cur[0];
next = ctxt->input->cur[1];
if ((cur == '<') && (next == '?')) {
if ((!terminate) &&
(xmlParseLookupSequence(ctxt, '?', '>', 0) < 0))
goto done;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: Parsing PI\n");
#endif
xmlParsePI(ctxt);
} else if ((cur == '<') && (next == '!') &&
(ctxt->input->cur[2] == '-') && (ctxt->input->cur[3] == '-')) {
if ((!terminate) &&
(xmlParseLookupSequence(ctxt, '-', '-', '>') < 0))
goto done;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: Parsing Comment\n");
#endif
xmlParseComment(ctxt);
ctxt->instate = XML_PARSER_MISC;
} else if ((cur == '<') && (next == '!') &&
(ctxt->input->cur[2] == 'D') && (ctxt->input->cur[3] == 'O') &&
(ctxt->input->cur[4] == 'C') && (ctxt->input->cur[5] == 'T') &&
(ctxt->input->cur[6] == 'Y') && (ctxt->input->cur[7] == 'P') &&
(ctxt->input->cur[8] == 'E')) {
if ((!terminate) &&
(xmlParseLookupSequence(ctxt, '>', 0, 0) < 0))
goto done;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: Parsing internal subset\n");
#endif
ctxt->inSubset = 1;
xmlParseDocTypeDecl(ctxt);
if (RAW == '[') {
ctxt->instate = XML_PARSER_DTD;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering DTD\n");
#endif
} else {
/*
* Create and update the external subset.
*/
ctxt->inSubset = 2;
if ((ctxt->sax != NULL) && (!ctxt->disableSAX) &&
(ctxt->sax->externalSubset != NULL))
ctxt->sax->externalSubset(ctxt->userData,
ctxt->intSubName, ctxt->extSubSystem,
ctxt->extSubURI);
ctxt->inSubset = 0;
ctxt->instate = XML_PARSER_PROLOG;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering PROLOG\n");
#endif
}
} else if ((cur == '<') && (next == '!') &&
(avail < 9)) {
goto done;
} else {
ctxt->instate = XML_PARSER_START_TAG;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering START_TAG\n");
#endif
}
break;
case XML_PARSER_IGNORE:
xmlGenericError(xmlGenericErrorContext,
"PP: internal error, state == IGNORE");
ctxt->instate = XML_PARSER_DTD;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering DTD\n");
#endif
break;
case XML_PARSER_PROLOG:
SKIP_BLANKS;
if (ctxt->input->buf == NULL)
avail = ctxt->input->length - (ctxt->input->cur - ctxt->input->base);
else
avail = ctxt->input->buf->buffer->use - (ctxt->input->cur - ctxt->input->base);
if (avail < 2)
goto done;
cur = ctxt->input->cur[0];
next = ctxt->input->cur[1];
if ((cur == '<') && (next == '?')) {
if ((!terminate) &&
(xmlParseLookupSequence(ctxt, '?', '>', 0) < 0))
goto done;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: Parsing PI\n");
#endif
xmlParsePI(ctxt);
} else if ((cur == '<') && (next == '!') &&
(ctxt->input->cur[2] == '-') && (ctxt->input->cur[3] == '-')) {
if ((!terminate) &&
(xmlParseLookupSequence(ctxt, '-', '-', '>') < 0))
goto done;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: Parsing Comment\n");
#endif
xmlParseComment(ctxt);
ctxt->instate = XML_PARSER_PROLOG;
} else if ((cur == '<') && (next == '!') &&
(avail < 4)) {
goto done;
} else {
ctxt->instate = XML_PARSER_START_TAG;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering START_TAG\n");
#endif
}
break;
case XML_PARSER_EPILOG:
SKIP_BLANKS;
if (ctxt->input->buf == NULL)
avail = ctxt->input->length - (ctxt->input->cur - ctxt->input->base);
else
avail = ctxt->input->buf->buffer->use - (ctxt->input->cur - ctxt->input->base);
if (avail < 2)
goto done;
cur = ctxt->input->cur[0];
next = ctxt->input->cur[1];
if ((cur == '<') && (next == '?')) {
if ((!terminate) &&
(xmlParseLookupSequence(ctxt, '?', '>', 0) < 0))
goto done;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: Parsing PI\n");
#endif
xmlParsePI(ctxt);
ctxt->instate = XML_PARSER_EPILOG;
} else if ((cur == '<') && (next == '!') &&
(ctxt->input->cur[2] == '-') && (ctxt->input->cur[3] == '-')) {
if ((!terminate) &&
(xmlParseLookupSequence(ctxt, '-', '-', '>') < 0))
goto done;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: Parsing Comment\n");
#endif
xmlParseComment(ctxt);
ctxt->instate = XML_PARSER_EPILOG;
} else if ((cur == '<') && (next == '!') &&
(avail < 4)) {
goto done;
} else {
ctxt->errNo = XML_ERR_DOCUMENT_END;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Extra content at the end of the document\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
ctxt->instate = XML_PARSER_EOF;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering EOF\n");
#endif
if ((ctxt->sax) && (ctxt->sax->endDocument != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->endDocument(ctxt->userData);
goto done;
}
break;
case XML_PARSER_START_TAG: {
xmlChar *name, *oldname;
if ((avail < 2) && (ctxt->inputNr == 1))
goto done;
cur = ctxt->input->cur[0];
if (cur != '<') {
ctxt->errNo = XML_ERR_DOCUMENT_EMPTY;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Start tag expect, '<' not found\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
ctxt->instate = XML_PARSER_EOF;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering EOF\n");
#endif
if ((ctxt->sax) && (ctxt->sax->endDocument != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->endDocument(ctxt->userData);
goto done;
}
if ((!terminate) &&
(xmlParseLookupSequence(ctxt, '>', 0, 0) < 0))
goto done;
if (ctxt->spaceNr == 0)
spacePush(ctxt, -1);
else
spacePush(ctxt, *ctxt->space);
name = xmlParseStartTag(ctxt);
if (name == NULL) {
spacePop(ctxt);
ctxt->instate = XML_PARSER_EOF;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering EOF\n");
#endif
if ((ctxt->sax) && (ctxt->sax->endDocument != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->endDocument(ctxt->userData);
goto done;
}
namePush(ctxt, xmlStrdup(name));
/*
* [ VC: Root Element Type ]
* The Name in the document type declaration must match
* the element type of the root element.
*/
if (ctxt->validate && ctxt->wellFormed && ctxt->myDoc &&
ctxt->node && (ctxt->node == ctxt->myDoc->children))
ctxt->valid &= xmlValidateRoot(&ctxt->vctxt, ctxt->myDoc);
/*
* Check for an Empty Element.
*/
if ((RAW == '/') && (NXT(1) == '>')) {
SKIP(2);
if ((ctxt->sax != NULL) &&
(ctxt->sax->endElement != NULL) && (!ctxt->disableSAX))
ctxt->sax->endElement(ctxt->userData, name);
xmlFree(name);
oldname = namePop(ctxt);
spacePop(ctxt);
if (oldname != NULL) {
#ifdef DEBUG_STACK
xmlGenericError(xmlGenericErrorContext,"Close: popped %s\n", oldname);
#endif
xmlFree(oldname);
}
if (ctxt->name == NULL) {
ctxt->instate = XML_PARSER_EPILOG;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering EPILOG\n");
#endif
} else {
ctxt->instate = XML_PARSER_CONTENT;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering CONTENT\n");
#endif
}
break;
}
if (RAW == '>') {
NEXT;
} else {
ctxt->errNo = XML_ERR_GT_REQUIRED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Couldn't find end of Start Tag %s\n",
name);
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
/*
* end of parsing of this node.
*/
nodePop(ctxt);
oldname = namePop(ctxt);
spacePop(ctxt);
if (oldname != NULL) {
#ifdef DEBUG_STACK
xmlGenericError(xmlGenericErrorContext,"Close: popped %s\n", oldname);
#endif
xmlFree(oldname);
}
}
xmlFree(name);
ctxt->instate = XML_PARSER_CONTENT;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering CONTENT\n");
#endif
break;
}
case XML_PARSER_CONTENT: {
const xmlChar *test;
int cons;
int tok;
/*
* Handle preparsed entities and charRef
*/
if (ctxt->token != 0) {
xmlChar current[2] = { 0 , 0 } ;
current[0] = (xmlChar) ctxt->token;
if ((ctxt->sax != NULL) && (!ctxt->disableSAX) &&
(ctxt->sax->characters != NULL))
ctxt->sax->characters(ctxt->userData, current, 1);
ctxt->token = 0;
}
if ((avail < 2) && (ctxt->inputNr == 1))
goto done;
cur = ctxt->input->cur[0];
next = ctxt->input->cur[1];
test = CUR_PTR;
cons = ctxt->input->consumed;
tok = ctxt->token;
if ((cur == '<') && (next == '?')) {
if ((!terminate) &&
(xmlParseLookupSequence(ctxt, '?', '>', 0) < 0))
goto done;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: Parsing PI\n");
#endif
xmlParsePI(ctxt);
} else if ((cur == '<') && (next == '!') &&
(ctxt->input->cur[2] == '-') && (ctxt->input->cur[3] == '-')) {
if ((!terminate) &&
(xmlParseLookupSequence(ctxt, '-', '-', '>') < 0))
goto done;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: Parsing Comment\n");
#endif
xmlParseComment(ctxt);
ctxt->instate = XML_PARSER_CONTENT;
} else if ((cur == '<') && (ctxt->input->cur[1] == '!') &&
(ctxt->input->cur[2] == '[') && (NXT(3) == 'C') &&
(ctxt->input->cur[4] == 'D') && (NXT(5) == 'A') &&
(ctxt->input->cur[6] == 'T') && (NXT(7) == 'A') &&
(ctxt->input->cur[8] == '[')) {
SKIP(9);
ctxt->instate = XML_PARSER_CDATA_SECTION;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering CDATA_SECTION\n");
#endif
break;
} else if ((cur == '<') && (next == '!') &&
(avail < 9)) {
goto done;
} else if ((cur == '<') && (next == '/')) {
ctxt->instate = XML_PARSER_END_TAG;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering END_TAG\n");
#endif
break;
} else if (cur == '<') {
ctxt->instate = XML_PARSER_START_TAG;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering START_TAG\n");
#endif
break;
} else if (cur == '&') {
if ((!terminate) &&
(xmlParseLookupSequence(ctxt, ';', 0, 0) < 0))
goto done;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: Parsing Reference\n");
#endif
xmlParseReference(ctxt);
} else {
/* TODO Avoid the extra copy, handle directly !!! */
/*
* Goal of the following test is:
* - minimize calls to the SAX 'character' callback
* when they are mergeable
* - handle an problem for isBlank when we only parse
* a sequence of blank chars and the next one is
* not available to check against '<' presence.
* - tries to homogenize the differences in SAX
* callbacks beween the push and pull versions
* of the parser.
*/
if ((ctxt->inputNr == 1) &&
(avail < XML_PARSER_BIG_BUFFER_SIZE)) {
if ((!terminate) &&
(xmlParseLookupSequence(ctxt, '<', 0, 0) < 0))
goto done;
}
ctxt->checkIndex = 0;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: Parsing char data\n");
#endif
xmlParseCharData(ctxt, 0);
}
/*
* Pop-up of finished entities.
*/
while ((RAW == 0) && (ctxt->inputNr > 1))
xmlPopInput(ctxt);
if ((cons == ctxt->input->consumed) && (test == CUR_PTR) &&
(tok == ctxt->token)) {
ctxt->errNo = XML_ERR_INTERNAL_ERROR;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"detected an error in element content\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
ctxt->instate = XML_PARSER_EOF;
break;
}
break;
}
case XML_PARSER_CDATA_SECTION: {
/*
* The Push mode need to have the SAX callback for
* cdataBlock merge back contiguous callbacks.
*/
int base;
base = xmlParseLookupSequence(ctxt, ']', ']', '>');
if (base < 0) {
if (avail >= XML_PARSER_BIG_BUFFER_SIZE + 2) {
if ((ctxt->sax != NULL) && (!ctxt->disableSAX)) {
if (ctxt->sax->cdataBlock != NULL)
ctxt->sax->cdataBlock(ctxt->userData, ctxt->input->cur,
XML_PARSER_BIG_BUFFER_SIZE);
}
SKIP(XML_PARSER_BIG_BUFFER_SIZE);
ctxt->checkIndex = 0;
}
goto done;
} else {
if ((ctxt->sax != NULL) && (base > 0) &&
(!ctxt->disableSAX)) {
if (ctxt->sax->cdataBlock != NULL)
ctxt->sax->cdataBlock(ctxt->userData,
ctxt->input->cur, base);
}
SKIP(base + 3);
ctxt->checkIndex = 0;
ctxt->instate = XML_PARSER_CONTENT;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering CONTENT\n");
#endif
}
break;
}
case XML_PARSER_END_TAG:
if (avail < 2)
goto done;
if ((!terminate) &&
(xmlParseLookupSequence(ctxt, '>', 0, 0) < 0))
goto done;
xmlParseEndTag(ctxt);
if (ctxt->name == NULL) {
ctxt->instate = XML_PARSER_EPILOG;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering EPILOG\n");
#endif
} else {
ctxt->instate = XML_PARSER_CONTENT;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering CONTENT\n");
#endif
}
break;
case XML_PARSER_DTD: {
/*
* Sorry but progressive parsing of the internal subset
* is not expected to be supported. We first check that
* the full content of the internal subset is available and
* the parsing is launched only at that point.
* Internal subset ends up with "']' S? '>'" in an unescaped
* section and not in a ']]>' sequence which are conditional
* sections (whoever argued to keep that crap in XML deserve
* a place in hell !).
*/
int base, i;
xmlChar *buf;
xmlChar quote = 0;
base = ctxt->input->cur - ctxt->input->base;
if (base < 0) return(0);
if (ctxt->checkIndex > base)
base = ctxt->checkIndex;
buf = ctxt->input->buf->buffer->content;
for (;(unsigned int) base < ctxt->input->buf->buffer->use;
base++) {
if (quote != 0) {
if (buf[base] == quote)
quote = 0;
continue;
}
if (buf[base] == '"') {
quote = '"';
continue;
}
if (buf[base] == '\'') {
quote = '\'';
continue;
}
if (buf[base] == ']') {
if ((unsigned int) base +1 >=
ctxt->input->buf->buffer->use)
break;
if (buf[base + 1] == ']') {
/* conditional crap, skip both ']' ! */
base++;
continue;
}
for (i = 0;
(unsigned int) base + i < ctxt->input->buf->buffer->use;
i++) {
if (buf[base + i] == '>')
goto found_end_int_subset;
}
break;
}
}
/*
* We didn't found the end of the Internal subset
*/
if (quote == 0)
ctxt->checkIndex = base;
#ifdef DEBUG_PUSH
if (next == 0)
xmlGenericError(xmlGenericErrorContext,
"PP: lookup of int subset end filed\n");
#endif
goto done;
found_end_int_subset:
xmlParseInternalSubset(ctxt);
ctxt->inSubset = 2;
if ((ctxt->sax != NULL) && (!ctxt->disableSAX) &&
(ctxt->sax->externalSubset != NULL))
ctxt->sax->externalSubset(ctxt->userData, ctxt->intSubName,
ctxt->extSubSystem, ctxt->extSubURI);
ctxt->inSubset = 0;
ctxt->instate = XML_PARSER_PROLOG;
ctxt->checkIndex = 0;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering PROLOG\n");
#endif
break;
}
case XML_PARSER_COMMENT:
xmlGenericError(xmlGenericErrorContext,
"PP: internal error, state == COMMENT\n");
ctxt->instate = XML_PARSER_CONTENT;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering CONTENT\n");
#endif
break;
case XML_PARSER_PI:
xmlGenericError(xmlGenericErrorContext,
"PP: internal error, state == PI\n");
ctxt->instate = XML_PARSER_CONTENT;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering CONTENT\n");
#endif
break;
case XML_PARSER_ENTITY_DECL:
xmlGenericError(xmlGenericErrorContext,
"PP: internal error, state == ENTITY_DECL\n");
ctxt->instate = XML_PARSER_DTD;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering DTD\n");
#endif
break;
case XML_PARSER_ENTITY_VALUE:
xmlGenericError(xmlGenericErrorContext,
"PP: internal error, state == ENTITY_VALUE\n");
ctxt->instate = XML_PARSER_CONTENT;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering DTD\n");
#endif
break;
case XML_PARSER_ATTRIBUTE_VALUE:
xmlGenericError(xmlGenericErrorContext,
"PP: internal error, state == ATTRIBUTE_VALUE\n");
ctxt->instate = XML_PARSER_START_TAG;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering START_TAG\n");
#endif
break;
case XML_PARSER_SYSTEM_LITERAL:
xmlGenericError(xmlGenericErrorContext,
"PP: internal error, state == SYSTEM_LITERAL\n");
ctxt->instate = XML_PARSER_START_TAG;
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext,
"PP: entering START_TAG\n");
#endif
break;
}
}
done:
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext, "PP: done %d\n", ret);
#endif
return(ret);
}
/**
* xmlParseChunk:
* @ctxt: an XML parser context
* @chunk: an char array
* @size: the size in byte of the chunk
* @terminate: last chunk indicator
*
* Parse a Chunk of memory
*
* Returns zero if no error, the xmlParserErrors otherwise.
*/
int
xmlParseChunk(xmlParserCtxtPtr ctxt, const char *chunk, int size,
int terminate) {
if ((size > 0) && (chunk != NULL) && (ctxt->input != NULL) &&
(ctxt->input->buf != NULL) && (ctxt->instate != XML_PARSER_EOF)) {
int base = ctxt->input->base - ctxt->input->buf->buffer->content;
int cur = ctxt->input->cur - ctxt->input->base;
xmlParserInputBufferPush(ctxt->input->buf, size, chunk);
ctxt->input->base = ctxt->input->buf->buffer->content + base;
ctxt->input->cur = ctxt->input->base + cur;
ctxt->input->end =
&ctxt->input->buf->buffer->content[ctxt->input->buf->buffer->use];
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext, "PP: pushed %d\n", size);
#endif
if ((terminate) || (ctxt->input->buf->buffer->use > 80))
xmlParseTryOrFinish(ctxt, terminate);
} else if (ctxt->instate != XML_PARSER_EOF) {
if ((ctxt->input != NULL) && ctxt->input->buf != NULL) {
xmlParserInputBufferPtr in = ctxt->input->buf;
if ((in->encoder != NULL) && (in->buffer != NULL) &&
(in->raw != NULL)) {
int nbchars;
nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
if (nbchars < 0) {
xmlGenericError(xmlGenericErrorContext,
"xmlParseChunk: encoder error\n");
return(XML_ERR_INVALID_ENCODING);
}
}
}
}
xmlParseTryOrFinish(ctxt, terminate);
if (terminate) {
/*
* Check for termination
*/
if ((ctxt->instate != XML_PARSER_EOF) &&
(ctxt->instate != XML_PARSER_EPILOG)) {
ctxt->errNo = XML_ERR_DOCUMENT_END;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"Extra content at the end of the document\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (ctxt->instate != XML_PARSER_EOF) {
if ((ctxt->sax) && (ctxt->sax->endDocument != NULL) &&
(!ctxt->disableSAX))
ctxt->sax->endDocument(ctxt->userData);
}
ctxt->instate = XML_PARSER_EOF;
}
return((xmlParserErrors) ctxt->errNo);
}
/************************************************************************
* *
* I/O front end functions to the parser *
* *
************************************************************************/
/**
* xmlStopParser:
* @ctxt: an XML parser context
*
* Blocks further parser processing
*/
void
xmlStopParser(xmlParserCtxtPtr ctxt) {
ctxt->instate = XML_PARSER_EOF;
if (ctxt->input != NULL)
ctxt->input->cur = BAD_CAST"";
}
/**
* xmlCreatePushParserCtxt:
* @sax: a SAX handler
* @user_data: The user data returned on SAX callbacks
* @chunk: a pointer to an array of chars
* @size: number of chars in the array
* @filename: an optional file name or URI
*
* Create a parser context for using the XML parser in push mode
* To allow content encoding detection, @size should be >= 4
* The value of @filename is used for fetching external entities
* and error/warning reports.
*
* Returns the new parser context or NULL
*/
xmlParserCtxtPtr
xmlCreatePushParserCtxt(xmlSAXHandlerPtr sax, void *user_data,
const char *chunk, int size, const char *filename) {
xmlParserCtxtPtr ctxt;
xmlParserInputPtr inputStream;
xmlParserInputBufferPtr buf;
xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
/*
* plug some encoding conversion routines
*/
if ((chunk != NULL) && (size >= 4))
enc = xmlDetectCharEncoding((const xmlChar *) chunk, size);
buf = xmlAllocParserInputBuffer(enc);
if (buf == NULL) return(NULL);
ctxt = xmlNewParserCtxt();
if (ctxt == NULL) {
xmlFree(buf);
return(NULL);
}
if (sax != NULL) {
if (ctxt->sax != &xmlDefaultSAXHandler)
xmlFree(ctxt->sax);
ctxt->sax = (xmlSAXHandlerPtr) xmlMalloc(sizeof(xmlSAXHandler));
if (ctxt->sax == NULL) {
xmlFree(buf);
xmlFree(ctxt);
return(NULL);
}
memcpy(ctxt->sax, sax, sizeof(xmlSAXHandler));
if (user_data != NULL)
ctxt->userData = user_data;
}
if (filename == NULL) {
ctxt->directory = NULL;
} else {
ctxt->directory = xmlParserGetDirectory(filename);
}
inputStream = xmlNewInputStream(ctxt);
if (inputStream == NULL) {
xmlFreeParserCtxt(ctxt);
return(NULL);
}
if (filename == NULL)
inputStream->filename = NULL;
else
inputStream->filename = xmlMemStrdup(filename);
inputStream->buf = buf;
inputStream->base = inputStream->buf->buffer->content;
inputStream->cur = inputStream->buf->buffer->content;
inputStream->end =
&inputStream->buf->buffer->content[inputStream->buf->buffer->use];
inputPush(ctxt, inputStream);
if ((size > 0) && (chunk != NULL) && (ctxt->input != NULL) &&
(ctxt->input->buf != NULL)) {
xmlParserInputBufferPush(ctxt->input->buf, size, chunk);
#ifdef DEBUG_PUSH
xmlGenericError(xmlGenericErrorContext, "PP: pushed %d\n", size);
#endif
}
if (enc != XML_CHAR_ENCODING_NONE) {
xmlSwitchEncoding(ctxt, enc);
}
return(ctxt);
}
/**
* xmlCreateIOParserCtxt:
* @sax: a SAX handler
* @user_data: The user data returned on SAX callbacks
* @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 parser context for using the XML parser with an existing
* I/O stream
*
* Returns the new parser context or NULL
*/
xmlParserCtxtPtr
xmlCreateIOParserCtxt(xmlSAXHandlerPtr sax, void *user_data,
xmlInputReadCallback ioread, xmlInputCloseCallback ioclose,
void *ioctx, xmlCharEncoding enc) {
xmlParserCtxtPtr ctxt;
xmlParserInputPtr inputStream;
xmlParserInputBufferPtr buf;
buf = xmlParserInputBufferCreateIO(ioread, ioclose, ioctx, enc);
if (buf == NULL) return(NULL);
ctxt = xmlNewParserCtxt();
if (ctxt == NULL) {
xmlFree(buf);
return(NULL);
}
if (sax != NULL) {
if (ctxt->sax != &xmlDefaultSAXHandler)
xmlFree(ctxt->sax);
ctxt->sax = (xmlSAXHandlerPtr) xmlMalloc(sizeof(xmlSAXHandler));
if (ctxt->sax == NULL) {
xmlFree(buf);
xmlFree(ctxt);
return(NULL);
}
memcpy(ctxt->sax, sax, sizeof(xmlSAXHandler));
if (user_data != NULL)
ctxt->userData = user_data;
}
inputStream = xmlNewIOInputStream(ctxt, buf, enc);
if (inputStream == NULL) {
xmlFreeParserCtxt(ctxt);
return(NULL);
}
inputPush(ctxt, inputStream);
return(ctxt);
}
/************************************************************************
* *
* Front ends when parsing a Dtd *
* *
************************************************************************/
/**
* xmlIOParseDTD:
* @sax: the SAX handler block or NULL
* @input: an Input Buffer
* @enc: the charset encoding if known
*
* Load and parse a DTD
*
* Returns the resulting xmlDtdPtr or NULL in case of error.
* @input will be freed at parsing end.
*/
xmlDtdPtr
xmlIOParseDTD(xmlSAXHandlerPtr sax, xmlParserInputBufferPtr input,
xmlCharEncoding enc) {
xmlDtdPtr ret = NULL;
xmlParserCtxtPtr ctxt;
xmlParserInputPtr pinput = NULL;
xmlChar start[4];
if (input == NULL)
return(NULL);
ctxt = xmlNewParserCtxt();
if (ctxt == NULL) {
return(NULL);
}
/*
* Set-up the SAX context
*/
if (sax != NULL) {
if (ctxt->sax != NULL)
xmlFree(ctxt->sax);
ctxt->sax = sax;
ctxt->userData = NULL;
}
/*
* generate a parser input from the I/O handler
*/
pinput = xmlNewIOInputStream(ctxt, input, enc);
if (pinput == NULL) {
if (sax != NULL) ctxt->sax = NULL;
xmlFreeParserCtxt(ctxt);
return(NULL);
}
/*
* plug some encoding conversion routines here.
*/
xmlPushInput(ctxt, pinput);
pinput->filename = NULL;
pinput->line = 1;
pinput->col = 1;
pinput->base = ctxt->input->cur;
pinput->cur = ctxt->input->cur;
pinput->free = NULL;
/*
* let's parse that entity knowing it's an external subset.
*/
ctxt->inSubset = 2;
ctxt->myDoc = xmlNewDoc(BAD_CAST "1.0");
ctxt->myDoc->extSubset = xmlNewDtd(ctxt->myDoc, BAD_CAST "none",
BAD_CAST "none", BAD_CAST "none");
if (enc == XML_CHAR_ENCODING_NONE) {
/*
* Get the 4 first bytes and decode the charset
* if enc != XML_CHAR_ENCODING_NONE
* plug some encoding conversion routines.
*/
start[0] = RAW;
start[1] = NXT(1);
start[2] = NXT(2);
start[3] = NXT(3);
enc = xmlDetectCharEncoding(start, 4);
if (enc != XML_CHAR_ENCODING_NONE) {
xmlSwitchEncoding(ctxt, enc);
}
}
xmlParseExternalSubset(ctxt, BAD_CAST "none", BAD_CAST "none");
if (ctxt->myDoc != NULL) {
if (ctxt->wellFormed) {
ret = ctxt->myDoc->extSubset;
ctxt->myDoc->extSubset = NULL;
} else {
ret = NULL;
}
xmlFreeDoc(ctxt->myDoc);
ctxt->myDoc = NULL;
}
if (sax != NULL) ctxt->sax = NULL;
xmlFreeParserCtxt(ctxt);
return(ret);
}
/**
* xmlSAXParseDTD:
* @sax: the SAX handler block
* @ExternalID: a NAME* containing the External ID of the DTD
* @SystemID: a NAME* containing the URL to the DTD
*
* Load and parse an external subset.
*
* Returns the resulting xmlDtdPtr or NULL in case of error.
*/
xmlDtdPtr
xmlSAXParseDTD(xmlSAXHandlerPtr sax, const xmlChar *ExternalID,
const xmlChar *SystemID) {
xmlDtdPtr ret = NULL;
xmlParserCtxtPtr ctxt;
xmlParserInputPtr input = NULL;
xmlCharEncoding enc;
if ((ExternalID == NULL) && (SystemID == NULL)) return(NULL);
ctxt = xmlNewParserCtxt();
if (ctxt == NULL) {
return(NULL);
}
/*
* Set-up the SAX context
*/
if (sax != NULL) {
if (ctxt->sax != NULL)
xmlFree(ctxt->sax);
ctxt->sax = sax;
ctxt->userData = NULL;
}
/*
* Ask the Entity resolver to load the damn thing
*/
if ((ctxt->sax != NULL) && (ctxt->sax->resolveEntity != NULL))
input = ctxt->sax->resolveEntity(ctxt->userData, ExternalID, SystemID);
if (input == NULL) {
if (sax != NULL) ctxt->sax = NULL;
xmlFreeParserCtxt(ctxt);
return(NULL);
}
/*
* plug some encoding conversion routines here.
*/
xmlPushInput(ctxt, input);
enc = xmlDetectCharEncoding(ctxt->input->cur, 4);
xmlSwitchEncoding(ctxt, enc);
if (input->filename == NULL)
input->filename = (char *) xmlStrdup(SystemID);
input->line = 1;
input->col = 1;
input->base = ctxt->input->cur;
input->cur = ctxt->input->cur;
input->free = NULL;
/*
* let's parse that entity knowing it's an external subset.
*/
ctxt->inSubset = 2;
ctxt->myDoc = xmlNewDoc(BAD_CAST "1.0");
ctxt->myDoc->extSubset = xmlNewDtd(ctxt->myDoc, BAD_CAST "none",
ExternalID, SystemID);
xmlParseExternalSubset(ctxt, ExternalID, SystemID);
if (ctxt->myDoc != NULL) {
if (ctxt->wellFormed) {
ret = ctxt->myDoc->extSubset;
ctxt->myDoc->extSubset = NULL;
} else {
ret = NULL;
}
xmlFreeDoc(ctxt->myDoc);
ctxt->myDoc = NULL;
}
if (sax != NULL) ctxt->sax = NULL;
xmlFreeParserCtxt(ctxt);
return(ret);
}
/**
* xmlParseDTD:
* @ExternalID: a NAME* containing the External ID of the DTD
* @SystemID: a NAME* containing the URL to the DTD
*
* Load and parse an external subset.
*
* Returns the resulting xmlDtdPtr or NULL in case of error.
*/
xmlDtdPtr
xmlParseDTD(const xmlChar *ExternalID, const xmlChar *SystemID) {
return(xmlSAXParseDTD(NULL, ExternalID, SystemID));
}
/************************************************************************
* *
* Front ends when parsing an Entity *
* *
************************************************************************/
/**
* xmlParseCtxtExternalEntity:
* @ctx: the existing parsing context
* @URL: the URL for the entity to load
* @ID: the System ID for the entity to load
* @lst: the return value for the set of parsed nodes
*
* Parse an external general entity within an existing parsing context
* An external general parsed entity is well-formed if it matches the
* production labeled extParsedEnt.
*
* [78] extParsedEnt ::= TextDecl? content
*
* Returns 0 if the entity is well formed, -1 in case of args problem and
* the parser error code otherwise
*/
int
xmlParseCtxtExternalEntity(xmlParserCtxtPtr ctx, const xmlChar *URL,
const xmlChar *ID, xmlNodePtr *lst) {
xmlParserCtxtPtr ctxt;
xmlDocPtr newDoc;
xmlSAXHandlerPtr oldsax = NULL;
int ret = 0;
xmlChar start[4];
xmlCharEncoding enc;
if (ctx->depth > 40) {
return(XML_ERR_ENTITY_LOOP);
}
if (lst != NULL)
*lst = NULL;
if ((URL == NULL) && (ID == NULL))
return(-1);
if (ctx->myDoc == NULL) /* @@ relax but check for dereferences */
return(-1);
ctxt = xmlCreateEntityParserCtxt(URL, ID, NULL);
if (ctxt == NULL) return(-1);
ctxt->userData = ctxt;
oldsax = ctxt->sax;
ctxt->sax = ctx->sax;
newDoc = xmlNewDoc(BAD_CAST "1.0");
if (newDoc == NULL) {
xmlFreeParserCtxt(ctxt);
return(-1);
}
if (ctx->myDoc != NULL) {
newDoc->intSubset = ctx->myDoc->intSubset;
newDoc->extSubset = ctx->myDoc->extSubset;
}
if (ctx->myDoc->URL != NULL) {
newDoc->URL = xmlStrdup(ctx->myDoc->URL);
}
newDoc->children = xmlNewDocNode(newDoc, NULL, BAD_CAST "pseudoroot", NULL);
if (newDoc->children == NULL) {
ctxt->sax = oldsax;
xmlFreeParserCtxt(ctxt);
newDoc->intSubset = NULL;
newDoc->extSubset = NULL;
xmlFreeDoc(newDoc);
return(-1);
}
nodePush(ctxt, newDoc->children);
if (ctx->myDoc == NULL) {
ctxt->myDoc = newDoc;
} else {
ctxt->myDoc = ctx->myDoc;
newDoc->children->doc = ctx->myDoc;
}
/*
* Get the 4 first bytes and decode the charset
* if enc != XML_CHAR_ENCODING_NONE
* plug some encoding conversion routines.
*/
GROW
start[0] = RAW;
start[1] = NXT(1);
start[2] = NXT(2);
start[3] = NXT(3);
enc = xmlDetectCharEncoding(start, 4);
if (enc != XML_CHAR_ENCODING_NONE) {
xmlSwitchEncoding(ctxt, enc);
}
/*
* Parse a possible text declaration first
*/
if ((RAW == '<') && (NXT(1) == '?') &&
(NXT(2) == 'x') && (NXT(3) == 'm') &&
(NXT(4) == 'l') && (IS_BLANK(NXT(5)))) {
xmlParseTextDecl(ctxt);
}
/*
* Doing validity checking on chunk doesn't make sense
*/
ctxt->instate = XML_PARSER_CONTENT;
ctxt->validate = ctx->validate;
ctxt->loadsubset = ctx->loadsubset;
ctxt->depth = ctx->depth + 1;
ctxt->replaceEntities = ctx->replaceEntities;
if (ctxt->validate) {
ctxt->vctxt.error = ctx->vctxt.error;
ctxt->vctxt.warning = ctx->vctxt.warning;
} else {
ctxt->vctxt.error = NULL;
ctxt->vctxt.warning = NULL;
}
ctxt->vctxt.nodeTab = NULL;
ctxt->vctxt.nodeNr = 0;
ctxt->vctxt.nodeMax = 0;
ctxt->vctxt.node = NULL;
xmlParseContent(ctxt);
if ((RAW == '<') && (NXT(1) == '/')) {
ctxt->errNo = XML_ERR_NOT_WELL_BALANCED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"chunk is not well balanced\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else if (RAW != 0) {
ctxt->errNo = XML_ERR_EXTRA_CONTENT;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"extra content at the end of well balanced chunk\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (ctxt->node != newDoc->children) {
ctxt->errNo = XML_ERR_NOT_WELL_BALANCED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"chunk is not well balanced\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (!ctxt->wellFormed) {
if (ctxt->errNo == 0)
ret = 1;
else
ret = ctxt->errNo;
} else {
if (lst != NULL) {
xmlNodePtr cur;
/*
* Return the newly created nodeset after unlinking it from
* they pseudo parent.
*/
cur = newDoc->children->children;
*lst = cur;
while (cur != NULL) {
cur->parent = NULL;
cur = cur->next;
}
newDoc->children->children = NULL;
}
ret = 0;
}
ctxt->sax = oldsax;
xmlFreeParserCtxt(ctxt);
newDoc->intSubset = NULL;
newDoc->extSubset = NULL;
xmlFreeDoc(newDoc);
return(ret);
}
/**
* xmlParseExternalEntityPrivate:
* @doc: the document the chunk pertains to
* @oldctxt: the previous parser context if available
* @sax: the SAX handler bloc (possibly NULL)
* @user_data: The user data returned on SAX callbacks (possibly NULL)
* @depth: Used for loop detection, use 0
* @URL: the URL for the entity to load
* @ID: the System ID for the entity to load
* @list: the return value for the set of parsed nodes
*
* Private version of xmlParseExternalEntity()
*
* Returns 0 if the entity is well formed, -1 in case of args problem and
* the parser error code otherwise
*/
static int
xmlParseExternalEntityPrivate(xmlDocPtr doc, xmlParserCtxtPtr oldctxt,
xmlSAXHandlerPtr sax,
void *user_data, int depth, const xmlChar *URL,
const xmlChar *ID, xmlNodePtr *list) {
xmlParserCtxtPtr ctxt;
xmlDocPtr newDoc;
xmlSAXHandlerPtr oldsax = NULL;
int ret = 0;
xmlChar start[4];
xmlCharEncoding enc;
if (depth > 40) {
return(XML_ERR_ENTITY_LOOP);
}
if (list != NULL)
*list = NULL;
if ((URL == NULL) && (ID == NULL))
return(-1);
if (doc == NULL) /* @@ relax but check for dereferences */
return(-1);
ctxt = xmlCreateEntityParserCtxt(URL, ID, NULL);
if (ctxt == NULL) return(-1);
ctxt->userData = ctxt;
if (oldctxt != NULL) {
ctxt->_private = oldctxt->_private;
ctxt->loadsubset = oldctxt->loadsubset;
ctxt->validate = oldctxt->validate;
ctxt->external = oldctxt->external;
} else {
/*
* Doing validity checking on chunk without context
* doesn't make sense
*/
ctxt->_private = NULL;
ctxt->validate = 0;
ctxt->external = 2;
ctxt->loadsubset = 0;
}
if (sax != NULL) {
oldsax = ctxt->sax;
ctxt->sax = sax;
if (user_data != NULL)
ctxt->userData = user_data;
}
newDoc = xmlNewDoc(BAD_CAST "1.0");
if (newDoc == NULL) {
xmlFreeParserCtxt(ctxt);
return(-1);
}
if (doc != NULL) {
newDoc->intSubset = doc->intSubset;
newDoc->extSubset = doc->extSubset;
}
if (doc->URL != NULL) {
newDoc->URL = xmlStrdup(doc->URL);
}
newDoc->children = xmlNewDocNode(newDoc, NULL, BAD_CAST "pseudoroot", NULL);
if (newDoc->children == NULL) {
if (sax != NULL)
ctxt->sax = oldsax;
xmlFreeParserCtxt(ctxt);
newDoc->intSubset = NULL;
newDoc->extSubset = NULL;
xmlFreeDoc(newDoc);
return(-1);
}
nodePush(ctxt, newDoc->children);
if (doc == NULL) {
ctxt->myDoc = newDoc;
} else {
ctxt->myDoc = doc;
newDoc->children->doc = doc;
}
/*
* Get the 4 first bytes and decode the charset
* if enc != XML_CHAR_ENCODING_NONE
* plug some encoding conversion routines.
*/
GROW;
start[0] = RAW;
start[1] = NXT(1);
start[2] = NXT(2);
start[3] = NXT(3);
enc = xmlDetectCharEncoding(start, 4);
if (enc != XML_CHAR_ENCODING_NONE) {
xmlSwitchEncoding(ctxt, enc);
}
/*
* Parse a possible text declaration first
*/
if ((RAW == '<') && (NXT(1) == '?') &&
(NXT(2) == 'x') && (NXT(3) == 'm') &&
(NXT(4) == 'l') && (IS_BLANK(NXT(5)))) {
xmlParseTextDecl(ctxt);
}
ctxt->instate = XML_PARSER_CONTENT;
ctxt->depth = depth;
xmlParseContent(ctxt);
if ((RAW == '<') && (NXT(1) == '/')) {
ctxt->errNo = XML_ERR_NOT_WELL_BALANCED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"chunk is not well balanced\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else if (RAW != 0) {
ctxt->errNo = XML_ERR_EXTRA_CONTENT;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"extra content at the end of well balanced chunk\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (ctxt->node != newDoc->children) {
ctxt->errNo = XML_ERR_NOT_WELL_BALANCED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"chunk is not well balanced\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (!ctxt->wellFormed) {
if (ctxt->errNo == 0)
ret = 1;
else
ret = ctxt->errNo;
} else {
if (list != NULL) {
xmlNodePtr cur;
/*
* Return the newly created nodeset after unlinking it from
* they pseudo parent.
*/
cur = newDoc->children->children;
*list = cur;
while (cur != NULL) {
cur->parent = NULL;
cur = cur->next;
}
newDoc->children->children = NULL;
}
ret = 0;
}
if (sax != NULL)
ctxt->sax = oldsax;
xmlFreeParserCtxt(ctxt);
newDoc->intSubset = NULL;
newDoc->extSubset = NULL;
xmlFreeDoc(newDoc);
return(ret);
}
/**
* xmlParseExternalEntity:
* @doc: the document the chunk pertains to
* @sax: the SAX handler bloc (possibly NULL)
* @user_data: The user data returned on SAX callbacks (possibly NULL)
* @depth: Used for loop detection, use 0
* @URL: the URL for the entity to load
* @ID: the System ID for the entity to load
* @lst: the return value for the set of parsed nodes
*
* Parse an external general entity
* An external general parsed entity is well-formed if it matches the
* production labeled extParsedEnt.
*
* [78] extParsedEnt ::= TextDecl? content
*
* Returns 0 if the entity is well formed, -1 in case of args problem and
* the parser error code otherwise
*/
int
xmlParseExternalEntity(xmlDocPtr doc, xmlSAXHandlerPtr sax, void *user_data,
int depth, const xmlChar *URL, const xmlChar *ID, xmlNodePtr *lst) {
return(xmlParseExternalEntityPrivate(doc, NULL, sax, user_data, depth, URL,
ID, lst));
}
/**
* xmlParseBalancedChunkMemory:
* @doc: the document the chunk pertains to
* @sax: the SAX handler bloc (possibly NULL)
* @user_data: The user data returned on SAX callbacks (possibly NULL)
* @depth: Used for loop detection, use 0
* @string: the input string in UTF8 or ISO-Latin (zero terminated)
* @lst: the return value for the set of parsed nodes
*
* Parse a well-balanced chunk of an XML document
* called by the parser
* The allowed sequence for the Well Balanced Chunk is the one defined by
* the content production in the XML grammar:
*
* [43] content ::= (element | CharData | Reference | CDSect | PI | Comment)*
*
* Returns 0 if the chunk is well balanced, -1 in case of args problem and
* the parser error code otherwise
*/
int
xmlParseBalancedChunkMemory(xmlDocPtr doc, xmlSAXHandlerPtr sax,
void *user_data, int depth, const xmlChar *string, xmlNodePtr *lst) {
xmlParserCtxtPtr ctxt;
xmlDocPtr newDoc;
xmlSAXHandlerPtr oldsax = NULL;
int size;
int ret = 0;
if (depth > 40) {
return(XML_ERR_ENTITY_LOOP);
}
if (lst != NULL)
*lst = NULL;
if (string == NULL)
return(-1);
size = xmlStrlen(string);
ctxt = xmlCreateMemoryParserCtxt((char *) string, size);
if (ctxt == NULL) return(-1);
ctxt->userData = ctxt;
if (sax != NULL) {
oldsax = ctxt->sax;
ctxt->sax = sax;
if (user_data != NULL)
ctxt->userData = user_data;
}
newDoc = xmlNewDoc(BAD_CAST "1.0");
if (newDoc == NULL) {
xmlFreeParserCtxt(ctxt);
return(-1);
}
if (doc != NULL) {
newDoc->intSubset = doc->intSubset;
newDoc->extSubset = doc->extSubset;
}
newDoc->children = xmlNewDocNode(newDoc, NULL, BAD_CAST "pseudoroot", NULL);
if (newDoc->children == NULL) {
if (sax != NULL)
ctxt->sax = oldsax;
xmlFreeParserCtxt(ctxt);
newDoc->intSubset = NULL;
newDoc->extSubset = NULL;
xmlFreeDoc(newDoc);
return(-1);
}
nodePush(ctxt, newDoc->children);
if (doc == NULL) {
ctxt->myDoc = newDoc;
} else {
ctxt->myDoc = doc;
newDoc->children->doc = doc;
}
ctxt->instate = XML_PARSER_CONTENT;
ctxt->depth = depth;
/*
* Doing validity checking on chunk doesn't make sense
*/
ctxt->validate = 0;
ctxt->loadsubset = 0;
xmlParseContent(ctxt);
if ((RAW == '<') && (NXT(1) == '/')) {
ctxt->errNo = XML_ERR_NOT_WELL_BALANCED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"chunk is not well balanced\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
} else if (RAW != 0) {
ctxt->errNo = XML_ERR_EXTRA_CONTENT;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"extra content at the end of well balanced chunk\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (ctxt->node != newDoc->children) {
ctxt->errNo = XML_ERR_NOT_WELL_BALANCED;
if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL))
ctxt->sax->error(ctxt->userData,
"chunk is not well balanced\n");
ctxt->wellFormed = 0;
ctxt->disableSAX = 1;
}
if (!ctxt->wellFormed) {
if (ctxt->errNo == 0)
ret = 1;
else
ret = ctxt->errNo;
} else {
if (lst != NULL) {
xmlNodePtr cur;
/*
* Return the newly created nodeset after unlinking it from
* they pseudo parent.
*/
cur = newDoc->children->children;
*lst = cur;
while (cur != NULL) {
cur->parent = NULL;
cur = cur->next;
}
newDoc->children->children = NULL;
}
ret = 0;
}
if (sax != NULL)
ctxt->sax = oldsax;
xmlFreeParserCtxt(ctxt);
newDoc->intSubset = NULL;
newDoc->extSubset = NULL;
xmlFreeDoc(newDoc);
return(ret);
}
/**
* xmlSAXParseEntity:
* @sax: the SAX handler block
* @filename: the filename
*
* parse an XML external entity out of context and build a tree.
* It use the given SAX function block to handle the parsing callback.
* If sax is NULL, fallback to the default DOM tree building routines.
*
* [78] extParsedEnt ::= TextDecl? content
*
* This correspond to a "Well Balanced" chunk
*
* Returns the resulting document tree
*/
xmlDocPtr
xmlSAXParseEntity(xmlSAXHandlerPtr sax, const char *filename) {
xmlDocPtr ret;
xmlParserCtxtPtr ctxt;
char *directory = NULL;
ctxt = xmlCreateFileParserCtxt(filename);
if (ctxt == NULL) {
return(NULL);
}
if (sax != NULL) {
if (ctxt->sax != NULL)
xmlFree(ctxt->sax);
ctxt->sax = sax;
ctxt->userData = NULL;
}
if ((ctxt->directory == NULL) && (directory == NULL))
directory = xmlParserGetDirectory(filename);
xmlParseExtParsedEnt(ctxt);
if (ctxt->wellFormed)
ret = ctxt->myDoc;
else {
ret = NULL;
xmlFreeDoc(ctxt->myDoc);
ctxt->myDoc = NULL;
}
if (sax != NULL)
ctxt->sax = NULL;
xmlFreeParserCtxt(ctxt);
return(ret);
}
/**
* xmlParseEntity:
* @filename: the filename
*
* parse an XML external entity out of context and build a tree.
*
* [78] extParsedEnt ::= TextDecl? content
*
* This correspond to a "Well Balanced" chunk
*
* Returns the resulting document tree
*/
xmlDocPtr
xmlParseEntity(const char *filename) {
return(xmlSAXParseEntity(NULL, filename));
}
/**
* xmlCreateEntityParserCtxt:
* @URL: the entity URL
* @ID: the entity PUBLIC ID
* @base: a posible base for the target URI
*
* Create a parser context for an external entity
* Automatic support for ZLIB/Compress compressed document is provided
* by default if found at compile-time.
*
* Returns the new parser context or NULL
*/
xmlParserCtxtPtr
xmlCreateEntityParserCtxt(const xmlChar *URL, const xmlChar *ID,
const xmlChar *base) {
xmlParserCtxtPtr ctxt;
xmlParserInputPtr inputStream;
char *directory = NULL;
xmlChar *uri;
ctxt = xmlNewParserCtxt();
if (ctxt == NULL) {
return(NULL);
}
uri = xmlBuildURI(URL, base);
if (uri == NULL) {
inputStream = xmlLoadExternalEntity((char *)URL, (char *)ID, ctxt);
if (inputStream == NULL) {
xmlFreeParserCtxt(ctxt);
return(NULL);
}
inputPush(ctxt, inputStream);
if ((ctxt->directory == NULL) && (directory == NULL))
directory = xmlParserGetDirectory((char *)URL);
if ((ctxt->directory == NULL) && (directory != NULL))
ctxt->directory = directory;
} else {
inputStream = xmlLoadExternalEntity((char *)uri, (char *)ID, ctxt);
if (inputStream == NULL) {
xmlFree(uri);
xmlFreeParserCtxt(ctxt);
return(NULL);
}
inputPush(ctxt, inputStream);
if ((ctxt->directory == NULL) && (directory == NULL))
directory = xmlParserGetDirectory((char *)uri);
if ((ctxt->directory == NULL) && (directory != NULL))
ctxt->directory = directory;
xmlFree(uri);
}
return(ctxt);
}
/************************************************************************
* *
* Front ends when parsing from a file *
* *
************************************************************************/
/**
* xmlCreateFileParserCtxt:
* @filename: the filename
*
* Create a parser context for a file content.
* Automatic support for ZLIB/Compress compressed document is provided
* by default if found at compile-time.
*
* Returns the new parser context or NULL
*/
xmlParserCtxtPtr
xmlCreateFileParserCtxt(const char *filename)
{
xmlParserCtxtPtr ctxt;
xmlParserInputPtr inputStream;
char *directory = NULL;
ctxt = xmlNewParserCtxt();
if (ctxt == NULL) {
if (xmlDefaultSAXHandler.error != NULL) {
xmlDefaultSAXHandler.error(NULL, "out of memory\n");
}
return(NULL);
}
inputStream = xmlLoadExternalEntity(filename, NULL, ctxt);
if (inputStream == NULL) {
xmlFreeParserCtxt(ctxt);
return(NULL);
}
inputPush(ctxt, inputStream);
if ((ctxt->directory == NULL) && (directory == NULL))
directory = xmlParserGetDirectory(filename);
if ((ctxt->directory == NULL) && (directory != NULL))
ctxt->directory = directory;
return(ctxt);
}
/**
* xmlSAXParseFile:
* @sax: the SAX handler block
* @filename: the filename
* @recovery: work in recovery mode, i.e. tries to read no Well Formed
* documents
*
* parse an XML file and build a tree. Automatic support for ZLIB/Compress
* compressed document is provided by default if found at compile-time.
* It use the given SAX function block to handle the parsing callback.
* If sax is NULL, fallback to the default DOM tree building routines.
*
* Returns the resulting document tree
*/
xmlDocPtr
xmlSAXParseFile(xmlSAXHandlerPtr sax, const char *filename,
int recovery) {
xmlDocPtr ret;
xmlParserCtxtPtr ctxt;
char *directory = NULL;
ctxt = xmlCreateFileParserCtxt(filename);
if (ctxt == NULL) {
return(NULL);
}
if (sax != NULL) {
if (ctxt->sax != NULL)
xmlFree(ctxt->sax);
ctxt->sax = sax;
}
if ((ctxt->directory == NULL) && (directory == NULL))
directory = xmlParserGetDirectory(filename);
if ((ctxt->directory == NULL) && (directory != NULL))
ctxt->directory = (char *) xmlStrdup((xmlChar *) directory);
xmlParseDocument(ctxt);
if ((ctxt->wellFormed) || recovery) ret = ctxt->myDoc;
else {
ret = NULL;
xmlFreeDoc(ctxt->myDoc);
ctxt->myDoc = NULL;
}
if (sax != NULL)
ctxt->sax = NULL;
xmlFreeParserCtxt(ctxt);
return(ret);
}
/**
* xmlRecoverDoc:
* @cur: a pointer to an array of xmlChar
*
* parse an XML in-memory document and build a tree.
* In the case the document is not Well Formed, a tree is built anyway
*
* Returns the resulting document tree
*/
xmlDocPtr
xmlRecoverDoc(xmlChar *cur) {
return(xmlSAXParseDoc(NULL, cur, 1));
}
/**
* xmlParseFile:
* @filename: the filename
*
* parse an XML file and build a tree. Automatic support for ZLIB/Compress
* compressed document is provided by default if found at compile-time.
*
* Returns the resulting document tree
*/
xmlDocPtr
xmlParseFile(const char *filename) {
return(xmlSAXParseFile(NULL, filename, 0));
}
/**
* xmlRecoverFile:
* @filename: the filename
*
* parse an XML file and build a tree. Automatic support for ZLIB/Compress
* compressed document is provided by default if found at compile-time.
* In the case the document is not Well Formed, a tree is built anyway
*
* Returns the resulting document tree
*/
xmlDocPtr
xmlRecoverFile(const char *filename) {
return(xmlSAXParseFile(NULL, filename, 1));
}
/**
* xmlSetupParserForBuffer:
* @ctxt: an XML parser context
* @buffer: a xmlChar * buffer
* @filename: a file name
*
* Setup the parser context to parse a new buffer; Clears any prior
* contents from the parser context. The buffer parameter must not be
* NULL, but the filename parameter can be
*/
void
xmlSetupParserForBuffer(xmlParserCtxtPtr ctxt, const xmlChar* buffer,
const char* filename)
{
xmlParserInputPtr input;
input = xmlNewInputStream(ctxt);
if (input == NULL) {
perror("malloc");
xmlFree(ctxt);
return;
}
xmlClearParserCtxt(ctxt);
if (filename != NULL)
input->filename = xmlMemStrdup(filename);
input->base = buffer;
input->cur = buffer;
input->end = &buffer[xmlStrlen(buffer)];
inputPush(ctxt, input);
}
/**
* xmlSAXUserParseFile:
* @sax: a SAX handler
* @user_data: The user data returned on SAX callbacks
* @filename: a file name
*
* parse an XML file and call the given SAX handler routines.
* Automatic support for ZLIB/Compress compressed document is provided
*
* Returns 0 in case of success or a error number otherwise
*/
int
xmlSAXUserParseFile(xmlSAXHandlerPtr sax, void *user_data,
const char *filename) {
int ret = 0;
xmlParserCtxtPtr ctxt;
ctxt = xmlCreateFileParserCtxt(filename);
if (ctxt == NULL) return -1;
if (ctxt->sax != &xmlDefaultSAXHandler)
xmlFree(ctxt->sax);
ctxt->sax = sax;
if (user_data != NULL)
ctxt->userData = user_data;
xmlParseDocument(ctxt);
if (ctxt->wellFormed)
ret = 0;
else {
if (ctxt->errNo != 0)
ret = ctxt->errNo;
else
ret = -1;
}
if (sax != NULL)
ctxt->sax = NULL;
xmlFreeParserCtxt(ctxt);
return ret;
}
/************************************************************************
* *
* Front ends when parsing from memory *
* *
************************************************************************/
/**
* xmlCreateMemoryParserCtxt:
* @buffer: a pointer to a char array
* @size: the size of the array
*
* Create a parser context for an XML in-memory document.
*
* Returns the new parser context or NULL
*/
xmlParserCtxtPtr
xmlCreateMemoryParserCtxt(const char *buffer, int size) {
xmlParserCtxtPtr ctxt;
xmlParserInputPtr input;
xmlParserInputBufferPtr buf;
if (buffer == NULL)
return(NULL);
if (size <= 0)
return(NULL);
ctxt = xmlNewParserCtxt();
if (ctxt == NULL)
return(NULL);
buf = xmlParserInputBufferCreateMem(buffer, size, XML_CHAR_ENCODING_NONE);
if (buf == NULL) return(NULL);
input = xmlNewInputStream(ctxt);
if (input == NULL) {
xmlFreeParserCtxt(ctxt);
return(NULL);
}
input->filename = NULL;
input->buf = buf;
input->base = input->buf->buffer->content;
input->cur = input->buf->buffer->content;
input->end = &input->buf->buffer->content[input->buf->buffer->use];
inputPush(ctxt, input);
return(ctxt);
}
/**
* xmlSAXParseMemory:
* @sax: the SAX handler block
* @buffer: an pointer to a char array
* @size: the size of the array
* @recovery: work in recovery mode, i.e. tries to read not Well Formed
* documents
*
* parse an XML in-memory block and use the given SAX function block
* to handle the parsing callback. If sax is NULL, fallback to the default
* DOM tree building routines.
*
* Returns the resulting document tree
*/
xmlDocPtr
xmlSAXParseMemory(xmlSAXHandlerPtr sax, const char *buffer,
int size, int recovery) {
xmlDocPtr ret;
xmlParserCtxtPtr ctxt;
ctxt = xmlCreateMemoryParserCtxt(buffer, size);
if (ctxt == NULL) return(NULL);
if (sax != NULL) {
ctxt->sax = sax;
}
xmlParseDocument(ctxt);
if ((ctxt->wellFormed) || recovery) ret = ctxt->myDoc;
else {
ret = NULL;
xmlFreeDoc(ctxt->myDoc);
ctxt->myDoc = NULL;
}
if (sax != NULL)
ctxt->sax = NULL;
xmlFreeParserCtxt(ctxt);
return(ret);
}
/**
* xmlParseMemory:
* @buffer: an pointer to a char array
* @size: the size of the array
*
* parse an XML in-memory block and build a tree.
*
* Returns the resulting document tree
*/
xmlDocPtr xmlParseMemory(const char *buffer, int size) {
return(xmlSAXParseMemory(NULL, buffer, size, 0));
}
/**
* xmlRecoverMemory:
* @buffer: an pointer to a char array
* @size: the size of the array
*
* parse an XML in-memory block and build a tree.
* In the case the document is not Well Formed, a tree is built anyway
*
* Returns the resulting document tree
*/
xmlDocPtr xmlRecoverMemory(const char *buffer, int size) {
return(xmlSAXParseMemory(NULL, buffer, size, 1));
}
/**
* xmlSAXUserParseMemory:
* @sax: a SAX handler
* @user_data: The user data returned on SAX callbacks
* @buffer: an in-memory XML document input
* @size: the length of the XML document in bytes
*
* A better SAX parsing routine.
* parse an XML in-memory buffer and call the given SAX handler routines.
*
* Returns 0 in case of success or a error number otherwise
*/
int xmlSAXUserParseMemory(xmlSAXHandlerPtr sax, void *user_data,
const char *buffer, int size) {
int ret = 0;
xmlParserCtxtPtr ctxt;
xmlSAXHandlerPtr oldsax = NULL;
ctxt = xmlCreateMemoryParserCtxt(buffer, size);
if (ctxt == NULL) return -1;
if (sax != NULL) {
oldsax = ctxt->sax;
ctxt->sax = sax;
}
if (user_data != NULL)
ctxt->userData = user_data;
xmlParseDocument(ctxt);
if (ctxt->wellFormed)
ret = 0;
else {
if (ctxt->errNo != 0)
ret = ctxt->errNo;
else
ret = -1;
}
if (sax != NULL) {
ctxt->sax = oldsax;
}
xmlFreeParserCtxt(ctxt);
return ret;
}
/**
* xmlCreateDocParserCtxt:
* @cur: a pointer to an array of xmlChar
*
* Creates a parser context for an XML in-memory document.
*
* Returns the new parser context or NULL
*/
xmlParserCtxtPtr
xmlCreateDocParserCtxt(xmlChar *cur) {
int len;
if (cur == NULL)
return(NULL);
len = xmlStrlen(cur);
return(xmlCreateMemoryParserCtxt((char *)cur, len));
}
/**
* xmlSAXParseDoc:
* @sax: the SAX handler block
* @cur: a pointer to an array of xmlChar
* @recovery: work in recovery mode, i.e. tries to read no Well Formed
* documents
*
* parse an XML in-memory document and build a tree.
* It use the given SAX function block to handle the parsing callback.
* If sax is NULL, fallback to the default DOM tree building routines.
*
* Returns the resulting document tree
*/
xmlDocPtr
xmlSAXParseDoc(xmlSAXHandlerPtr sax, xmlChar *cur, int recovery) {
xmlDocPtr ret;
xmlParserCtxtPtr ctxt;
if (cur == NULL) return(NULL);
ctxt = xmlCreateDocParserCtxt(cur);
if (ctxt == NULL) return(NULL);
if (sax != NULL) {
ctxt->sax = sax;
ctxt->userData = NULL;
}
xmlParseDocument(ctxt);
if ((ctxt->wellFormed) || recovery) ret = ctxt->myDoc;
else {
ret = NULL;
xmlFreeDoc(ctxt->myDoc);
ctxt->myDoc = NULL;
}
if (sax != NULL)
ctxt->sax = NULL;
xmlFreeParserCtxt(ctxt);
return(ret);
}
/**
* xmlParseDoc:
* @cur: a pointer to an array of xmlChar
*
* parse an XML in-memory document and build a tree.
*
* Returns the resulting document tree
*/
xmlDocPtr
xmlParseDoc(xmlChar *cur) {
return(xmlSAXParseDoc(NULL, cur, 0));
}
/************************************************************************
* *
* Miscellaneous *
* *
************************************************************************/
#ifdef LIBXML_XPATH_ENABLED
#include <libxml/xpath.h>
#endif
static int xmlParserInitialized = 0;
/**
* xmlInitParser:
*
* Initialization function for the XML parser.
* This is not reentrant. Call once before processing in case of
* use in multithreaded programs.
*/
void
xmlInitParser(void) {
if (xmlParserInitialized) return;
xmlInitCharEncodingHandlers();
xmlInitializePredefinedEntities();
xmlDefaultSAXHandlerInit();
xmlRegisterDefaultInputCallbacks();
xmlRegisterDefaultOutputCallbacks();
#ifdef LIBXML_HTML_ENABLED
htmlInitAutoClose();
htmlDefaultSAXHandlerInit();
#endif
#ifdef LIBXML_XPATH_ENABLED
xmlXPathInit();
#endif
xmlParserInitialized = 1;
}
/**
* xmlCleanupParser:
*
* Cleanup function for the XML parser. It tries to reclaim all
* parsing related global memory allocated for the parser processing.
* It doesn't deallocate any document related memory. Calling this
* function should not prevent reusing the parser.
*/
void
xmlCleanupParser(void) {
xmlParserInitialized = 0;
xmlCleanupCharEncodingHandlers();
xmlCleanupPredefinedEntities();
#ifdef LIBXML_CATALOG_ENABLED
xmlCatalogCleanup();
#endif
}