diff options
author | wawaka <wawaka@yandex-team.ru> | 2022-02-10 16:47:48 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:47:48 +0300 |
commit | 76cdacbb1f8f9a6a7059a9c2ec73b06431cb6fc2 (patch) | |
tree | c0748b5dcbade83af788c0abfa89c0383d6b779c /contrib/libs/libxml/pattern.c | |
parent | ed524783c88c81047033c5d6e5543db3a2251ad5 (diff) | |
download | ydb-76cdacbb1f8f9a6a7059a9c2ec73b06431cb6fc2.tar.gz |
Restoring authorship annotation for <wawaka@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/libs/libxml/pattern.c')
-rw-r--r-- | contrib/libs/libxml/pattern.c | 5220 |
1 files changed, 2610 insertions, 2610 deletions
diff --git a/contrib/libs/libxml/pattern.c b/contrib/libs/libxml/pattern.c index 3767c16ae5..27e96946b6 100644 --- a/contrib/libs/libxml/pattern.c +++ b/contrib/libs/libxml/pattern.c @@ -1,2621 +1,2621 @@ -/* +/* * pattern.c: Implementation of selectors for nodes - * - * Reference: - * http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/ - * to some extent - * http://www.w3.org/TR/1999/REC-xml-19991116 - * - * See Copyright for the status of this software. - * - * daniel@veillard.com - */ - -/* - * TODO: - * - compilation flags to check for specific syntaxes - * using flags of xmlPatterncompile() - * - making clear how pattern starting with / or . need to be handled, - * currently push(NULL, NULL) means a reset of the streaming context - * and indicating we are on / (the document node), probably need - * something similar for . - * - get rid of the "compile" starting with lowercase - * - DONE (2006-05-16): get rid of the Strdup/Strndup in case of dictionary - */ - -#define IN_LIBXML -#include "libxml.h" - -#include <string.h> -#include <libxml/xmlmemory.h> -#include <libxml/tree.h> -#include <libxml/hash.h> -#include <libxml/dict.h> -#include <libxml/xmlerror.h> -#include <libxml/parserInternals.h> -#include <libxml/pattern.h> - -#ifdef LIBXML_PATTERN_ENABLED - -/* #define DEBUG_STREAMING */ - -#ifdef ERROR -#undef ERROR -#endif -#define ERROR(a, b, c, d) -#define ERROR5(a, b, c, d, e) - -#define XML_STREAM_STEP_DESC 1 -#define XML_STREAM_STEP_FINAL 2 -#define XML_STREAM_STEP_ROOT 4 -#define XML_STREAM_STEP_ATTR 8 -#define XML_STREAM_STEP_NODE 16 -#define XML_STREAM_STEP_IN_SET 32 - -/* -* NOTE: Those private flags (XML_STREAM_xxx) are used -* in _xmlStreamCtxt->flag. They extend the public + * + * Reference: + * http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/ + * to some extent + * http://www.w3.org/TR/1999/REC-xml-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +/* + * TODO: + * - compilation flags to check for specific syntaxes + * using flags of xmlPatterncompile() + * - making clear how pattern starting with / or . need to be handled, + * currently push(NULL, NULL) means a reset of the streaming context + * and indicating we are on / (the document node), probably need + * something similar for . + * - get rid of the "compile" starting with lowercase + * - DONE (2006-05-16): get rid of the Strdup/Strndup in case of dictionary + */ + +#define IN_LIBXML +#include "libxml.h" + +#include <string.h> +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/hash.h> +#include <libxml/dict.h> +#include <libxml/xmlerror.h> +#include <libxml/parserInternals.h> +#include <libxml/pattern.h> + +#ifdef LIBXML_PATTERN_ENABLED + +/* #define DEBUG_STREAMING */ + +#ifdef ERROR +#undef ERROR +#endif +#define ERROR(a, b, c, d) +#define ERROR5(a, b, c, d, e) + +#define XML_STREAM_STEP_DESC 1 +#define XML_STREAM_STEP_FINAL 2 +#define XML_STREAM_STEP_ROOT 4 +#define XML_STREAM_STEP_ATTR 8 +#define XML_STREAM_STEP_NODE 16 +#define XML_STREAM_STEP_IN_SET 32 + +/* +* NOTE: Those private flags (XML_STREAM_xxx) are used +* in _xmlStreamCtxt->flag. They extend the public * xmlPatternFlags, so be careful not to interfere with the -* reserved values for xmlPatternFlags. -*/ -#define XML_STREAM_FINAL_IS_ANY_NODE 1<<14 -#define XML_STREAM_FROM_ROOT 1<<15 -#define XML_STREAM_DESC 1<<16 - -/* -* XML_STREAM_ANY_NODE is used for comparison against -* xmlElementType enums, to indicate a node of any type. -*/ -#define XML_STREAM_ANY_NODE 100 - -#define XML_PATTERN_NOTPATTERN (XML_PATTERN_XPATH | \ - XML_PATTERN_XSSEL | \ - XML_PATTERN_XSFIELD) - -#define XML_STREAM_XS_IDC(c) ((c)->flags & \ - (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD)) - -#define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL) - -#define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD) - -#define XML_PAT_COPY_NSNAME(c, r, nsname) \ - if ((c)->comp->dict) \ - r = (xmlChar *) xmlDictLookup((c)->comp->dict, BAD_CAST nsname, -1); \ - else r = xmlStrdup(BAD_CAST nsname); - -#define XML_PAT_FREE_STRING(c, r) if ((c)->comp->dict == NULL) xmlFree(r); - -typedef struct _xmlStreamStep xmlStreamStep; -typedef xmlStreamStep *xmlStreamStepPtr; -struct _xmlStreamStep { - int flags; /* properties of that step */ - const xmlChar *name; /* first string value if NULL accept all */ - const xmlChar *ns; /* second string value */ - int nodeType; /* type of node */ -}; - -typedef struct _xmlStreamComp xmlStreamComp; -typedef xmlStreamComp *xmlStreamCompPtr; -struct _xmlStreamComp { - xmlDict *dict; /* the dictionary if any */ - int nbStep; /* number of steps in the automata */ - int maxStep; /* allocated number of steps */ - xmlStreamStepPtr steps; /* the array of steps */ - int flags; -}; - -struct _xmlStreamCtxt { - struct _xmlStreamCtxt *next;/* link to next sub pattern if | */ - xmlStreamCompPtr comp; /* the compiled stream */ - int nbState; /* number of states in the automata */ - int maxState; /* allocated number of states */ - int level; /* how deep are we ? */ - int *states; /* the array of step indexes */ - int flags; /* validation options */ - int blockLevel; -}; - -static void xmlFreeStreamComp(xmlStreamCompPtr comp); - -/* - * Types are private: - */ - -typedef enum { - XML_OP_END=0, - XML_OP_ROOT, - XML_OP_ELEM, - XML_OP_CHILD, - XML_OP_ATTR, - XML_OP_PARENT, - XML_OP_ANCESTOR, - XML_OP_NS, - XML_OP_ALL -} xmlPatOp; - - -typedef struct _xmlStepState xmlStepState; -typedef xmlStepState *xmlStepStatePtr; -struct _xmlStepState { - int step; - xmlNodePtr node; -}; - -typedef struct _xmlStepStates xmlStepStates; -typedef xmlStepStates *xmlStepStatesPtr; -struct _xmlStepStates { - int nbstates; - int maxstates; - xmlStepStatePtr states; -}; - -typedef struct _xmlStepOp xmlStepOp; -typedef xmlStepOp *xmlStepOpPtr; -struct _xmlStepOp { - xmlPatOp op; - const xmlChar *value; - const xmlChar *value2; /* The namespace name */ -}; - -#define PAT_FROM_ROOT (1<<8) -#define PAT_FROM_CUR (1<<9) - -struct _xmlPattern { - void *data; /* the associated template */ - xmlDictPtr dict; /* the optional dictionary */ - struct _xmlPattern *next; /* next pattern if | is used */ - const xmlChar *pattern; /* the pattern */ - int flags; /* flags */ - int nbStep; - int maxStep; - xmlStepOpPtr steps; /* ops for computation */ - xmlStreamCompPtr stream; /* the streaming data if any */ -}; - -typedef struct _xmlPatParserContext xmlPatParserContext; -typedef xmlPatParserContext *xmlPatParserContextPtr; -struct _xmlPatParserContext { - const xmlChar *cur; /* the current char being parsed */ - const xmlChar *base; /* the full expression */ - int error; /* error code */ - xmlDictPtr dict; /* the dictionary if any */ - xmlPatternPtr comp; /* the result */ - xmlNodePtr elem; /* the current node if any */ - const xmlChar **namespaces; /* the namespaces definitions */ - int nb_namespaces; /* the number of namespaces */ -}; - -/************************************************************************ - * * - * Type functions * - * * - ************************************************************************/ - -/** - * xmlNewPattern: - * - * Create a new XSLT Pattern - * - * Returns the newly allocated xmlPatternPtr or NULL in case of error - */ -static xmlPatternPtr -xmlNewPattern(void) { - xmlPatternPtr cur; - - cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern)); - if (cur == NULL) { - ERROR(NULL, NULL, NULL, - "xmlNewPattern : malloc failed\n"); - return(NULL); - } - memset(cur, 0, sizeof(xmlPattern)); - cur->maxStep = 10; - cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp)); - if (cur->steps == NULL) { - xmlFree(cur); - ERROR(NULL, NULL, NULL, - "xmlNewPattern : malloc failed\n"); - return(NULL); - } - return(cur); -} - -/** - * xmlFreePattern: - * @comp: an XSLT comp - * - * Free up the memory allocated by @comp - */ -void -xmlFreePattern(xmlPatternPtr comp) { +* reserved values for xmlPatternFlags. +*/ +#define XML_STREAM_FINAL_IS_ANY_NODE 1<<14 +#define XML_STREAM_FROM_ROOT 1<<15 +#define XML_STREAM_DESC 1<<16 + +/* +* XML_STREAM_ANY_NODE is used for comparison against +* xmlElementType enums, to indicate a node of any type. +*/ +#define XML_STREAM_ANY_NODE 100 + +#define XML_PATTERN_NOTPATTERN (XML_PATTERN_XPATH | \ + XML_PATTERN_XSSEL | \ + XML_PATTERN_XSFIELD) + +#define XML_STREAM_XS_IDC(c) ((c)->flags & \ + (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD)) + +#define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL) + +#define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD) + +#define XML_PAT_COPY_NSNAME(c, r, nsname) \ + if ((c)->comp->dict) \ + r = (xmlChar *) xmlDictLookup((c)->comp->dict, BAD_CAST nsname, -1); \ + else r = xmlStrdup(BAD_CAST nsname); + +#define XML_PAT_FREE_STRING(c, r) if ((c)->comp->dict == NULL) xmlFree(r); + +typedef struct _xmlStreamStep xmlStreamStep; +typedef xmlStreamStep *xmlStreamStepPtr; +struct _xmlStreamStep { + int flags; /* properties of that step */ + const xmlChar *name; /* first string value if NULL accept all */ + const xmlChar *ns; /* second string value */ + int nodeType; /* type of node */ +}; + +typedef struct _xmlStreamComp xmlStreamComp; +typedef xmlStreamComp *xmlStreamCompPtr; +struct _xmlStreamComp { + xmlDict *dict; /* the dictionary if any */ + int nbStep; /* number of steps in the automata */ + int maxStep; /* allocated number of steps */ + xmlStreamStepPtr steps; /* the array of steps */ + int flags; +}; + +struct _xmlStreamCtxt { + struct _xmlStreamCtxt *next;/* link to next sub pattern if | */ + xmlStreamCompPtr comp; /* the compiled stream */ + int nbState; /* number of states in the automata */ + int maxState; /* allocated number of states */ + int level; /* how deep are we ? */ + int *states; /* the array of step indexes */ + int flags; /* validation options */ + int blockLevel; +}; + +static void xmlFreeStreamComp(xmlStreamCompPtr comp); + +/* + * Types are private: + */ + +typedef enum { + XML_OP_END=0, + XML_OP_ROOT, + XML_OP_ELEM, + XML_OP_CHILD, + XML_OP_ATTR, + XML_OP_PARENT, + XML_OP_ANCESTOR, + XML_OP_NS, + XML_OP_ALL +} xmlPatOp; + + +typedef struct _xmlStepState xmlStepState; +typedef xmlStepState *xmlStepStatePtr; +struct _xmlStepState { + int step; + xmlNodePtr node; +}; + +typedef struct _xmlStepStates xmlStepStates; +typedef xmlStepStates *xmlStepStatesPtr; +struct _xmlStepStates { + int nbstates; + int maxstates; + xmlStepStatePtr states; +}; + +typedef struct _xmlStepOp xmlStepOp; +typedef xmlStepOp *xmlStepOpPtr; +struct _xmlStepOp { + xmlPatOp op; + const xmlChar *value; + const xmlChar *value2; /* The namespace name */ +}; + +#define PAT_FROM_ROOT (1<<8) +#define PAT_FROM_CUR (1<<9) + +struct _xmlPattern { + void *data; /* the associated template */ + xmlDictPtr dict; /* the optional dictionary */ + struct _xmlPattern *next; /* next pattern if | is used */ + const xmlChar *pattern; /* the pattern */ + int flags; /* flags */ + int nbStep; + int maxStep; + xmlStepOpPtr steps; /* ops for computation */ + xmlStreamCompPtr stream; /* the streaming data if any */ +}; + +typedef struct _xmlPatParserContext xmlPatParserContext; +typedef xmlPatParserContext *xmlPatParserContextPtr; +struct _xmlPatParserContext { + const xmlChar *cur; /* the current char being parsed */ + const xmlChar *base; /* the full expression */ + int error; /* error code */ + xmlDictPtr dict; /* the dictionary if any */ + xmlPatternPtr comp; /* the result */ + xmlNodePtr elem; /* the current node if any */ + const xmlChar **namespaces; /* the namespaces definitions */ + int nb_namespaces; /* the number of namespaces */ +}; + +/************************************************************************ + * * + * Type functions * + * * + ************************************************************************/ + +/** + * xmlNewPattern: + * + * Create a new XSLT Pattern + * + * Returns the newly allocated xmlPatternPtr or NULL in case of error + */ +static xmlPatternPtr +xmlNewPattern(void) { + xmlPatternPtr cur; + + cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern)); + if (cur == NULL) { + ERROR(NULL, NULL, NULL, + "xmlNewPattern : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xmlPattern)); + cur->maxStep = 10; + cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp)); + if (cur->steps == NULL) { + xmlFree(cur); + ERROR(NULL, NULL, NULL, + "xmlNewPattern : malloc failed\n"); + return(NULL); + } + return(cur); +} + +/** + * xmlFreePattern: + * @comp: an XSLT comp + * + * Free up the memory allocated by @comp + */ +void +xmlFreePattern(xmlPatternPtr comp) { xmlFreePatternList(comp); } static void xmlFreePatternInternal(xmlPatternPtr comp) { - xmlStepOpPtr op; - int i; - - if (comp == NULL) - return; - if (comp->stream != NULL) - xmlFreeStreamComp(comp->stream); - if (comp->pattern != NULL) - xmlFree((xmlChar *)comp->pattern); - if (comp->steps != NULL) { - if (comp->dict == NULL) { - for (i = 0;i < comp->nbStep;i++) { - op = &comp->steps[i]; - if (op->value != NULL) - xmlFree((xmlChar *) op->value); - if (op->value2 != NULL) - xmlFree((xmlChar *) op->value2); - } - } - xmlFree(comp->steps); - } - if (comp->dict != NULL) - xmlDictFree(comp->dict); - - memset(comp, -1, sizeof(xmlPattern)); - xmlFree(comp); -} - -/** - * xmlFreePatternList: - * @comp: an XSLT comp list - * - * Free up the memory allocated by all the elements of @comp - */ -void -xmlFreePatternList(xmlPatternPtr comp) { - xmlPatternPtr cur; - - while (comp != NULL) { - cur = comp; - comp = comp->next; - cur->next = NULL; + xmlStepOpPtr op; + int i; + + if (comp == NULL) + return; + if (comp->stream != NULL) + xmlFreeStreamComp(comp->stream); + if (comp->pattern != NULL) + xmlFree((xmlChar *)comp->pattern); + if (comp->steps != NULL) { + if (comp->dict == NULL) { + for (i = 0;i < comp->nbStep;i++) { + op = &comp->steps[i]; + if (op->value != NULL) + xmlFree((xmlChar *) op->value); + if (op->value2 != NULL) + xmlFree((xmlChar *) op->value2); + } + } + xmlFree(comp->steps); + } + if (comp->dict != NULL) + xmlDictFree(comp->dict); + + memset(comp, -1, sizeof(xmlPattern)); + xmlFree(comp); +} + +/** + * xmlFreePatternList: + * @comp: an XSLT comp list + * + * Free up the memory allocated by all the elements of @comp + */ +void +xmlFreePatternList(xmlPatternPtr comp) { + xmlPatternPtr cur; + + while (comp != NULL) { + cur = comp; + comp = comp->next; + cur->next = NULL; xmlFreePatternInternal(cur); - } -} - -/** - * xmlNewPatParserContext: - * @pattern: the pattern context - * @dict: the inherited dictionary or NULL - * @namespaces: the prefix definitions, array of [URI, prefix] terminated - * with [NULL, NULL] or NULL if no namespace is used - * - * Create a new XML pattern parser context - * - * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error - */ -static xmlPatParserContextPtr -xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict, - const xmlChar **namespaces) { - xmlPatParserContextPtr cur; - - if (pattern == NULL) - return(NULL); - - cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext)); - if (cur == NULL) { - ERROR(NULL, NULL, NULL, - "xmlNewPatParserContext : malloc failed\n"); - return(NULL); - } - memset(cur, 0, sizeof(xmlPatParserContext)); - cur->dict = dict; - cur->cur = pattern; - cur->base = pattern; - if (namespaces != NULL) { - int i; - for (i = 0;namespaces[2 * i] != NULL;i++) - ; - cur->nb_namespaces = i; - } else { - cur->nb_namespaces = 0; - } - cur->namespaces = namespaces; - return(cur); -} - -/** - * xmlFreePatParserContext: - * @ctxt: an XSLT parser context - * - * Free up the memory allocated by @ctxt - */ -static void -xmlFreePatParserContext(xmlPatParserContextPtr ctxt) { - if (ctxt == NULL) - return; - memset(ctxt, -1, sizeof(xmlPatParserContext)); - xmlFree(ctxt); -} - -/** - * xmlPatternAdd: - * @comp: the compiled match expression - * @op: an op - * @value: the first value - * @value2: the second value - * - * Add a step to an XSLT Compiled Match - * - * Returns -1 in case of failure, 0 otherwise. - */ -static int -xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED, - xmlPatternPtr comp, - xmlPatOp op, xmlChar * value, xmlChar * value2) -{ - if (comp->nbStep >= comp->maxStep) { - xmlStepOpPtr temp; - temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 * - sizeof(xmlStepOp)); - if (temp == NULL) { - ERROR(ctxt, NULL, NULL, - "xmlPatternAdd: realloc failed\n"); - return (-1); - } - comp->steps = temp; - comp->maxStep *= 2; - } - comp->steps[comp->nbStep].op = op; - comp->steps[comp->nbStep].value = value; - comp->steps[comp->nbStep].value2 = value2; - comp->nbStep++; - return (0); -} - -#if 0 -/** - * xsltSwapTopPattern: - * @comp: the compiled match expression - * - * reverse the two top steps. - */ -static void -xsltSwapTopPattern(xmlPatternPtr comp) { - int i; - int j = comp->nbStep - 1; - - if (j > 0) { - register const xmlChar *tmp; - register xmlPatOp op; - i = j - 1; - tmp = comp->steps[i].value; - comp->steps[i].value = comp->steps[j].value; - comp->steps[j].value = tmp; - tmp = comp->steps[i].value2; - comp->steps[i].value2 = comp->steps[j].value2; - comp->steps[j].value2 = tmp; - op = comp->steps[i].op; - comp->steps[i].op = comp->steps[j].op; - comp->steps[j].op = op; - } -} -#endif - -/** - * xmlReversePattern: - * @comp: the compiled match expression - * - * reverse all the stack of expressions - * - * returns 0 in case of success and -1 in case of error. - */ -static int -xmlReversePattern(xmlPatternPtr comp) { - int i, j; - - /* - * remove the leading // for //a or .//a - */ - if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) { - for (i = 0, j = 1;j < comp->nbStep;i++,j++) { - comp->steps[i].value = comp->steps[j].value; - comp->steps[i].value2 = comp->steps[j].value2; - comp->steps[i].op = comp->steps[j].op; - } - comp->nbStep--; - } - if (comp->nbStep >= comp->maxStep) { - xmlStepOpPtr temp; - temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 * - sizeof(xmlStepOp)); - if (temp == NULL) { - ERROR(ctxt, NULL, NULL, - "xmlReversePattern: realloc failed\n"); - return (-1); - } - comp->steps = temp; - comp->maxStep *= 2; - } - i = 0; - j = comp->nbStep - 1; - while (j > i) { - register const xmlChar *tmp; - register xmlPatOp op; - tmp = comp->steps[i].value; - comp->steps[i].value = comp->steps[j].value; - comp->steps[j].value = tmp; - tmp = comp->steps[i].value2; - comp->steps[i].value2 = comp->steps[j].value2; - comp->steps[j].value2 = tmp; - op = comp->steps[i].op; - comp->steps[i].op = comp->steps[j].op; - comp->steps[j].op = op; - j--; - i++; - } - comp->steps[comp->nbStep].value = NULL; - comp->steps[comp->nbStep].value2 = NULL; - comp->steps[comp->nbStep++].op = XML_OP_END; - return(0); -} - -/************************************************************************ - * * - * The interpreter for the precompiled patterns * - * * - ************************************************************************/ - -static int -xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) { - if ((states->states == NULL) || (states->maxstates <= 0)) { - states->maxstates = 4; - states->nbstates = 0; - states->states = xmlMalloc(4 * sizeof(xmlStepState)); - } - else if (states->maxstates <= states->nbstates) { - xmlStepState *tmp; - - tmp = (xmlStepStatePtr) xmlRealloc(states->states, - 2 * states->maxstates * sizeof(xmlStepState)); - if (tmp == NULL) - return(-1); - states->states = tmp; - states->maxstates *= 2; - } - states->states[states->nbstates].step = step; - states->states[states->nbstates++].node = node; -#if 0 - fprintf(stderr, "Push: %d, %s\n", step, node->name); -#endif - return(0); -} - -/** - * xmlPatMatch: - * @comp: the precompiled pattern - * @node: a node - * - * Test whether the node matches the pattern - * - * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure - */ -static int -xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) { - int i; - xmlStepOpPtr step; - xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */ - - if ((comp == NULL) || (node == NULL)) return(-1); - i = 0; -restart: - for (;i < comp->nbStep;i++) { - step = &comp->steps[i]; - switch (step->op) { - case XML_OP_END: - goto found; - case XML_OP_ROOT: - if (node->type == XML_NAMESPACE_DECL) - goto rollback; - node = node->parent; - if ((node->type == XML_DOCUMENT_NODE) || -#ifdef LIBXML_DOCB_ENABLED - (node->type == XML_DOCB_DOCUMENT_NODE) || -#endif - (node->type == XML_HTML_DOCUMENT_NODE)) - continue; - goto rollback; - case XML_OP_ELEM: - if (node->type != XML_ELEMENT_NODE) - goto rollback; - if (step->value == NULL) - continue; - if (step->value[0] != node->name[0]) - goto rollback; - if (!xmlStrEqual(step->value, node->name)) - goto rollback; - - /* Namespace test */ - if (node->ns == NULL) { - if (step->value2 != NULL) - goto rollback; - } else if (node->ns->href != NULL) { - if (step->value2 == NULL) - goto rollback; - if (!xmlStrEqual(step->value2, node->ns->href)) - goto rollback; - } - continue; - case XML_OP_CHILD: { - xmlNodePtr lst; - - if ((node->type != XML_ELEMENT_NODE) && - (node->type != XML_DOCUMENT_NODE) && -#ifdef LIBXML_DOCB_ENABLED - (node->type != XML_DOCB_DOCUMENT_NODE) && -#endif - (node->type != XML_HTML_DOCUMENT_NODE)) - goto rollback; - - lst = node->children; - - if (step->value != NULL) { - while (lst != NULL) { - if ((lst->type == XML_ELEMENT_NODE) && - (step->value[0] == lst->name[0]) && - (xmlStrEqual(step->value, lst->name))) - break; - lst = lst->next; - } - if (lst != NULL) - continue; - } - goto rollback; - } - case XML_OP_ATTR: - if (node->type != XML_ATTRIBUTE_NODE) - goto rollback; - if (step->value != NULL) { - if (step->value[0] != node->name[0]) - goto rollback; - if (!xmlStrEqual(step->value, node->name)) - goto rollback; - } - /* Namespace test */ - if (node->ns == NULL) { - if (step->value2 != NULL) - goto rollback; - } else if (step->value2 != NULL) { - if (!xmlStrEqual(step->value2, node->ns->href)) - goto rollback; - } - continue; - case XML_OP_PARENT: - if ((node->type == XML_DOCUMENT_NODE) || - (node->type == XML_HTML_DOCUMENT_NODE) || -#ifdef LIBXML_DOCB_ENABLED - (node->type == XML_DOCB_DOCUMENT_NODE) || -#endif - (node->type == XML_NAMESPACE_DECL)) - goto rollback; - node = node->parent; - if (node == NULL) - goto rollback; - if (step->value == NULL) - continue; - if (step->value[0] != node->name[0]) - goto rollback; - if (!xmlStrEqual(step->value, node->name)) - goto rollback; - /* Namespace test */ - if (node->ns == NULL) { - if (step->value2 != NULL) - goto rollback; - } else if (node->ns->href != NULL) { - if (step->value2 == NULL) - goto rollback; - if (!xmlStrEqual(step->value2, node->ns->href)) - goto rollback; - } - continue; - case XML_OP_ANCESTOR: - /* TODO: implement coalescing of ANCESTOR/NODE ops */ - if (step->value == NULL) { - i++; - step = &comp->steps[i]; - if (step->op == XML_OP_ROOT) - goto found; - if (step->op != XML_OP_ELEM) - goto rollback; - if (step->value == NULL) - return(-1); - } - if (node == NULL) - goto rollback; - if ((node->type == XML_DOCUMENT_NODE) || - (node->type == XML_HTML_DOCUMENT_NODE) || -#ifdef LIBXML_DOCB_ENABLED - (node->type == XML_DOCB_DOCUMENT_NODE) || -#endif - (node->type == XML_NAMESPACE_DECL)) - goto rollback; - node = node->parent; - while (node != NULL) { - if ((node->type == XML_ELEMENT_NODE) && - (step->value[0] == node->name[0]) && - (xmlStrEqual(step->value, node->name))) { - /* Namespace test */ - if (node->ns == NULL) { - if (step->value2 == NULL) - break; - } else if (node->ns->href != NULL) { - if ((step->value2 != NULL) && - (xmlStrEqual(step->value2, node->ns->href))) - break; - } - } - node = node->parent; - } - if (node == NULL) - goto rollback; - /* - * prepare a potential rollback from here - * for ancestors of that node. - */ - if (step->op == XML_OP_ANCESTOR) - xmlPatPushState(&states, i, node); - else - xmlPatPushState(&states, i - 1, node); - continue; - case XML_OP_NS: - if (node->type != XML_ELEMENT_NODE) - goto rollback; - if (node->ns == NULL) { - if (step->value != NULL) - goto rollback; - } else if (node->ns->href != NULL) { - if (step->value == NULL) - goto rollback; - if (!xmlStrEqual(step->value, node->ns->href)) - goto rollback; - } - break; - case XML_OP_ALL: - if (node->type != XML_ELEMENT_NODE) - goto rollback; - break; - } - } -found: - if (states.states != NULL) { - /* Free the rollback states */ - xmlFree(states.states); - } - return(1); -rollback: - /* got an error try to rollback */ - if (states.states == NULL) - return(0); - if (states.nbstates <= 0) { - xmlFree(states.states); - return(0); - } - states.nbstates--; - i = states.states[states.nbstates].step; - node = states.states[states.nbstates].node; -#if 0 - fprintf(stderr, "Pop: %d, %s\n", i, node->name); -#endif - goto restart; -} - -/************************************************************************ - * * - * Dedicated parser for templates * - * * - ************************************************************************/ - -#define TODO \ - xmlGenericError(xmlGenericErrorContext, \ - "Unimplemented block at %s:%d\n", \ - __FILE__, __LINE__); -#define CUR (*ctxt->cur) -#define SKIP(val) ctxt->cur += (val) -#define NXT(val) ctxt->cur[(val)] -#define PEEKPREV(val) ctxt->cur[-(val)] -#define CUR_PTR ctxt->cur - -#define SKIP_BLANKS \ - while (IS_BLANK_CH(CUR)) NEXT - -#define CURRENT (*ctxt->cur) -#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) - - -#define PUSH(op, val, val2) \ - if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error; - -#define XSLT_ERROR(X) \ - { xsltError(ctxt, __FILE__, __LINE__, X); \ - ctxt->error = (X); return; } - -#define XSLT_ERROR0(X) \ - { xsltError(ctxt, __FILE__, __LINE__, X); \ - ctxt->error = (X); return(0); } - -#if 0 -/** - * xmlPatScanLiteral: - * @ctxt: the XPath Parser context - * + } +} + +/** + * xmlNewPatParserContext: + * @pattern: the pattern context + * @dict: the inherited dictionary or NULL + * @namespaces: the prefix definitions, array of [URI, prefix] terminated + * with [NULL, NULL] or NULL if no namespace is used + * + * Create a new XML pattern parser context + * + * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error + */ +static xmlPatParserContextPtr +xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict, + const xmlChar **namespaces) { + xmlPatParserContextPtr cur; + + if (pattern == NULL) + return(NULL); + + cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext)); + if (cur == NULL) { + ERROR(NULL, NULL, NULL, + "xmlNewPatParserContext : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xmlPatParserContext)); + cur->dict = dict; + cur->cur = pattern; + cur->base = pattern; + if (namespaces != NULL) { + int i; + for (i = 0;namespaces[2 * i] != NULL;i++) + ; + cur->nb_namespaces = i; + } else { + cur->nb_namespaces = 0; + } + cur->namespaces = namespaces; + return(cur); +} + +/** + * xmlFreePatParserContext: + * @ctxt: an XSLT parser context + * + * Free up the memory allocated by @ctxt + */ +static void +xmlFreePatParserContext(xmlPatParserContextPtr ctxt) { + if (ctxt == NULL) + return; + memset(ctxt, -1, sizeof(xmlPatParserContext)); + xmlFree(ctxt); +} + +/** + * xmlPatternAdd: + * @comp: the compiled match expression + * @op: an op + * @value: the first value + * @value2: the second value + * + * Add a step to an XSLT Compiled Match + * + * Returns -1 in case of failure, 0 otherwise. + */ +static int +xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED, + xmlPatternPtr comp, + xmlPatOp op, xmlChar * value, xmlChar * value2) +{ + if (comp->nbStep >= comp->maxStep) { + xmlStepOpPtr temp; + temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 * + sizeof(xmlStepOp)); + if (temp == NULL) { + ERROR(ctxt, NULL, NULL, + "xmlPatternAdd: realloc failed\n"); + return (-1); + } + comp->steps = temp; + comp->maxStep *= 2; + } + comp->steps[comp->nbStep].op = op; + comp->steps[comp->nbStep].value = value; + comp->steps[comp->nbStep].value2 = value2; + comp->nbStep++; + return (0); +} + +#if 0 +/** + * xsltSwapTopPattern: + * @comp: the compiled match expression + * + * reverse the two top steps. + */ +static void +xsltSwapTopPattern(xmlPatternPtr comp) { + int i; + int j = comp->nbStep - 1; + + if (j > 0) { + register const xmlChar *tmp; + register xmlPatOp op; + i = j - 1; + tmp = comp->steps[i].value; + comp->steps[i].value = comp->steps[j].value; + comp->steps[j].value = tmp; + tmp = comp->steps[i].value2; + comp->steps[i].value2 = comp->steps[j].value2; + comp->steps[j].value2 = tmp; + op = comp->steps[i].op; + comp->steps[i].op = comp->steps[j].op; + comp->steps[j].op = op; + } +} +#endif + +/** + * xmlReversePattern: + * @comp: the compiled match expression + * + * reverse all the stack of expressions + * + * returns 0 in case of success and -1 in case of error. + */ +static int +xmlReversePattern(xmlPatternPtr comp) { + int i, j; + + /* + * remove the leading // for //a or .//a + */ + if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) { + for (i = 0, j = 1;j < comp->nbStep;i++,j++) { + comp->steps[i].value = comp->steps[j].value; + comp->steps[i].value2 = comp->steps[j].value2; + comp->steps[i].op = comp->steps[j].op; + } + comp->nbStep--; + } + if (comp->nbStep >= comp->maxStep) { + xmlStepOpPtr temp; + temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 * + sizeof(xmlStepOp)); + if (temp == NULL) { + ERROR(ctxt, NULL, NULL, + "xmlReversePattern: realloc failed\n"); + return (-1); + } + comp->steps = temp; + comp->maxStep *= 2; + } + i = 0; + j = comp->nbStep - 1; + while (j > i) { + register const xmlChar *tmp; + register xmlPatOp op; + tmp = comp->steps[i].value; + comp->steps[i].value = comp->steps[j].value; + comp->steps[j].value = tmp; + tmp = comp->steps[i].value2; + comp->steps[i].value2 = comp->steps[j].value2; + comp->steps[j].value2 = tmp; + op = comp->steps[i].op; + comp->steps[i].op = comp->steps[j].op; + comp->steps[j].op = op; + j--; + i++; + } + comp->steps[comp->nbStep].value = NULL; + comp->steps[comp->nbStep].value2 = NULL; + comp->steps[comp->nbStep++].op = XML_OP_END; + return(0); +} + +/************************************************************************ + * * + * The interpreter for the precompiled patterns * + * * + ************************************************************************/ + +static int +xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) { + if ((states->states == NULL) || (states->maxstates <= 0)) { + states->maxstates = 4; + states->nbstates = 0; + states->states = xmlMalloc(4 * sizeof(xmlStepState)); + } + else if (states->maxstates <= states->nbstates) { + xmlStepState *tmp; + + tmp = (xmlStepStatePtr) xmlRealloc(states->states, + 2 * states->maxstates * sizeof(xmlStepState)); + if (tmp == NULL) + return(-1); + states->states = tmp; + states->maxstates *= 2; + } + states->states[states->nbstates].step = step; + states->states[states->nbstates++].node = node; +#if 0 + fprintf(stderr, "Push: %d, %s\n", step, node->name); +#endif + return(0); +} + +/** + * xmlPatMatch: + * @comp: the precompiled pattern + * @node: a node + * + * Test whether the node matches the pattern + * + * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure + */ +static int +xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) { + int i; + xmlStepOpPtr step; + xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */ + + if ((comp == NULL) || (node == NULL)) return(-1); + i = 0; +restart: + for (;i < comp->nbStep;i++) { + step = &comp->steps[i]; + switch (step->op) { + case XML_OP_END: + goto found; + case XML_OP_ROOT: + if (node->type == XML_NAMESPACE_DECL) + goto rollback; + node = node->parent; + if ((node->type == XML_DOCUMENT_NODE) || +#ifdef LIBXML_DOCB_ENABLED + (node->type == XML_DOCB_DOCUMENT_NODE) || +#endif + (node->type == XML_HTML_DOCUMENT_NODE)) + continue; + goto rollback; + case XML_OP_ELEM: + if (node->type != XML_ELEMENT_NODE) + goto rollback; + if (step->value == NULL) + continue; + if (step->value[0] != node->name[0]) + goto rollback; + if (!xmlStrEqual(step->value, node->name)) + goto rollback; + + /* Namespace test */ + if (node->ns == NULL) { + if (step->value2 != NULL) + goto rollback; + } else if (node->ns->href != NULL) { + if (step->value2 == NULL) + goto rollback; + if (!xmlStrEqual(step->value2, node->ns->href)) + goto rollback; + } + continue; + case XML_OP_CHILD: { + xmlNodePtr lst; + + if ((node->type != XML_ELEMENT_NODE) && + (node->type != XML_DOCUMENT_NODE) && +#ifdef LIBXML_DOCB_ENABLED + (node->type != XML_DOCB_DOCUMENT_NODE) && +#endif + (node->type != XML_HTML_DOCUMENT_NODE)) + goto rollback; + + lst = node->children; + + if (step->value != NULL) { + while (lst != NULL) { + if ((lst->type == XML_ELEMENT_NODE) && + (step->value[0] == lst->name[0]) && + (xmlStrEqual(step->value, lst->name))) + break; + lst = lst->next; + } + if (lst != NULL) + continue; + } + goto rollback; + } + case XML_OP_ATTR: + if (node->type != XML_ATTRIBUTE_NODE) + goto rollback; + if (step->value != NULL) { + if (step->value[0] != node->name[0]) + goto rollback; + if (!xmlStrEqual(step->value, node->name)) + goto rollback; + } + /* Namespace test */ + if (node->ns == NULL) { + if (step->value2 != NULL) + goto rollback; + } else if (step->value2 != NULL) { + if (!xmlStrEqual(step->value2, node->ns->href)) + goto rollback; + } + continue; + case XML_OP_PARENT: + if ((node->type == XML_DOCUMENT_NODE) || + (node->type == XML_HTML_DOCUMENT_NODE) || +#ifdef LIBXML_DOCB_ENABLED + (node->type == XML_DOCB_DOCUMENT_NODE) || +#endif + (node->type == XML_NAMESPACE_DECL)) + goto rollback; + node = node->parent; + if (node == NULL) + goto rollback; + if (step->value == NULL) + continue; + if (step->value[0] != node->name[0]) + goto rollback; + if (!xmlStrEqual(step->value, node->name)) + goto rollback; + /* Namespace test */ + if (node->ns == NULL) { + if (step->value2 != NULL) + goto rollback; + } else if (node->ns->href != NULL) { + if (step->value2 == NULL) + goto rollback; + if (!xmlStrEqual(step->value2, node->ns->href)) + goto rollback; + } + continue; + case XML_OP_ANCESTOR: + /* TODO: implement coalescing of ANCESTOR/NODE ops */ + if (step->value == NULL) { + i++; + step = &comp->steps[i]; + if (step->op == XML_OP_ROOT) + goto found; + if (step->op != XML_OP_ELEM) + goto rollback; + if (step->value == NULL) + return(-1); + } + if (node == NULL) + goto rollback; + if ((node->type == XML_DOCUMENT_NODE) || + (node->type == XML_HTML_DOCUMENT_NODE) || +#ifdef LIBXML_DOCB_ENABLED + (node->type == XML_DOCB_DOCUMENT_NODE) || +#endif + (node->type == XML_NAMESPACE_DECL)) + goto rollback; + node = node->parent; + while (node != NULL) { + if ((node->type == XML_ELEMENT_NODE) && + (step->value[0] == node->name[0]) && + (xmlStrEqual(step->value, node->name))) { + /* Namespace test */ + if (node->ns == NULL) { + if (step->value2 == NULL) + break; + } else if (node->ns->href != NULL) { + if ((step->value2 != NULL) && + (xmlStrEqual(step->value2, node->ns->href))) + break; + } + } + node = node->parent; + } + if (node == NULL) + goto rollback; + /* + * prepare a potential rollback from here + * for ancestors of that node. + */ + if (step->op == XML_OP_ANCESTOR) + xmlPatPushState(&states, i, node); + else + xmlPatPushState(&states, i - 1, node); + continue; + case XML_OP_NS: + if (node->type != XML_ELEMENT_NODE) + goto rollback; + if (node->ns == NULL) { + if (step->value != NULL) + goto rollback; + } else if (node->ns->href != NULL) { + if (step->value == NULL) + goto rollback; + if (!xmlStrEqual(step->value, node->ns->href)) + goto rollback; + } + break; + case XML_OP_ALL: + if (node->type != XML_ELEMENT_NODE) + goto rollback; + break; + } + } +found: + if (states.states != NULL) { + /* Free the rollback states */ + xmlFree(states.states); + } + return(1); +rollback: + /* got an error try to rollback */ + if (states.states == NULL) + return(0); + if (states.nbstates <= 0) { + xmlFree(states.states); + return(0); + } + states.nbstates--; + i = states.states[states.nbstates].step; + node = states.states[states.nbstates].node; +#if 0 + fprintf(stderr, "Pop: %d, %s\n", i, node->name); +#endif + goto restart; +} + +/************************************************************************ + * * + * Dedicated parser for templates * + * * + ************************************************************************/ + +#define TODO \ + xmlGenericError(xmlGenericErrorContext, \ + "Unimplemented block at %s:%d\n", \ + __FILE__, __LINE__); +#define CUR (*ctxt->cur) +#define SKIP(val) ctxt->cur += (val) +#define NXT(val) ctxt->cur[(val)] +#define PEEKPREV(val) ctxt->cur[-(val)] +#define CUR_PTR ctxt->cur + +#define SKIP_BLANKS \ + while (IS_BLANK_CH(CUR)) NEXT + +#define CURRENT (*ctxt->cur) +#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) + + +#define PUSH(op, val, val2) \ + if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error; + +#define XSLT_ERROR(X) \ + { xsltError(ctxt, __FILE__, __LINE__, X); \ + ctxt->error = (X); return; } + +#define XSLT_ERROR0(X) \ + { xsltError(ctxt, __FILE__, __LINE__, X); \ + ctxt->error = (X); return(0); } + +#if 0 +/** + * xmlPatScanLiteral: + * @ctxt: the XPath Parser context + * * Parse an XPath Literal: - * - * [29] Literal ::= '"' [^"]* '"' - * | "'" [^']* "'" - * - * Returns the Literal parsed or NULL - */ - -static xmlChar * -xmlPatScanLiteral(xmlPatParserContextPtr ctxt) { - const xmlChar *q, *cur; - xmlChar *ret = NULL; - int val, len; - - SKIP_BLANKS; - if (CUR == '"') { - NEXT; - cur = q = CUR_PTR; - val = xmlStringCurrentChar(NULL, cur, &len); - while ((IS_CHAR(val)) && (val != '"')) { - cur += len; - val = xmlStringCurrentChar(NULL, cur, &len); - } - if (!IS_CHAR(val)) { - ctxt->error = 1; - return(NULL); - } else { - if (ctxt->dict) - ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q); - else - ret = xmlStrndup(q, cur - q); - } - cur += len; - CUR_PTR = cur; - } else if (CUR == '\'') { - NEXT; - cur = q = CUR_PTR; - val = xmlStringCurrentChar(NULL, cur, &len); - while ((IS_CHAR(val)) && (val != '\'')) { - cur += len; - val = xmlStringCurrentChar(NULL, cur, &len); - } - if (!IS_CHAR(val)) { - ctxt->error = 1; - return(NULL); - } else { - if (ctxt->dict) - ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q); - else - ret = xmlStrndup(q, cur - q); - } - cur += len; - CUR_PTR = cur; - } else { - /* XP_ERROR(XPATH_START_LITERAL_ERROR); */ - ctxt->error = 1; - return(NULL); - } - return(ret); -} -#endif - -/** - * xmlPatScanName: - * @ctxt: the XPath Parser context - * - * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | - * CombiningChar | Extender - * - * [5] Name ::= (Letter | '_' | ':') (NameChar)* - * - * [6] Names ::= Name (S Name)* - * - * Returns the Name parsed or NULL - */ - -static xmlChar * -xmlPatScanName(xmlPatParserContextPtr ctxt) { - const xmlChar *q, *cur; - xmlChar *ret = NULL; - int val, len; - - SKIP_BLANKS; - - cur = q = CUR_PTR; - val = xmlStringCurrentChar(NULL, cur, &len); - if (!IS_LETTER(val) && (val != '_') && (val != ':')) - return(NULL); - - while ((IS_LETTER(val)) || (IS_DIGIT(val)) || - (val == '.') || (val == '-') || - (val == '_') || - (IS_COMBINING(val)) || - (IS_EXTENDER(val))) { - cur += len; - val = xmlStringCurrentChar(NULL, cur, &len); - } - if (ctxt->dict) - ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q); - else - ret = xmlStrndup(q, cur - q); - CUR_PTR = cur; - return(ret); -} - -/** - * xmlPatScanNCName: - * @ctxt: the XPath Parser context - * - * Parses a non qualified name - * - * Returns the Name parsed or NULL - */ - -static xmlChar * -xmlPatScanNCName(xmlPatParserContextPtr ctxt) { - const xmlChar *q, *cur; - xmlChar *ret = NULL; - int val, len; - - SKIP_BLANKS; - - cur = q = CUR_PTR; - val = xmlStringCurrentChar(NULL, cur, &len); - if (!IS_LETTER(val) && (val != '_')) - return(NULL); - - while ((IS_LETTER(val)) || (IS_DIGIT(val)) || - (val == '.') || (val == '-') || - (val == '_') || - (IS_COMBINING(val)) || - (IS_EXTENDER(val))) { - cur += len; - val = xmlStringCurrentChar(NULL, cur, &len); - } - if (ctxt->dict) - ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q); - else - ret = xmlStrndup(q, cur - q); - CUR_PTR = cur; - return(ret); -} - -#if 0 -/** - * xmlPatScanQName: - * @ctxt: the XPath Parser context - * @prefix: the place to store the prefix - * - * Parse a qualified name - * - * Returns the Name parsed or NULL - */ - -static xmlChar * -xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) { - xmlChar *ret = NULL; - - *prefix = NULL; - ret = xmlPatScanNCName(ctxt); - if (CUR == ':') { - *prefix = ret; - NEXT; - ret = xmlPatScanNCName(ctxt); - } - return(ret); -} -#endif - -/** - * xmlCompileAttributeTest: - * @ctxt: the compilation context - * - * Compile an attribute test. - */ -static void -xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) { - xmlChar *token = NULL; - xmlChar *name = NULL; - xmlChar *URL = NULL; - - SKIP_BLANKS; - name = xmlPatScanNCName(ctxt); - if (name == NULL) { - if (CUR == '*') { - PUSH(XML_OP_ATTR, NULL, NULL); - NEXT; - } else { - ERROR(NULL, NULL, NULL, - "xmlCompileAttributeTest : Name expected\n"); - ctxt->error = 1; - } - return; - } - if (CUR == ':') { - int i; - xmlChar *prefix = name; - - NEXT; - - if (IS_BLANK_CH(CUR)) { - ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL); - XML_PAT_FREE_STRING(ctxt, prefix); - ctxt->error = 1; - goto error; - } - /* - * This is a namespace match - */ - token = xmlPatScanName(ctxt); - if ((prefix[0] == 'x') && - (prefix[1] == 'm') && - (prefix[2] == 'l') && - (prefix[3] == 0)) - { - XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE); - } else { - for (i = 0;i < ctxt->nb_namespaces;i++) { - if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) { - XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i]) - break; - } - } - if (i >= ctxt->nb_namespaces) { - ERROR5(NULL, NULL, NULL, - "xmlCompileAttributeTest : no namespace bound to prefix %s\n", - prefix); + * + * [29] Literal ::= '"' [^"]* '"' + * | "'" [^']* "'" + * + * Returns the Literal parsed or NULL + */ + +static xmlChar * +xmlPatScanLiteral(xmlPatParserContextPtr ctxt) { + const xmlChar *q, *cur; + xmlChar *ret = NULL; + int val, len; + + SKIP_BLANKS; + if (CUR == '"') { + NEXT; + cur = q = CUR_PTR; + val = xmlStringCurrentChar(NULL, cur, &len); + while ((IS_CHAR(val)) && (val != '"')) { + cur += len; + val = xmlStringCurrentChar(NULL, cur, &len); + } + if (!IS_CHAR(val)) { + ctxt->error = 1; + return(NULL); + } else { + if (ctxt->dict) + ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q); + else + ret = xmlStrndup(q, cur - q); + } + cur += len; + CUR_PTR = cur; + } else if (CUR == '\'') { + NEXT; + cur = q = CUR_PTR; + val = xmlStringCurrentChar(NULL, cur, &len); + while ((IS_CHAR(val)) && (val != '\'')) { + cur += len; + val = xmlStringCurrentChar(NULL, cur, &len); + } + if (!IS_CHAR(val)) { + ctxt->error = 1; + return(NULL); + } else { + if (ctxt->dict) + ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q); + else + ret = xmlStrndup(q, cur - q); + } + cur += len; + CUR_PTR = cur; + } else { + /* XP_ERROR(XPATH_START_LITERAL_ERROR); */ + ctxt->error = 1; + return(NULL); + } + return(ret); +} +#endif + +/** + * xmlPatScanName: + * @ctxt: the XPath Parser context + * + * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | + * CombiningChar | Extender + * + * [5] Name ::= (Letter | '_' | ':') (NameChar)* + * + * [6] Names ::= Name (S Name)* + * + * Returns the Name parsed or NULL + */ + +static xmlChar * +xmlPatScanName(xmlPatParserContextPtr ctxt) { + const xmlChar *q, *cur; + xmlChar *ret = NULL; + int val, len; + + SKIP_BLANKS; + + cur = q = CUR_PTR; + val = xmlStringCurrentChar(NULL, cur, &len); + if (!IS_LETTER(val) && (val != '_') && (val != ':')) + return(NULL); + + while ((IS_LETTER(val)) || (IS_DIGIT(val)) || + (val == '.') || (val == '-') || + (val == '_') || + (IS_COMBINING(val)) || + (IS_EXTENDER(val))) { + cur += len; + val = xmlStringCurrentChar(NULL, cur, &len); + } + if (ctxt->dict) + ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q); + else + ret = xmlStrndup(q, cur - q); + CUR_PTR = cur; + return(ret); +} + +/** + * xmlPatScanNCName: + * @ctxt: the XPath Parser context + * + * Parses a non qualified name + * + * Returns the Name parsed or NULL + */ + +static xmlChar * +xmlPatScanNCName(xmlPatParserContextPtr ctxt) { + const xmlChar *q, *cur; + xmlChar *ret = NULL; + int val, len; + + SKIP_BLANKS; + + cur = q = CUR_PTR; + val = xmlStringCurrentChar(NULL, cur, &len); + if (!IS_LETTER(val) && (val != '_')) + return(NULL); + + while ((IS_LETTER(val)) || (IS_DIGIT(val)) || + (val == '.') || (val == '-') || + (val == '_') || + (IS_COMBINING(val)) || + (IS_EXTENDER(val))) { + cur += len; + val = xmlStringCurrentChar(NULL, cur, &len); + } + if (ctxt->dict) + ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q); + else + ret = xmlStrndup(q, cur - q); + CUR_PTR = cur; + return(ret); +} + +#if 0 +/** + * xmlPatScanQName: + * @ctxt: the XPath Parser context + * @prefix: the place to store the prefix + * + * Parse a qualified name + * + * Returns the Name parsed or NULL + */ + +static xmlChar * +xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) { + xmlChar *ret = NULL; + + *prefix = NULL; + ret = xmlPatScanNCName(ctxt); + if (CUR == ':') { + *prefix = ret; + NEXT; + ret = xmlPatScanNCName(ctxt); + } + return(ret); +} +#endif + +/** + * xmlCompileAttributeTest: + * @ctxt: the compilation context + * + * Compile an attribute test. + */ +static void +xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) { + xmlChar *token = NULL; + xmlChar *name = NULL; + xmlChar *URL = NULL; + + SKIP_BLANKS; + name = xmlPatScanNCName(ctxt); + if (name == NULL) { + if (CUR == '*') { + PUSH(XML_OP_ATTR, NULL, NULL); + NEXT; + } else { + ERROR(NULL, NULL, NULL, + "xmlCompileAttributeTest : Name expected\n"); + ctxt->error = 1; + } + return; + } + if (CUR == ':') { + int i; + xmlChar *prefix = name; + + NEXT; + + if (IS_BLANK_CH(CUR)) { + ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL); + XML_PAT_FREE_STRING(ctxt, prefix); + ctxt->error = 1; + goto error; + } + /* + * This is a namespace match + */ + token = xmlPatScanName(ctxt); + if ((prefix[0] == 'x') && + (prefix[1] == 'm') && + (prefix[2] == 'l') && + (prefix[3] == 0)) + { + XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE); + } else { + for (i = 0;i < ctxt->nb_namespaces;i++) { + if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) { + XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i]) + break; + } + } + if (i >= ctxt->nb_namespaces) { + ERROR5(NULL, NULL, NULL, + "xmlCompileAttributeTest : no namespace bound to prefix %s\n", + prefix); XML_PAT_FREE_STRING(ctxt, prefix); - ctxt->error = 1; - goto error; - } - } - XML_PAT_FREE_STRING(ctxt, prefix); - if (token == NULL) { - if (CUR == '*') { - NEXT; - PUSH(XML_OP_ATTR, NULL, URL); - } else { - ERROR(NULL, NULL, NULL, - "xmlCompileAttributeTest : Name expected\n"); - ctxt->error = 1; - goto error; - } - } else { - PUSH(XML_OP_ATTR, token, URL); - } - } else { - PUSH(XML_OP_ATTR, name, NULL); - } - return; -error: - if (URL != NULL) - XML_PAT_FREE_STRING(ctxt, URL) - if (token != NULL) - XML_PAT_FREE_STRING(ctxt, token); -} - -/** - * xmlCompileStepPattern: - * @ctxt: the compilation context - * - * Compile the Step Pattern and generates a precompiled - * form suitable for fast matching. - * - * [3] Step ::= '.' | NameTest - * [4] NameTest ::= QName | '*' | NCName ':' '*' - */ - -static void -xmlCompileStepPattern(xmlPatParserContextPtr ctxt) { - xmlChar *token = NULL; - xmlChar *name = NULL; - xmlChar *URL = NULL; - int hasBlanks = 0; - - SKIP_BLANKS; - if (CUR == '.') { - /* - * Context node. - */ - NEXT; - PUSH(XML_OP_ELEM, NULL, NULL); - return; - } - if (CUR == '@') { - /* - * Attribute test. - */ - if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) { - ERROR5(NULL, NULL, NULL, - "Unexpected attribute axis in '%s'.\n", ctxt->base); - ctxt->error = 1; - return; - } - NEXT; - xmlCompileAttributeTest(ctxt); - if (ctxt->error != 0) - goto error; - return; - } - name = xmlPatScanNCName(ctxt); - if (name == NULL) { - if (CUR == '*') { - NEXT; - PUSH(XML_OP_ALL, NULL, NULL); - return; - } else { - ERROR(NULL, NULL, NULL, - "xmlCompileStepPattern : Name expected\n"); - ctxt->error = 1; - return; - } - } - if (IS_BLANK_CH(CUR)) { - hasBlanks = 1; - SKIP_BLANKS; - } - if (CUR == ':') { - NEXT; - if (CUR != ':') { - xmlChar *prefix = name; - int i; - - if (hasBlanks || IS_BLANK_CH(CUR)) { - ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL); - ctxt->error = 1; - goto error; - } - /* - * This is a namespace match - */ - token = xmlPatScanName(ctxt); - if ((prefix[0] == 'x') && - (prefix[1] == 'm') && - (prefix[2] == 'l') && - (prefix[3] == 0)) - { - XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE) - } else { - for (i = 0;i < ctxt->nb_namespaces;i++) { - if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) { - XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i]) - break; - } - } - if (i >= ctxt->nb_namespaces) { - ERROR5(NULL, NULL, NULL, - "xmlCompileStepPattern : no namespace bound to prefix %s\n", - prefix); - ctxt->error = 1; - goto error; - } - } - XML_PAT_FREE_STRING(ctxt, prefix); - name = NULL; - if (token == NULL) { - if (CUR == '*') { - NEXT; - PUSH(XML_OP_NS, URL, NULL); - } else { - ERROR(NULL, NULL, NULL, - "xmlCompileStepPattern : Name expected\n"); - ctxt->error = 1; - goto error; - } - } else { - PUSH(XML_OP_ELEM, token, URL); - } - } else { - NEXT; - if (xmlStrEqual(name, (const xmlChar *) "child")) { - XML_PAT_FREE_STRING(ctxt, name); - name = xmlPatScanName(ctxt); - if (name == NULL) { - if (CUR == '*') { - NEXT; - PUSH(XML_OP_ALL, NULL, NULL); - return; - } else { - ERROR(NULL, NULL, NULL, - "xmlCompileStepPattern : QName expected\n"); - ctxt->error = 1; - goto error; - } - } - if (CUR == ':') { - xmlChar *prefix = name; - int i; - - NEXT; - if (IS_BLANK_CH(CUR)) { - ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL); - ctxt->error = 1; - goto error; - } - /* - * This is a namespace match - */ - token = xmlPatScanName(ctxt); - if ((prefix[0] == 'x') && - (prefix[1] == 'm') && - (prefix[2] == 'l') && - (prefix[3] == 0)) - { - XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE) - } else { - for (i = 0;i < ctxt->nb_namespaces;i++) { - if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) { - XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i]) - break; - } - } - if (i >= ctxt->nb_namespaces) { - ERROR5(NULL, NULL, NULL, - "xmlCompileStepPattern : no namespace bound " - "to prefix %s\n", prefix); - ctxt->error = 1; - goto error; - } - } - XML_PAT_FREE_STRING(ctxt, prefix); - name = NULL; - if (token == NULL) { - if (CUR == '*') { - NEXT; - PUSH(XML_OP_NS, URL, NULL); - } else { - ERROR(NULL, NULL, NULL, - "xmlCompileStepPattern : Name expected\n"); - ctxt->error = 1; - goto error; - } - } else { - PUSH(XML_OP_CHILD, token, URL); - } - } else - PUSH(XML_OP_CHILD, name, NULL); - return; - } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) { - XML_PAT_FREE_STRING(ctxt, name) - name = NULL; - if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) { - ERROR5(NULL, NULL, NULL, - "Unexpected attribute axis in '%s'.\n", ctxt->base); - ctxt->error = 1; - goto error; - } - xmlCompileAttributeTest(ctxt); - if (ctxt->error != 0) - goto error; - return; - } else { - ERROR5(NULL, NULL, NULL, - "The 'element' or 'attribute' axis is expected.\n", NULL); - ctxt->error = 1; - goto error; - } - } - } else if (CUR == '*') { - if (name != NULL) { - ctxt->error = 1; - goto error; - } - NEXT; - PUSH(XML_OP_ALL, token, NULL); - } else { - PUSH(XML_OP_ELEM, name, NULL); - } - return; -error: - if (URL != NULL) - XML_PAT_FREE_STRING(ctxt, URL) - if (token != NULL) - XML_PAT_FREE_STRING(ctxt, token) - if (name != NULL) - XML_PAT_FREE_STRING(ctxt, name) -} - -/** - * xmlCompilePathPattern: - * @ctxt: the compilation context - * - * Compile the Path Pattern and generates a precompiled - * form suitable for fast matching. - * - * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest ) - */ -static void -xmlCompilePathPattern(xmlPatParserContextPtr ctxt) { - SKIP_BLANKS; - if (CUR == '/') { - ctxt->comp->flags |= PAT_FROM_ROOT; - } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) { - ctxt->comp->flags |= PAT_FROM_CUR; - } - - if ((CUR == '/') && (NXT(1) == '/')) { - PUSH(XML_OP_ANCESTOR, NULL, NULL); - NEXT; - NEXT; - } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) { - PUSH(XML_OP_ANCESTOR, NULL, NULL); - NEXT; - NEXT; - NEXT; - /* Check for incompleteness. */ - SKIP_BLANKS; - if (CUR == 0) { - ERROR5(NULL, NULL, NULL, - "Incomplete expression '%s'.\n", ctxt->base); - ctxt->error = 1; - goto error; - } - } - if (CUR == '@') { - NEXT; - xmlCompileAttributeTest(ctxt); - SKIP_BLANKS; - /* TODO: check for incompleteness */ - if (CUR != 0) { - xmlCompileStepPattern(ctxt); - if (ctxt->error != 0) - goto error; - } - } else { - if (CUR == '/') { - PUSH(XML_OP_ROOT, NULL, NULL); - NEXT; - /* Check for incompleteness. */ - SKIP_BLANKS; - if (CUR == 0) { - ERROR5(NULL, NULL, NULL, - "Incomplete expression '%s'.\n", ctxt->base); - ctxt->error = 1; - goto error; - } - } - xmlCompileStepPattern(ctxt); - if (ctxt->error != 0) - goto error; - SKIP_BLANKS; - while (CUR == '/') { - if (NXT(1) == '/') { - PUSH(XML_OP_ANCESTOR, NULL, NULL); - NEXT; - NEXT; - SKIP_BLANKS; - xmlCompileStepPattern(ctxt); - if (ctxt->error != 0) - goto error; - } else { - PUSH(XML_OP_PARENT, NULL, NULL); - NEXT; - SKIP_BLANKS; - if (CUR == 0) { - ERROR5(NULL, NULL, NULL, - "Incomplete expression '%s'.\n", ctxt->base); - ctxt->error = 1; - goto error; - } - xmlCompileStepPattern(ctxt); - if (ctxt->error != 0) - goto error; - } - } - } - if (CUR != 0) { - ERROR5(NULL, NULL, NULL, - "Failed to compile pattern %s\n", ctxt->base); - ctxt->error = 1; - } -error: - return; -} - -/** - * xmlCompileIDCXPathPath: - * @ctxt: the compilation context - * - * Compile the Path Pattern and generates a precompiled - * form suitable for fast matching. - * - * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest ) - */ -static void -xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) { - SKIP_BLANKS; - if (CUR == '/') { - ERROR5(NULL, NULL, NULL, - "Unexpected selection of the document root in '%s'.\n", - ctxt->base); - goto error; - } - ctxt->comp->flags |= PAT_FROM_CUR; - - if (CUR == '.') { - /* "." - "self::node()" */ - NEXT; - SKIP_BLANKS; - if (CUR == 0) { - /* - * Selection of the context node. - */ - PUSH(XML_OP_ELEM, NULL, NULL); - return; - } - if (CUR != '/') { - /* TODO: A more meaningful error message. */ - ERROR5(NULL, NULL, NULL, - "Unexpected token after '.' in '%s'.\n", ctxt->base); - goto error; - } - /* "./" - "self::node()/" */ - NEXT; - SKIP_BLANKS; - if (CUR == '/') { - if (IS_BLANK_CH(PEEKPREV(1))) { - /* - * Disallow "./ /" - */ - ERROR5(NULL, NULL, NULL, - "Unexpected '/' token in '%s'.\n", ctxt->base); - goto error; - } - /* ".//" - "self:node()/descendant-or-self::node()/" */ - PUSH(XML_OP_ANCESTOR, NULL, NULL); - NEXT; - SKIP_BLANKS; - } - if (CUR == 0) - goto error_unfinished; - } - /* - * Process steps. - */ - do { - xmlCompileStepPattern(ctxt); - if (ctxt->error != 0) - goto error; - SKIP_BLANKS; - if (CUR != '/') - break; - PUSH(XML_OP_PARENT, NULL, NULL); - NEXT; - SKIP_BLANKS; - if (CUR == '/') { - /* - * Disallow subsequent '//'. - */ - ERROR5(NULL, NULL, NULL, - "Unexpected subsequent '//' in '%s'.\n", - ctxt->base); - goto error; - } - if (CUR == 0) - goto error_unfinished; - - } while (CUR != 0); - - if (CUR != 0) { - ERROR5(NULL, NULL, NULL, - "Failed to compile expression '%s'.\n", ctxt->base); - ctxt->error = 1; - } - return; -error: - ctxt->error = 1; - return; - -error_unfinished: - ctxt->error = 1; - ERROR5(NULL, NULL, NULL, - "Unfinished expression '%s'.\n", ctxt->base); - return; -} - -/************************************************************************ - * * - * The streaming code * - * * - ************************************************************************/ - -#ifdef DEBUG_STREAMING -static void -xmlDebugStreamComp(xmlStreamCompPtr stream) { - int i; - - if (stream == NULL) { - printf("Stream: NULL\n"); - return; - } - printf("Stream: %d steps\n", stream->nbStep); - for (i = 0;i < stream->nbStep;i++) { - if (stream->steps[i].ns != NULL) { - printf("{%s}", stream->steps[i].ns); - } - if (stream->steps[i].name == NULL) { - printf("* "); - } else { - printf("%s ", stream->steps[i].name); - } - if (stream->steps[i].flags & XML_STREAM_STEP_ROOT) - printf("root "); - if (stream->steps[i].flags & XML_STREAM_STEP_DESC) - printf("// "); - if (stream->steps[i].flags & XML_STREAM_STEP_FINAL) - printf("final "); - printf("\n"); - } -} -static void -xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) { - int i; - - if (ctxt == NULL) { - printf("Stream: NULL\n"); - return; - } - printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState); - if (match) - printf("matches\n"); - else - printf("\n"); - for (i = 0;i < ctxt->nbState;i++) { - if (ctxt->states[2 * i] < 0) - printf(" %d: free\n", i); - else { - printf(" %d: step %d, level %d", i, ctxt->states[2 * i], - ctxt->states[(2 * i) + 1]); - if (ctxt->comp->steps[ctxt->states[2 * i]].flags & - XML_STREAM_STEP_DESC) - printf(" //\n"); - else - printf("\n"); - } - } -} -#endif -/** - * xmlNewStreamComp: - * @size: the number of expected steps - * - * build a new compiled pattern for streaming - * - * Returns the new structure or NULL in case of error. - */ -static xmlStreamCompPtr -xmlNewStreamComp(int size) { - xmlStreamCompPtr cur; - - if (size < 4) - size = 4; - - cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp)); - if (cur == NULL) { - ERROR(NULL, NULL, NULL, - "xmlNewStreamComp: malloc failed\n"); - return(NULL); - } - memset(cur, 0, sizeof(xmlStreamComp)); - cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep)); - if (cur->steps == NULL) { - xmlFree(cur); - ERROR(NULL, NULL, NULL, - "xmlNewStreamComp: malloc failed\n"); - return(NULL); - } - cur->nbStep = 0; - cur->maxStep = size; - return(cur); -} - -/** - * xmlFreeStreamComp: - * @comp: the compiled pattern for streaming - * - * Free the compiled pattern for streaming - */ -static void -xmlFreeStreamComp(xmlStreamCompPtr comp) { - if (comp != NULL) { - if (comp->steps != NULL) - xmlFree(comp->steps); - if (comp->dict != NULL) - xmlDictFree(comp->dict); - xmlFree(comp); - } -} - -/** - * xmlStreamCompAddStep: - * @comp: the compiled pattern for streaming - * @name: the first string, the name, or NULL for * - * @ns: the second step, the namespace name - * @flags: the flags for that step - * - * Add a new step to the compiled pattern - * - * Returns -1 in case of error or the step index if successful - */ -static int -xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name, - const xmlChar *ns, int nodeType, int flags) { - xmlStreamStepPtr cur; - - if (comp->nbStep >= comp->maxStep) { - cur = (xmlStreamStepPtr) xmlRealloc(comp->steps, - comp->maxStep * 2 * sizeof(xmlStreamStep)); - if (cur == NULL) { - ERROR(NULL, NULL, NULL, - "xmlNewStreamComp: malloc failed\n"); - return(-1); - } - comp->steps = cur; - comp->maxStep *= 2; - } - cur = &comp->steps[comp->nbStep++]; - cur->flags = flags; - cur->name = name; - cur->ns = ns; - cur->nodeType = nodeType; - return(comp->nbStep - 1); -} - -/** - * xmlStreamCompile: - * @comp: the precompiled pattern - * - * Tries to stream compile a pattern - * - * Returns -1 in case of failure and 0 in case of success. - */ -static int -xmlStreamCompile(xmlPatternPtr comp) { - xmlStreamCompPtr stream; - int i, s = 0, root = 0, flags = 0, prevs = -1; - xmlStepOp step; - - if ((comp == NULL) || (comp->steps == NULL)) - return(-1); - /* - * special case for . - */ - if ((comp->nbStep == 1) && - (comp->steps[0].op == XML_OP_ELEM) && - (comp->steps[0].value == NULL) && - (comp->steps[0].value2 == NULL)) { - stream = xmlNewStreamComp(0); - if (stream == NULL) - return(-1); - /* Note that the stream will have no steps in this case. */ - stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE; - comp->stream = stream; - return(0); - } - - stream = xmlNewStreamComp((comp->nbStep / 2) + 1); - if (stream == NULL) - return(-1); - if (comp->dict != NULL) { - stream->dict = comp->dict; - xmlDictReference(stream->dict); - } - - i = 0; - if (comp->flags & PAT_FROM_ROOT) - stream->flags |= XML_STREAM_FROM_ROOT; - - for (;i < comp->nbStep;i++) { - step = comp->steps[i]; - switch (step.op) { - case XML_OP_END: - break; - case XML_OP_ROOT: - if (i != 0) - goto error; - root = 1; - break; - case XML_OP_NS: - s = xmlStreamCompAddStep(stream, NULL, step.value, - XML_ELEMENT_NODE, flags); - if (s < 0) - goto error; - prevs = s; - flags = 0; - break; - case XML_OP_ATTR: - flags |= XML_STREAM_STEP_ATTR; - prevs = -1; - s = xmlStreamCompAddStep(stream, - step.value, step.value2, XML_ATTRIBUTE_NODE, flags); - flags = 0; - if (s < 0) - goto error; - break; - case XML_OP_ELEM: - if ((step.value == NULL) && (step.value2 == NULL)) { - /* - * We have a "." or "self::node()" here. - * Eliminate redundant self::node() tests like in "/./." - * or "//./" - * The only case we won't eliminate is "//.", i.e. if - * self::node() is the last node test and we had - * continuation somewhere beforehand. - */ - if ((comp->nbStep == i + 1) && - (flags & XML_STREAM_STEP_DESC)) { - /* - * Mark the special case where the expression resolves - * to any type of node. - */ - if (comp->nbStep == i + 1) { - stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE; - } - flags |= XML_STREAM_STEP_NODE; - s = xmlStreamCompAddStep(stream, NULL, NULL, - XML_STREAM_ANY_NODE, flags); - if (s < 0) - goto error; - flags = 0; - /* - * If there was a previous step, mark it to be added to - * the result node-set; this is needed since only - * the last step will be marked as "final" and only - * "final" nodes are added to the resulting set. - */ - if (prevs != -1) { - stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET; - prevs = -1; - } - break; - - } else { - /* Just skip this one. */ - continue; - } - } - /* An element node. */ - s = xmlStreamCompAddStep(stream, step.value, step.value2, - XML_ELEMENT_NODE, flags); - if (s < 0) - goto error; - prevs = s; - flags = 0; - break; - case XML_OP_CHILD: - /* An element node child. */ - s = xmlStreamCompAddStep(stream, step.value, step.value2, - XML_ELEMENT_NODE, flags); - if (s < 0) - goto error; - prevs = s; - flags = 0; - break; - case XML_OP_ALL: - s = xmlStreamCompAddStep(stream, NULL, NULL, - XML_ELEMENT_NODE, flags); - if (s < 0) - goto error; - prevs = s; - flags = 0; - break; - case XML_OP_PARENT: - break; - case XML_OP_ANCESTOR: - /* Skip redundant continuations. */ - if (flags & XML_STREAM_STEP_DESC) - break; - flags |= XML_STREAM_STEP_DESC; - /* - * Mark the expression as having "//". - */ - if ((stream->flags & XML_STREAM_DESC) == 0) - stream->flags |= XML_STREAM_DESC; - break; - } - } - if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) { - /* - * If this should behave like a real pattern, we will mark - * the first step as having "//", to be reentrant on every - * tree level. - */ - if ((stream->flags & XML_STREAM_DESC) == 0) - stream->flags |= XML_STREAM_DESC; - - if (stream->nbStep > 0) { - if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0) - stream->steps[0].flags |= XML_STREAM_STEP_DESC; - } - } - if (stream->nbStep <= s) - goto error; - stream->steps[s].flags |= XML_STREAM_STEP_FINAL; - if (root) - stream->steps[0].flags |= XML_STREAM_STEP_ROOT; -#ifdef DEBUG_STREAMING - xmlDebugStreamComp(stream); -#endif - comp->stream = stream; - return(0); -error: - xmlFreeStreamComp(stream); - return(0); -} - -/** - * xmlNewStreamCtxt: - * @size: the number of expected states - * - * build a new stream context - * - * Returns the new structure or NULL in case of error. - */ -static xmlStreamCtxtPtr -xmlNewStreamCtxt(xmlStreamCompPtr stream) { - xmlStreamCtxtPtr cur; - - cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt)); - if (cur == NULL) { - ERROR(NULL, NULL, NULL, - "xmlNewStreamCtxt: malloc failed\n"); - return(NULL); - } - memset(cur, 0, sizeof(xmlStreamCtxt)); - cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int)); - if (cur->states == NULL) { - xmlFree(cur); - ERROR(NULL, NULL, NULL, - "xmlNewStreamCtxt: malloc failed\n"); - return(NULL); - } - cur->nbState = 0; - cur->maxState = 4; - cur->level = 0; - cur->comp = stream; - cur->blockLevel = -1; - return(cur); -} - -/** - * xmlFreeStreamCtxt: - * @stream: the stream context - * - * Free the stream context - */ -void -xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) { - xmlStreamCtxtPtr next; - - while (stream != NULL) { - next = stream->next; - if (stream->states != NULL) - xmlFree(stream->states); - xmlFree(stream); - stream = next; - } -} - -/** - * xmlStreamCtxtAddState: - * @comp: the stream context - * @idx: the step index for that streaming state - * - * Add a new state to the stream context - * - * Returns -1 in case of error or the state index if successful - */ -static int -xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) { - int i; - for (i = 0;i < comp->nbState;i++) { - if (comp->states[2 * i] < 0) { - comp->states[2 * i] = idx; - comp->states[2 * i + 1] = level; - return(i); - } - } - if (comp->nbState >= comp->maxState) { - int *cur; - - cur = (int *) xmlRealloc(comp->states, - comp->maxState * 4 * sizeof(int)); - if (cur == NULL) { - ERROR(NULL, NULL, NULL, - "xmlNewStreamCtxt: malloc failed\n"); - return(-1); - } - comp->states = cur; - comp->maxState *= 2; - } - comp->states[2 * comp->nbState] = idx; - comp->states[2 * comp->nbState++ + 1] = level; - return(comp->nbState - 1); -} - -/** - * xmlStreamPushInternal: - * @stream: the stream context - * @name: the current name - * @ns: the namespace name - * @nodeType: the type of the node - * - * Push new data onto the stream. NOTE: if the call xmlPatterncompile() - * indicated a dictionary, then strings for name and ns will be expected - * to come from the dictionary. - * Both @name and @ns being NULL means the / i.e. the root of the document. - * This can also act as a reset. - * - * Returns: -1 in case of error, 1 if the current state in the stream is a - * match and 0 otherwise. - */ -static int -xmlStreamPushInternal(xmlStreamCtxtPtr stream, - const xmlChar *name, const xmlChar *ns, - int nodeType) { - int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc; - xmlStreamCompPtr comp; - xmlStreamStep step; -#ifdef DEBUG_STREAMING - xmlStreamCtxtPtr orig = stream; -#endif - - if ((stream == NULL) || (stream->nbState < 0)) - return(-1); - - while (stream != NULL) { - comp = stream->comp; - - if ((nodeType == XML_ELEMENT_NODE) && - (name == NULL) && (ns == NULL)) { - /* We have a document node here (or a reset). */ - stream->nbState = 0; - stream->level = 0; - stream->blockLevel = -1; - if (comp->flags & XML_STREAM_FROM_ROOT) { - if (comp->nbStep == 0) { - /* TODO: We have a "/." here? */ - ret = 1; - } else { - if ((comp->nbStep == 1) && - (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) && - (comp->steps[0].flags & XML_STREAM_STEP_DESC)) - { - /* - * In the case of "//." the document node will match - * as well. - */ - ret = 1; - } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) { - /* TODO: Do we need this ? */ - tmp = xmlStreamCtxtAddState(stream, 0, 0); - if (tmp < 0) - err++; - } - } - } - stream = stream->next; - continue; /* while */ - } - - /* - * Fast check for ".". - */ - if (comp->nbStep == 0) { - /* - * / and . are handled at the XPath node set creation - * level by checking min depth - */ - if (stream->flags & XML_PATTERN_XPATH) { - stream = stream->next; - continue; /* while */ - } - /* - * For non-pattern like evaluation like XML Schema IDCs - * or traditional XPath expressions, this will match if - * we are at the first level only, otherwise on every level. - */ - if ((nodeType != XML_ATTRIBUTE_NODE) && - (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) || - (stream->level == 0))) { - ret = 1; - } - stream->level++; - goto stream_next; - } - if (stream->blockLevel != -1) { - /* - * Skip blocked expressions. - */ - stream->level++; - goto stream_next; - } - - if ((nodeType != XML_ELEMENT_NODE) && - (nodeType != XML_ATTRIBUTE_NODE) && - ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) { - /* - * No need to process nodes of other types if we don't - * resolve to those types. - * TODO: Do we need to block the context here? - */ - stream->level++; - goto stream_next; - } - - /* - * Check evolution of existing states - */ - i = 0; - m = stream->nbState; - while (i < m) { - if ((comp->flags & XML_STREAM_DESC) == 0) { - /* - * If there is no "//", then only the last - * added state is of interest. - */ - stepNr = stream->states[2 * (stream->nbState -1)]; - /* - * TODO: Security check, should not happen, remove it. - */ - if (stream->states[(2 * (stream->nbState -1)) + 1] < - stream->level) { - return (-1); - } - desc = 0; - /* loop-stopper */ - i = m; - } else { - /* - * If there are "//", then we need to process every "//" + ctxt->error = 1; + goto error; + } + } + XML_PAT_FREE_STRING(ctxt, prefix); + if (token == NULL) { + if (CUR == '*') { + NEXT; + PUSH(XML_OP_ATTR, NULL, URL); + } else { + ERROR(NULL, NULL, NULL, + "xmlCompileAttributeTest : Name expected\n"); + ctxt->error = 1; + goto error; + } + } else { + PUSH(XML_OP_ATTR, token, URL); + } + } else { + PUSH(XML_OP_ATTR, name, NULL); + } + return; +error: + if (URL != NULL) + XML_PAT_FREE_STRING(ctxt, URL) + if (token != NULL) + XML_PAT_FREE_STRING(ctxt, token); +} + +/** + * xmlCompileStepPattern: + * @ctxt: the compilation context + * + * Compile the Step Pattern and generates a precompiled + * form suitable for fast matching. + * + * [3] Step ::= '.' | NameTest + * [4] NameTest ::= QName | '*' | NCName ':' '*' + */ + +static void +xmlCompileStepPattern(xmlPatParserContextPtr ctxt) { + xmlChar *token = NULL; + xmlChar *name = NULL; + xmlChar *URL = NULL; + int hasBlanks = 0; + + SKIP_BLANKS; + if (CUR == '.') { + /* + * Context node. + */ + NEXT; + PUSH(XML_OP_ELEM, NULL, NULL); + return; + } + if (CUR == '@') { + /* + * Attribute test. + */ + if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) { + ERROR5(NULL, NULL, NULL, + "Unexpected attribute axis in '%s'.\n", ctxt->base); + ctxt->error = 1; + return; + } + NEXT; + xmlCompileAttributeTest(ctxt); + if (ctxt->error != 0) + goto error; + return; + } + name = xmlPatScanNCName(ctxt); + if (name == NULL) { + if (CUR == '*') { + NEXT; + PUSH(XML_OP_ALL, NULL, NULL); + return; + } else { + ERROR(NULL, NULL, NULL, + "xmlCompileStepPattern : Name expected\n"); + ctxt->error = 1; + return; + } + } + if (IS_BLANK_CH(CUR)) { + hasBlanks = 1; + SKIP_BLANKS; + } + if (CUR == ':') { + NEXT; + if (CUR != ':') { + xmlChar *prefix = name; + int i; + + if (hasBlanks || IS_BLANK_CH(CUR)) { + ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL); + ctxt->error = 1; + goto error; + } + /* + * This is a namespace match + */ + token = xmlPatScanName(ctxt); + if ((prefix[0] == 'x') && + (prefix[1] == 'm') && + (prefix[2] == 'l') && + (prefix[3] == 0)) + { + XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE) + } else { + for (i = 0;i < ctxt->nb_namespaces;i++) { + if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) { + XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i]) + break; + } + } + if (i >= ctxt->nb_namespaces) { + ERROR5(NULL, NULL, NULL, + "xmlCompileStepPattern : no namespace bound to prefix %s\n", + prefix); + ctxt->error = 1; + goto error; + } + } + XML_PAT_FREE_STRING(ctxt, prefix); + name = NULL; + if (token == NULL) { + if (CUR == '*') { + NEXT; + PUSH(XML_OP_NS, URL, NULL); + } else { + ERROR(NULL, NULL, NULL, + "xmlCompileStepPattern : Name expected\n"); + ctxt->error = 1; + goto error; + } + } else { + PUSH(XML_OP_ELEM, token, URL); + } + } else { + NEXT; + if (xmlStrEqual(name, (const xmlChar *) "child")) { + XML_PAT_FREE_STRING(ctxt, name); + name = xmlPatScanName(ctxt); + if (name == NULL) { + if (CUR == '*') { + NEXT; + PUSH(XML_OP_ALL, NULL, NULL); + return; + } else { + ERROR(NULL, NULL, NULL, + "xmlCompileStepPattern : QName expected\n"); + ctxt->error = 1; + goto error; + } + } + if (CUR == ':') { + xmlChar *prefix = name; + int i; + + NEXT; + if (IS_BLANK_CH(CUR)) { + ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL); + ctxt->error = 1; + goto error; + } + /* + * This is a namespace match + */ + token = xmlPatScanName(ctxt); + if ((prefix[0] == 'x') && + (prefix[1] == 'm') && + (prefix[2] == 'l') && + (prefix[3] == 0)) + { + XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE) + } else { + for (i = 0;i < ctxt->nb_namespaces;i++) { + if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) { + XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i]) + break; + } + } + if (i >= ctxt->nb_namespaces) { + ERROR5(NULL, NULL, NULL, + "xmlCompileStepPattern : no namespace bound " + "to prefix %s\n", prefix); + ctxt->error = 1; + goto error; + } + } + XML_PAT_FREE_STRING(ctxt, prefix); + name = NULL; + if (token == NULL) { + if (CUR == '*') { + NEXT; + PUSH(XML_OP_NS, URL, NULL); + } else { + ERROR(NULL, NULL, NULL, + "xmlCompileStepPattern : Name expected\n"); + ctxt->error = 1; + goto error; + } + } else { + PUSH(XML_OP_CHILD, token, URL); + } + } else + PUSH(XML_OP_CHILD, name, NULL); + return; + } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) { + XML_PAT_FREE_STRING(ctxt, name) + name = NULL; + if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) { + ERROR5(NULL, NULL, NULL, + "Unexpected attribute axis in '%s'.\n", ctxt->base); + ctxt->error = 1; + goto error; + } + xmlCompileAttributeTest(ctxt); + if (ctxt->error != 0) + goto error; + return; + } else { + ERROR5(NULL, NULL, NULL, + "The 'element' or 'attribute' axis is expected.\n", NULL); + ctxt->error = 1; + goto error; + } + } + } else if (CUR == '*') { + if (name != NULL) { + ctxt->error = 1; + goto error; + } + NEXT; + PUSH(XML_OP_ALL, token, NULL); + } else { + PUSH(XML_OP_ELEM, name, NULL); + } + return; +error: + if (URL != NULL) + XML_PAT_FREE_STRING(ctxt, URL) + if (token != NULL) + XML_PAT_FREE_STRING(ctxt, token) + if (name != NULL) + XML_PAT_FREE_STRING(ctxt, name) +} + +/** + * xmlCompilePathPattern: + * @ctxt: the compilation context + * + * Compile the Path Pattern and generates a precompiled + * form suitable for fast matching. + * + * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest ) + */ +static void +xmlCompilePathPattern(xmlPatParserContextPtr ctxt) { + SKIP_BLANKS; + if (CUR == '/') { + ctxt->comp->flags |= PAT_FROM_ROOT; + } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) { + ctxt->comp->flags |= PAT_FROM_CUR; + } + + if ((CUR == '/') && (NXT(1) == '/')) { + PUSH(XML_OP_ANCESTOR, NULL, NULL); + NEXT; + NEXT; + } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) { + PUSH(XML_OP_ANCESTOR, NULL, NULL); + NEXT; + NEXT; + NEXT; + /* Check for incompleteness. */ + SKIP_BLANKS; + if (CUR == 0) { + ERROR5(NULL, NULL, NULL, + "Incomplete expression '%s'.\n", ctxt->base); + ctxt->error = 1; + goto error; + } + } + if (CUR == '@') { + NEXT; + xmlCompileAttributeTest(ctxt); + SKIP_BLANKS; + /* TODO: check for incompleteness */ + if (CUR != 0) { + xmlCompileStepPattern(ctxt); + if (ctxt->error != 0) + goto error; + } + } else { + if (CUR == '/') { + PUSH(XML_OP_ROOT, NULL, NULL); + NEXT; + /* Check for incompleteness. */ + SKIP_BLANKS; + if (CUR == 0) { + ERROR5(NULL, NULL, NULL, + "Incomplete expression '%s'.\n", ctxt->base); + ctxt->error = 1; + goto error; + } + } + xmlCompileStepPattern(ctxt); + if (ctxt->error != 0) + goto error; + SKIP_BLANKS; + while (CUR == '/') { + if (NXT(1) == '/') { + PUSH(XML_OP_ANCESTOR, NULL, NULL); + NEXT; + NEXT; + SKIP_BLANKS; + xmlCompileStepPattern(ctxt); + if (ctxt->error != 0) + goto error; + } else { + PUSH(XML_OP_PARENT, NULL, NULL); + NEXT; + SKIP_BLANKS; + if (CUR == 0) { + ERROR5(NULL, NULL, NULL, + "Incomplete expression '%s'.\n", ctxt->base); + ctxt->error = 1; + goto error; + } + xmlCompileStepPattern(ctxt); + if (ctxt->error != 0) + goto error; + } + } + } + if (CUR != 0) { + ERROR5(NULL, NULL, NULL, + "Failed to compile pattern %s\n", ctxt->base); + ctxt->error = 1; + } +error: + return; +} + +/** + * xmlCompileIDCXPathPath: + * @ctxt: the compilation context + * + * Compile the Path Pattern and generates a precompiled + * form suitable for fast matching. + * + * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest ) + */ +static void +xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) { + SKIP_BLANKS; + if (CUR == '/') { + ERROR5(NULL, NULL, NULL, + "Unexpected selection of the document root in '%s'.\n", + ctxt->base); + goto error; + } + ctxt->comp->flags |= PAT_FROM_CUR; + + if (CUR == '.') { + /* "." - "self::node()" */ + NEXT; + SKIP_BLANKS; + if (CUR == 0) { + /* + * Selection of the context node. + */ + PUSH(XML_OP_ELEM, NULL, NULL); + return; + } + if (CUR != '/') { + /* TODO: A more meaningful error message. */ + ERROR5(NULL, NULL, NULL, + "Unexpected token after '.' in '%s'.\n", ctxt->base); + goto error; + } + /* "./" - "self::node()/" */ + NEXT; + SKIP_BLANKS; + if (CUR == '/') { + if (IS_BLANK_CH(PEEKPREV(1))) { + /* + * Disallow "./ /" + */ + ERROR5(NULL, NULL, NULL, + "Unexpected '/' token in '%s'.\n", ctxt->base); + goto error; + } + /* ".//" - "self:node()/descendant-or-self::node()/" */ + PUSH(XML_OP_ANCESTOR, NULL, NULL); + NEXT; + SKIP_BLANKS; + } + if (CUR == 0) + goto error_unfinished; + } + /* + * Process steps. + */ + do { + xmlCompileStepPattern(ctxt); + if (ctxt->error != 0) + goto error; + SKIP_BLANKS; + if (CUR != '/') + break; + PUSH(XML_OP_PARENT, NULL, NULL); + NEXT; + SKIP_BLANKS; + if (CUR == '/') { + /* + * Disallow subsequent '//'. + */ + ERROR5(NULL, NULL, NULL, + "Unexpected subsequent '//' in '%s'.\n", + ctxt->base); + goto error; + } + if (CUR == 0) + goto error_unfinished; + + } while (CUR != 0); + + if (CUR != 0) { + ERROR5(NULL, NULL, NULL, + "Failed to compile expression '%s'.\n", ctxt->base); + ctxt->error = 1; + } + return; +error: + ctxt->error = 1; + return; + +error_unfinished: + ctxt->error = 1; + ERROR5(NULL, NULL, NULL, + "Unfinished expression '%s'.\n", ctxt->base); + return; +} + +/************************************************************************ + * * + * The streaming code * + * * + ************************************************************************/ + +#ifdef DEBUG_STREAMING +static void +xmlDebugStreamComp(xmlStreamCompPtr stream) { + int i; + + if (stream == NULL) { + printf("Stream: NULL\n"); + return; + } + printf("Stream: %d steps\n", stream->nbStep); + for (i = 0;i < stream->nbStep;i++) { + if (stream->steps[i].ns != NULL) { + printf("{%s}", stream->steps[i].ns); + } + if (stream->steps[i].name == NULL) { + printf("* "); + } else { + printf("%s ", stream->steps[i].name); + } + if (stream->steps[i].flags & XML_STREAM_STEP_ROOT) + printf("root "); + if (stream->steps[i].flags & XML_STREAM_STEP_DESC) + printf("// "); + if (stream->steps[i].flags & XML_STREAM_STEP_FINAL) + printf("final "); + printf("\n"); + } +} +static void +xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) { + int i; + + if (ctxt == NULL) { + printf("Stream: NULL\n"); + return; + } + printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState); + if (match) + printf("matches\n"); + else + printf("\n"); + for (i = 0;i < ctxt->nbState;i++) { + if (ctxt->states[2 * i] < 0) + printf(" %d: free\n", i); + else { + printf(" %d: step %d, level %d", i, ctxt->states[2 * i], + ctxt->states[(2 * i) + 1]); + if (ctxt->comp->steps[ctxt->states[2 * i]].flags & + XML_STREAM_STEP_DESC) + printf(" //\n"); + else + printf("\n"); + } + } +} +#endif +/** + * xmlNewStreamComp: + * @size: the number of expected steps + * + * build a new compiled pattern for streaming + * + * Returns the new structure or NULL in case of error. + */ +static xmlStreamCompPtr +xmlNewStreamComp(int size) { + xmlStreamCompPtr cur; + + if (size < 4) + size = 4; + + cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp)); + if (cur == NULL) { + ERROR(NULL, NULL, NULL, + "xmlNewStreamComp: malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xmlStreamComp)); + cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep)); + if (cur->steps == NULL) { + xmlFree(cur); + ERROR(NULL, NULL, NULL, + "xmlNewStreamComp: malloc failed\n"); + return(NULL); + } + cur->nbStep = 0; + cur->maxStep = size; + return(cur); +} + +/** + * xmlFreeStreamComp: + * @comp: the compiled pattern for streaming + * + * Free the compiled pattern for streaming + */ +static void +xmlFreeStreamComp(xmlStreamCompPtr comp) { + if (comp != NULL) { + if (comp->steps != NULL) + xmlFree(comp->steps); + if (comp->dict != NULL) + xmlDictFree(comp->dict); + xmlFree(comp); + } +} + +/** + * xmlStreamCompAddStep: + * @comp: the compiled pattern for streaming + * @name: the first string, the name, or NULL for * + * @ns: the second step, the namespace name + * @flags: the flags for that step + * + * Add a new step to the compiled pattern + * + * Returns -1 in case of error or the step index if successful + */ +static int +xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name, + const xmlChar *ns, int nodeType, int flags) { + xmlStreamStepPtr cur; + + if (comp->nbStep >= comp->maxStep) { + cur = (xmlStreamStepPtr) xmlRealloc(comp->steps, + comp->maxStep * 2 * sizeof(xmlStreamStep)); + if (cur == NULL) { + ERROR(NULL, NULL, NULL, + "xmlNewStreamComp: malloc failed\n"); + return(-1); + } + comp->steps = cur; + comp->maxStep *= 2; + } + cur = &comp->steps[comp->nbStep++]; + cur->flags = flags; + cur->name = name; + cur->ns = ns; + cur->nodeType = nodeType; + return(comp->nbStep - 1); +} + +/** + * xmlStreamCompile: + * @comp: the precompiled pattern + * + * Tries to stream compile a pattern + * + * Returns -1 in case of failure and 0 in case of success. + */ +static int +xmlStreamCompile(xmlPatternPtr comp) { + xmlStreamCompPtr stream; + int i, s = 0, root = 0, flags = 0, prevs = -1; + xmlStepOp step; + + if ((comp == NULL) || (comp->steps == NULL)) + return(-1); + /* + * special case for . + */ + if ((comp->nbStep == 1) && + (comp->steps[0].op == XML_OP_ELEM) && + (comp->steps[0].value == NULL) && + (comp->steps[0].value2 == NULL)) { + stream = xmlNewStreamComp(0); + if (stream == NULL) + return(-1); + /* Note that the stream will have no steps in this case. */ + stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE; + comp->stream = stream; + return(0); + } + + stream = xmlNewStreamComp((comp->nbStep / 2) + 1); + if (stream == NULL) + return(-1); + if (comp->dict != NULL) { + stream->dict = comp->dict; + xmlDictReference(stream->dict); + } + + i = 0; + if (comp->flags & PAT_FROM_ROOT) + stream->flags |= XML_STREAM_FROM_ROOT; + + for (;i < comp->nbStep;i++) { + step = comp->steps[i]; + switch (step.op) { + case XML_OP_END: + break; + case XML_OP_ROOT: + if (i != 0) + goto error; + root = 1; + break; + case XML_OP_NS: + s = xmlStreamCompAddStep(stream, NULL, step.value, + XML_ELEMENT_NODE, flags); + if (s < 0) + goto error; + prevs = s; + flags = 0; + break; + case XML_OP_ATTR: + flags |= XML_STREAM_STEP_ATTR; + prevs = -1; + s = xmlStreamCompAddStep(stream, + step.value, step.value2, XML_ATTRIBUTE_NODE, flags); + flags = 0; + if (s < 0) + goto error; + break; + case XML_OP_ELEM: + if ((step.value == NULL) && (step.value2 == NULL)) { + /* + * We have a "." or "self::node()" here. + * Eliminate redundant self::node() tests like in "/./." + * or "//./" + * The only case we won't eliminate is "//.", i.e. if + * self::node() is the last node test and we had + * continuation somewhere beforehand. + */ + if ((comp->nbStep == i + 1) && + (flags & XML_STREAM_STEP_DESC)) { + /* + * Mark the special case where the expression resolves + * to any type of node. + */ + if (comp->nbStep == i + 1) { + stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE; + } + flags |= XML_STREAM_STEP_NODE; + s = xmlStreamCompAddStep(stream, NULL, NULL, + XML_STREAM_ANY_NODE, flags); + if (s < 0) + goto error; + flags = 0; + /* + * If there was a previous step, mark it to be added to + * the result node-set; this is needed since only + * the last step will be marked as "final" and only + * "final" nodes are added to the resulting set. + */ + if (prevs != -1) { + stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET; + prevs = -1; + } + break; + + } else { + /* Just skip this one. */ + continue; + } + } + /* An element node. */ + s = xmlStreamCompAddStep(stream, step.value, step.value2, + XML_ELEMENT_NODE, flags); + if (s < 0) + goto error; + prevs = s; + flags = 0; + break; + case XML_OP_CHILD: + /* An element node child. */ + s = xmlStreamCompAddStep(stream, step.value, step.value2, + XML_ELEMENT_NODE, flags); + if (s < 0) + goto error; + prevs = s; + flags = 0; + break; + case XML_OP_ALL: + s = xmlStreamCompAddStep(stream, NULL, NULL, + XML_ELEMENT_NODE, flags); + if (s < 0) + goto error; + prevs = s; + flags = 0; + break; + case XML_OP_PARENT: + break; + case XML_OP_ANCESTOR: + /* Skip redundant continuations. */ + if (flags & XML_STREAM_STEP_DESC) + break; + flags |= XML_STREAM_STEP_DESC; + /* + * Mark the expression as having "//". + */ + if ((stream->flags & XML_STREAM_DESC) == 0) + stream->flags |= XML_STREAM_DESC; + break; + } + } + if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) { + /* + * If this should behave like a real pattern, we will mark + * the first step as having "//", to be reentrant on every + * tree level. + */ + if ((stream->flags & XML_STREAM_DESC) == 0) + stream->flags |= XML_STREAM_DESC; + + if (stream->nbStep > 0) { + if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0) + stream->steps[0].flags |= XML_STREAM_STEP_DESC; + } + } + if (stream->nbStep <= s) + goto error; + stream->steps[s].flags |= XML_STREAM_STEP_FINAL; + if (root) + stream->steps[0].flags |= XML_STREAM_STEP_ROOT; +#ifdef DEBUG_STREAMING + xmlDebugStreamComp(stream); +#endif + comp->stream = stream; + return(0); +error: + xmlFreeStreamComp(stream); + return(0); +} + +/** + * xmlNewStreamCtxt: + * @size: the number of expected states + * + * build a new stream context + * + * Returns the new structure or NULL in case of error. + */ +static xmlStreamCtxtPtr +xmlNewStreamCtxt(xmlStreamCompPtr stream) { + xmlStreamCtxtPtr cur; + + cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt)); + if (cur == NULL) { + ERROR(NULL, NULL, NULL, + "xmlNewStreamCtxt: malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xmlStreamCtxt)); + cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int)); + if (cur->states == NULL) { + xmlFree(cur); + ERROR(NULL, NULL, NULL, + "xmlNewStreamCtxt: malloc failed\n"); + return(NULL); + } + cur->nbState = 0; + cur->maxState = 4; + cur->level = 0; + cur->comp = stream; + cur->blockLevel = -1; + return(cur); +} + +/** + * xmlFreeStreamCtxt: + * @stream: the stream context + * + * Free the stream context + */ +void +xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) { + xmlStreamCtxtPtr next; + + while (stream != NULL) { + next = stream->next; + if (stream->states != NULL) + xmlFree(stream->states); + xmlFree(stream); + stream = next; + } +} + +/** + * xmlStreamCtxtAddState: + * @comp: the stream context + * @idx: the step index for that streaming state + * + * Add a new state to the stream context + * + * Returns -1 in case of error or the state index if successful + */ +static int +xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) { + int i; + for (i = 0;i < comp->nbState;i++) { + if (comp->states[2 * i] < 0) { + comp->states[2 * i] = idx; + comp->states[2 * i + 1] = level; + return(i); + } + } + if (comp->nbState >= comp->maxState) { + int *cur; + + cur = (int *) xmlRealloc(comp->states, + comp->maxState * 4 * sizeof(int)); + if (cur == NULL) { + ERROR(NULL, NULL, NULL, + "xmlNewStreamCtxt: malloc failed\n"); + return(-1); + } + comp->states = cur; + comp->maxState *= 2; + } + comp->states[2 * comp->nbState] = idx; + comp->states[2 * comp->nbState++ + 1] = level; + return(comp->nbState - 1); +} + +/** + * xmlStreamPushInternal: + * @stream: the stream context + * @name: the current name + * @ns: the namespace name + * @nodeType: the type of the node + * + * Push new data onto the stream. NOTE: if the call xmlPatterncompile() + * indicated a dictionary, then strings for name and ns will be expected + * to come from the dictionary. + * Both @name and @ns being NULL means the / i.e. the root of the document. + * This can also act as a reset. + * + * Returns: -1 in case of error, 1 if the current state in the stream is a + * match and 0 otherwise. + */ +static int +xmlStreamPushInternal(xmlStreamCtxtPtr stream, + const xmlChar *name, const xmlChar *ns, + int nodeType) { + int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc; + xmlStreamCompPtr comp; + xmlStreamStep step; +#ifdef DEBUG_STREAMING + xmlStreamCtxtPtr orig = stream; +#endif + + if ((stream == NULL) || (stream->nbState < 0)) + return(-1); + + while (stream != NULL) { + comp = stream->comp; + + if ((nodeType == XML_ELEMENT_NODE) && + (name == NULL) && (ns == NULL)) { + /* We have a document node here (or a reset). */ + stream->nbState = 0; + stream->level = 0; + stream->blockLevel = -1; + if (comp->flags & XML_STREAM_FROM_ROOT) { + if (comp->nbStep == 0) { + /* TODO: We have a "/." here? */ + ret = 1; + } else { + if ((comp->nbStep == 1) && + (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) && + (comp->steps[0].flags & XML_STREAM_STEP_DESC)) + { + /* + * In the case of "//." the document node will match + * as well. + */ + ret = 1; + } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) { + /* TODO: Do we need this ? */ + tmp = xmlStreamCtxtAddState(stream, 0, 0); + if (tmp < 0) + err++; + } + } + } + stream = stream->next; + continue; /* while */ + } + + /* + * Fast check for ".". + */ + if (comp->nbStep == 0) { + /* + * / and . are handled at the XPath node set creation + * level by checking min depth + */ + if (stream->flags & XML_PATTERN_XPATH) { + stream = stream->next; + continue; /* while */ + } + /* + * For non-pattern like evaluation like XML Schema IDCs + * or traditional XPath expressions, this will match if + * we are at the first level only, otherwise on every level. + */ + if ((nodeType != XML_ATTRIBUTE_NODE) && + (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) || + (stream->level == 0))) { + ret = 1; + } + stream->level++; + goto stream_next; + } + if (stream->blockLevel != -1) { + /* + * Skip blocked expressions. + */ + stream->level++; + goto stream_next; + } + + if ((nodeType != XML_ELEMENT_NODE) && + (nodeType != XML_ATTRIBUTE_NODE) && + ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) { + /* + * No need to process nodes of other types if we don't + * resolve to those types. + * TODO: Do we need to block the context here? + */ + stream->level++; + goto stream_next; + } + + /* + * Check evolution of existing states + */ + i = 0; + m = stream->nbState; + while (i < m) { + if ((comp->flags & XML_STREAM_DESC) == 0) { + /* + * If there is no "//", then only the last + * added state is of interest. + */ + stepNr = stream->states[2 * (stream->nbState -1)]; + /* + * TODO: Security check, should not happen, remove it. + */ + if (stream->states[(2 * (stream->nbState -1)) + 1] < + stream->level) { + return (-1); + } + desc = 0; + /* loop-stopper */ + i = m; + } else { + /* + * If there are "//", then we need to process every "//" * occurring in the states, plus any other state for this - * level. - */ - stepNr = stream->states[2 * i]; - - /* TODO: should not happen anymore: dead states */ - if (stepNr < 0) - goto next_state; - - tmp = stream->states[(2 * i) + 1]; - - /* skip new states just added */ - if (tmp > stream->level) - goto next_state; - - /* skip states at ancestor levels, except if "//" */ - desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC; - if ((tmp < stream->level) && (!desc)) - goto next_state; - } - /* - * Check for correct node-type. - */ - step = comp->steps[stepNr]; - if (step.nodeType != nodeType) { - if (step.nodeType == XML_ATTRIBUTE_NODE) { - /* - * Block this expression for deeper evaluation. - */ - if ((comp->flags & XML_STREAM_DESC) == 0) - stream->blockLevel = stream->level +1; - goto next_state; - } else if (step.nodeType != XML_STREAM_ANY_NODE) - goto next_state; - } - /* - * Compare local/namespace-name. - */ - match = 0; - if (step.nodeType == XML_STREAM_ANY_NODE) { - match = 1; - } else if (step.name == NULL) { - if (step.ns == NULL) { - /* - * This lets through all elements/attributes. - */ - match = 1; - } else if (ns != NULL) - match = xmlStrEqual(step.ns, ns); - } else if (((step.ns != NULL) == (ns != NULL)) && - (name != NULL) && - (step.name[0] == name[0]) && - xmlStrEqual(step.name, name) && - ((step.ns == ns) || xmlStrEqual(step.ns, ns))) - { - match = 1; - } -#if 0 -/* -* TODO: Pointer comparison won't work, since not guaranteed that the given -* values are in the same dict; especially if it's the namespace name, -* normally coming from ns->href. We need a namespace dict mechanism ! -*/ - } else if (comp->dict) { - if (step.name == NULL) { - if (step.ns == NULL) - match = 1; - else - match = (step.ns == ns); - } else { - match = ((step.name == name) && (step.ns == ns)); - } -#endif /* if 0 ------------------------------------------------------- */ - if (match) { - final = step.flags & XML_STREAM_STEP_FINAL; - if (desc) { - if (final) { - ret = 1; - } else { - /* descending match create a new state */ - xmlStreamCtxtAddState(stream, stepNr + 1, - stream->level + 1); - } - } else { - if (final) { - ret = 1; - } else { - xmlStreamCtxtAddState(stream, stepNr + 1, - stream->level + 1); - } - } - if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) { - /* - * Check if we have a special case like "foo/bar//.", where - * "foo" is selected as well. - */ - ret = 1; - } - } - if (((comp->flags & XML_STREAM_DESC) == 0) && - ((! match) || final)) { - /* - * Mark this expression as blocked for any evaluation at - * deeper levels. Note that this includes "/foo" - * expressions if the *pattern* behaviour is used. - */ - stream->blockLevel = stream->level +1; - } -next_state: - i++; - } - - stream->level++; - - /* - * Re/enter the expression. - * Don't reenter if it's an absolute expression like "/foo", - * except "//foo". - */ - step = comp->steps[0]; - if (step.flags & XML_STREAM_STEP_ROOT) - goto stream_next; - - desc = step.flags & XML_STREAM_STEP_DESC; - if (stream->flags & XML_PATTERN_NOTPATTERN) { - /* - * Re/enter the expression if it is a "descendant" one, - * or if we are at the 1st level of evaluation. - */ - - if (stream->level == 1) { - if (XML_STREAM_XS_IDC(stream)) { - /* - * XS-IDC: The missing "self::node()" will always - * match the first given node. - */ - goto stream_next; - } else - goto compare; - } - /* - * A "//" is always reentrant. - */ - if (desc) - goto compare; - - /* - * XS-IDC: Process the 2nd level, since the missing - * "self::node()" is responsible for the 2nd level being - * the real start level. - */ - if ((stream->level == 2) && XML_STREAM_XS_IDC(stream)) - goto compare; - - goto stream_next; - } - -compare: - /* - * Check expected node-type. - */ - if (step.nodeType != nodeType) { - if (nodeType == XML_ATTRIBUTE_NODE) - goto stream_next; - else if (step.nodeType != XML_STREAM_ANY_NODE) - goto stream_next; - } - /* - * Compare local/namespace-name. - */ - match = 0; - if (step.nodeType == XML_STREAM_ANY_NODE) { - match = 1; - } else if (step.name == NULL) { - if (step.ns == NULL) { - /* - * This lets through all elements/attributes. - */ - match = 1; - } else if (ns != NULL) - match = xmlStrEqual(step.ns, ns); - } else if (((step.ns != NULL) == (ns != NULL)) && - (name != NULL) && - (step.name[0] == name[0]) && - xmlStrEqual(step.name, name) && - ((step.ns == ns) || xmlStrEqual(step.ns, ns))) - { - match = 1; - } - final = step.flags & XML_STREAM_STEP_FINAL; - if (match) { - if (final) - ret = 1; - else - xmlStreamCtxtAddState(stream, 1, stream->level); - if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) { - /* - * Check if we have a special case like "foo//.", where - * "foo" is selected as well. - */ - ret = 1; - } - } - if (((comp->flags & XML_STREAM_DESC) == 0) && - ((! match) || final)) { - /* - * Mark this expression as blocked for any evaluation at - * deeper levels. - */ - stream->blockLevel = stream->level; - } - -stream_next: - stream = stream->next; - } /* while stream != NULL */ - - if (err > 0) - ret = -1; -#ifdef DEBUG_STREAMING - xmlDebugStreamCtxt(orig, ret); -#endif - return(ret); -} - -/** - * xmlStreamPush: - * @stream: the stream context - * @name: the current name - * @ns: the namespace name - * - * Push new data onto the stream. NOTE: if the call xmlPatterncompile() - * indicated a dictionary, then strings for name and ns will be expected - * to come from the dictionary. - * Both @name and @ns being NULL means the / i.e. the root of the document. - * This can also act as a reset. - * Otherwise the function will act as if it has been given an element-node. - * - * Returns: -1 in case of error, 1 if the current state in the stream is a - * match and 0 otherwise. - */ -int -xmlStreamPush(xmlStreamCtxtPtr stream, - const xmlChar *name, const xmlChar *ns) { - return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE)); -} - -/** - * xmlStreamPushNode: - * @stream: the stream context - * @name: the current name - * @ns: the namespace name - * @nodeType: the type of the node being pushed - * - * Push new data onto the stream. NOTE: if the call xmlPatterncompile() - * indicated a dictionary, then strings for name and ns will be expected - * to come from the dictionary. - * Both @name and @ns being NULL means the / i.e. the root of the document. - * This can also act as a reset. - * Different from xmlStreamPush() this function can be fed with nodes of type: - * element-, attribute-, text-, cdata-section-, comment- and - * processing-instruction-node. - * - * Returns: -1 in case of error, 1 if the current state in the stream is a - * match and 0 otherwise. - */ -int -xmlStreamPushNode(xmlStreamCtxtPtr stream, - const xmlChar *name, const xmlChar *ns, - int nodeType) -{ - return (xmlStreamPushInternal(stream, name, ns, - nodeType)); -} - -/** -* xmlStreamPushAttr: -* @stream: the stream context -* @name: the current name -* @ns: the namespace name -* -* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile() -* indicated a dictionary, then strings for name and ns will be expected -* to come from the dictionary. -* Both @name and @ns being NULL means the / i.e. the root of the document. -* This can also act as a reset. -* Otherwise the function will act as if it has been given an attribute-node. -* -* Returns: -1 in case of error, 1 if the current state in the stream is a -* match and 0 otherwise. -*/ -int -xmlStreamPushAttr(xmlStreamCtxtPtr stream, - const xmlChar *name, const xmlChar *ns) { - return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE)); -} - -/** - * xmlStreamPop: - * @stream: the stream context - * - * push one level from the stream. - * - * Returns: -1 in case of error, 0 otherwise. - */ -int -xmlStreamPop(xmlStreamCtxtPtr stream) { - int i, lev; - - if (stream == NULL) - return(-1); - while (stream != NULL) { - /* - * Reset block-level. - */ - if (stream->blockLevel == stream->level) - stream->blockLevel = -1; - - /* - * stream->level can be zero when XML_FINAL_IS_ANY_NODE is set - * (see the thread at - * http://mail.gnome.org/archives/xslt/2008-July/msg00027.html) - */ - if (stream->level) - stream->level--; - /* - * Check evolution of existing states - */ - for (i = stream->nbState -1; i >= 0; i--) { - /* discard obsoleted states */ - lev = stream->states[(2 * i) + 1]; - if (lev > stream->level) - stream->nbState--; - if (lev <= stream->level) - break; - } - stream = stream->next; - } - return(0); -} - -/** - * xmlStreamWantsAnyNode: - * @streamCtxt: the stream context - * - * Query if the streaming pattern additionally needs to be fed with - * text-, cdata-section-, comment- and processing-instruction-nodes. - * If the result is 0 then only element-nodes and attribute-nodes - * need to be pushed. - * - * Returns: 1 in case of need of nodes of the above described types, - * 0 otherwise. -1 on API errors. - */ -int -xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt) -{ - if (streamCtxt == NULL) - return(-1); - while (streamCtxt != NULL) { - if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) - return(1); - streamCtxt = streamCtxt->next; - } - return(0); -} - -/************************************************************************ - * * - * The public interfaces * - * * - ************************************************************************/ - -/** - * xmlPatterncompile: - * @pattern: the pattern to compile - * @dict: an optional dictionary for interned strings - * @flags: compilation flags, see xmlPatternFlags - * @namespaces: the prefix definitions, array of [URI, prefix] or NULL - * - * Compile a pattern. - * - * Returns the compiled form of the pattern or NULL in case of error - */ -xmlPatternPtr -xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags, - const xmlChar **namespaces) { - xmlPatternPtr ret = NULL, cur; - xmlPatParserContextPtr ctxt = NULL; - const xmlChar *or, *start; - xmlChar *tmp = NULL; - int type = 0; - int streamable = 1; - - if (pattern == NULL) - return(NULL); - - start = pattern; - or = start; - while (*or != 0) { - tmp = NULL; - while ((*or != 0) && (*or != '|')) or++; - if (*or == 0) - ctxt = xmlNewPatParserContext(start, dict, namespaces); - else { - tmp = xmlStrndup(start, or - start); - if (tmp != NULL) { - ctxt = xmlNewPatParserContext(tmp, dict, namespaces); - } - or++; - } - if (ctxt == NULL) goto error; - cur = xmlNewPattern(); - if (cur == NULL) goto error; - /* - * Assign string dict. - */ - if (dict) { - cur->dict = dict; - xmlDictReference(dict); - } - if (ret == NULL) - ret = cur; - else { - cur->next = ret->next; - ret->next = cur; - } - cur->flags = flags; - ctxt->comp = cur; - - if (XML_STREAM_XS_IDC(cur)) - xmlCompileIDCXPathPath(ctxt); - else - xmlCompilePathPattern(ctxt); - if (ctxt->error != 0) - goto error; - xmlFreePatParserContext(ctxt); - ctxt = NULL; - - - if (streamable) { - if (type == 0) { - type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR); - } else if (type == PAT_FROM_ROOT) { - if (cur->flags & PAT_FROM_CUR) - streamable = 0; - } else if (type == PAT_FROM_CUR) { - if (cur->flags & PAT_FROM_ROOT) - streamable = 0; - } - } - if (streamable) - xmlStreamCompile(cur); - if (xmlReversePattern(cur) < 0) - goto error; - if (tmp != NULL) { - xmlFree(tmp); - tmp = NULL; - } - start = or; - } - if (streamable == 0) { - cur = ret; - while (cur != NULL) { - if (cur->stream != NULL) { - xmlFreeStreamComp(cur->stream); - cur->stream = NULL; - } - cur = cur->next; - } - } - - return(ret); -error: - if (ctxt != NULL) xmlFreePatParserContext(ctxt); - if (ret != NULL) xmlFreePattern(ret); - if (tmp != NULL) xmlFree(tmp); - return(NULL); -} - -/** - * xmlPatternMatch: - * @comp: the precompiled pattern - * @node: a node - * - * Test whether the node matches the pattern - * - * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure - */ -int -xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node) -{ - int ret = 0; - - if ((comp == NULL) || (node == NULL)) - return(-1); - - while (comp != NULL) { - ret = xmlPatMatch(comp, node); - if (ret != 0) - return(ret); - comp = comp->next; - } - return(ret); -} - -/** - * xmlPatternGetStreamCtxt: - * @comp: the precompiled pattern - * - * Get a streaming context for that pattern - * Use xmlFreeStreamCtxt to free the context. - * - * Returns a pointer to the context or NULL in case of failure - */ -xmlStreamCtxtPtr -xmlPatternGetStreamCtxt(xmlPatternPtr comp) -{ - xmlStreamCtxtPtr ret = NULL, cur; - - if ((comp == NULL) || (comp->stream == NULL)) - return(NULL); - - while (comp != NULL) { - if (comp->stream == NULL) - goto failed; - cur = xmlNewStreamCtxt(comp->stream); - if (cur == NULL) - goto failed; - if (ret == NULL) - ret = cur; - else { - cur->next = ret->next; - ret->next = cur; - } - cur->flags = comp->flags; - comp = comp->next; - } - return(ret); -failed: - xmlFreeStreamCtxt(ret); - return(NULL); -} - -/** - * xmlPatternStreamable: - * @comp: the precompiled pattern - * - * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt() - * should work. - * - * Returns 1 if streamable, 0 if not and -1 in case of error. - */ -int -xmlPatternStreamable(xmlPatternPtr comp) { - if (comp == NULL) - return(-1); - while (comp != NULL) { - if (comp->stream == NULL) - return(0); - comp = comp->next; - } - return(1); -} - -/** - * xmlPatternMaxDepth: - * @comp: the precompiled pattern - * - * Check the maximum depth reachable by a pattern - * - * Returns -2 if no limit (using //), otherwise the depth, - * and -1 in case of error - */ -int -xmlPatternMaxDepth(xmlPatternPtr comp) { - int ret = 0, i; - if (comp == NULL) - return(-1); - while (comp != NULL) { - if (comp->stream == NULL) - return(-1); - for (i = 0;i < comp->stream->nbStep;i++) - if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC) - return(-2); - if (comp->stream->nbStep > ret) - ret = comp->stream->nbStep; - comp = comp->next; - } - return(ret); -} - -/** - * xmlPatternMinDepth: - * @comp: the precompiled pattern - * - * Check the minimum depth reachable by a pattern, 0 mean the / or . are - * part of the set. - * - * Returns -1 in case of error otherwise the depth, - * - */ -int -xmlPatternMinDepth(xmlPatternPtr comp) { - int ret = 12345678; - if (comp == NULL) - return(-1); - while (comp != NULL) { - if (comp->stream == NULL) - return(-1); - if (comp->stream->nbStep < ret) - ret = comp->stream->nbStep; - if (ret == 0) - return(0); - comp = comp->next; - } - return(ret); -} - -/** - * xmlPatternFromRoot: - * @comp: the precompiled pattern - * - * Check if the pattern must be looked at from the root. - * - * Returns 1 if true, 0 if false and -1 in case of error - */ -int -xmlPatternFromRoot(xmlPatternPtr comp) { - if (comp == NULL) - return(-1); - while (comp != NULL) { - if (comp->stream == NULL) - return(-1); - if (comp->flags & PAT_FROM_ROOT) - return(1); - comp = comp->next; - } - return(0); - -} - -#define bottom_pattern -#include "elfgcchack.h" -#endif /* LIBXML_PATTERN_ENABLED */ + * level. + */ + stepNr = stream->states[2 * i]; + + /* TODO: should not happen anymore: dead states */ + if (stepNr < 0) + goto next_state; + + tmp = stream->states[(2 * i) + 1]; + + /* skip new states just added */ + if (tmp > stream->level) + goto next_state; + + /* skip states at ancestor levels, except if "//" */ + desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC; + if ((tmp < stream->level) && (!desc)) + goto next_state; + } + /* + * Check for correct node-type. + */ + step = comp->steps[stepNr]; + if (step.nodeType != nodeType) { + if (step.nodeType == XML_ATTRIBUTE_NODE) { + /* + * Block this expression for deeper evaluation. + */ + if ((comp->flags & XML_STREAM_DESC) == 0) + stream->blockLevel = stream->level +1; + goto next_state; + } else if (step.nodeType != XML_STREAM_ANY_NODE) + goto next_state; + } + /* + * Compare local/namespace-name. + */ + match = 0; + if (step.nodeType == XML_STREAM_ANY_NODE) { + match = 1; + } else if (step.name == NULL) { + if (step.ns == NULL) { + /* + * This lets through all elements/attributes. + */ + match = 1; + } else if (ns != NULL) + match = xmlStrEqual(step.ns, ns); + } else if (((step.ns != NULL) == (ns != NULL)) && + (name != NULL) && + (step.name[0] == name[0]) && + xmlStrEqual(step.name, name) && + ((step.ns == ns) || xmlStrEqual(step.ns, ns))) + { + match = 1; + } +#if 0 +/* +* TODO: Pointer comparison won't work, since not guaranteed that the given +* values are in the same dict; especially if it's the namespace name, +* normally coming from ns->href. We need a namespace dict mechanism ! +*/ + } else if (comp->dict) { + if (step.name == NULL) { + if (step.ns == NULL) + match = 1; + else + match = (step.ns == ns); + } else { + match = ((step.name == name) && (step.ns == ns)); + } +#endif /* if 0 ------------------------------------------------------- */ + if (match) { + final = step.flags & XML_STREAM_STEP_FINAL; + if (desc) { + if (final) { + ret = 1; + } else { + /* descending match create a new state */ + xmlStreamCtxtAddState(stream, stepNr + 1, + stream->level + 1); + } + } else { + if (final) { + ret = 1; + } else { + xmlStreamCtxtAddState(stream, stepNr + 1, + stream->level + 1); + } + } + if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) { + /* + * Check if we have a special case like "foo/bar//.", where + * "foo" is selected as well. + */ + ret = 1; + } + } + if (((comp->flags & XML_STREAM_DESC) == 0) && + ((! match) || final)) { + /* + * Mark this expression as blocked for any evaluation at + * deeper levels. Note that this includes "/foo" + * expressions if the *pattern* behaviour is used. + */ + stream->blockLevel = stream->level +1; + } +next_state: + i++; + } + + stream->level++; + + /* + * Re/enter the expression. + * Don't reenter if it's an absolute expression like "/foo", + * except "//foo". + */ + step = comp->steps[0]; + if (step.flags & XML_STREAM_STEP_ROOT) + goto stream_next; + + desc = step.flags & XML_STREAM_STEP_DESC; + if (stream->flags & XML_PATTERN_NOTPATTERN) { + /* + * Re/enter the expression if it is a "descendant" one, + * or if we are at the 1st level of evaluation. + */ + + if (stream->level == 1) { + if (XML_STREAM_XS_IDC(stream)) { + /* + * XS-IDC: The missing "self::node()" will always + * match the first given node. + */ + goto stream_next; + } else + goto compare; + } + /* + * A "//" is always reentrant. + */ + if (desc) + goto compare; + + /* + * XS-IDC: Process the 2nd level, since the missing + * "self::node()" is responsible for the 2nd level being + * the real start level. + */ + if ((stream->level == 2) && XML_STREAM_XS_IDC(stream)) + goto compare; + + goto stream_next; + } + +compare: + /* + * Check expected node-type. + */ + if (step.nodeType != nodeType) { + if (nodeType == XML_ATTRIBUTE_NODE) + goto stream_next; + else if (step.nodeType != XML_STREAM_ANY_NODE) + goto stream_next; + } + /* + * Compare local/namespace-name. + */ + match = 0; + if (step.nodeType == XML_STREAM_ANY_NODE) { + match = 1; + } else if (step.name == NULL) { + if (step.ns == NULL) { + /* + * This lets through all elements/attributes. + */ + match = 1; + } else if (ns != NULL) + match = xmlStrEqual(step.ns, ns); + } else if (((step.ns != NULL) == (ns != NULL)) && + (name != NULL) && + (step.name[0] == name[0]) && + xmlStrEqual(step.name, name) && + ((step.ns == ns) || xmlStrEqual(step.ns, ns))) + { + match = 1; + } + final = step.flags & XML_STREAM_STEP_FINAL; + if (match) { + if (final) + ret = 1; + else + xmlStreamCtxtAddState(stream, 1, stream->level); + if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) { + /* + * Check if we have a special case like "foo//.", where + * "foo" is selected as well. + */ + ret = 1; + } + } + if (((comp->flags & XML_STREAM_DESC) == 0) && + ((! match) || final)) { + /* + * Mark this expression as blocked for any evaluation at + * deeper levels. + */ + stream->blockLevel = stream->level; + } + +stream_next: + stream = stream->next; + } /* while stream != NULL */ + + if (err > 0) + ret = -1; +#ifdef DEBUG_STREAMING + xmlDebugStreamCtxt(orig, ret); +#endif + return(ret); +} + +/** + * xmlStreamPush: + * @stream: the stream context + * @name: the current name + * @ns: the namespace name + * + * Push new data onto the stream. NOTE: if the call xmlPatterncompile() + * indicated a dictionary, then strings for name and ns will be expected + * to come from the dictionary. + * Both @name and @ns being NULL means the / i.e. the root of the document. + * This can also act as a reset. + * Otherwise the function will act as if it has been given an element-node. + * + * Returns: -1 in case of error, 1 if the current state in the stream is a + * match and 0 otherwise. + */ +int +xmlStreamPush(xmlStreamCtxtPtr stream, + const xmlChar *name, const xmlChar *ns) { + return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE)); +} + +/** + * xmlStreamPushNode: + * @stream: the stream context + * @name: the current name + * @ns: the namespace name + * @nodeType: the type of the node being pushed + * + * Push new data onto the stream. NOTE: if the call xmlPatterncompile() + * indicated a dictionary, then strings for name and ns will be expected + * to come from the dictionary. + * Both @name and @ns being NULL means the / i.e. the root of the document. + * This can also act as a reset. + * Different from xmlStreamPush() this function can be fed with nodes of type: + * element-, attribute-, text-, cdata-section-, comment- and + * processing-instruction-node. + * + * Returns: -1 in case of error, 1 if the current state in the stream is a + * match and 0 otherwise. + */ +int +xmlStreamPushNode(xmlStreamCtxtPtr stream, + const xmlChar *name, const xmlChar *ns, + int nodeType) +{ + return (xmlStreamPushInternal(stream, name, ns, + nodeType)); +} + +/** +* xmlStreamPushAttr: +* @stream: the stream context +* @name: the current name +* @ns: the namespace name +* +* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile() +* indicated a dictionary, then strings for name and ns will be expected +* to come from the dictionary. +* Both @name and @ns being NULL means the / i.e. the root of the document. +* This can also act as a reset. +* Otherwise the function will act as if it has been given an attribute-node. +* +* Returns: -1 in case of error, 1 if the current state in the stream is a +* match and 0 otherwise. +*/ +int +xmlStreamPushAttr(xmlStreamCtxtPtr stream, + const xmlChar *name, const xmlChar *ns) { + return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE)); +} + +/** + * xmlStreamPop: + * @stream: the stream context + * + * push one level from the stream. + * + * Returns: -1 in case of error, 0 otherwise. + */ +int +xmlStreamPop(xmlStreamCtxtPtr stream) { + int i, lev; + + if (stream == NULL) + return(-1); + while (stream != NULL) { + /* + * Reset block-level. + */ + if (stream->blockLevel == stream->level) + stream->blockLevel = -1; + + /* + * stream->level can be zero when XML_FINAL_IS_ANY_NODE is set + * (see the thread at + * http://mail.gnome.org/archives/xslt/2008-July/msg00027.html) + */ + if (stream->level) + stream->level--; + /* + * Check evolution of existing states + */ + for (i = stream->nbState -1; i >= 0; i--) { + /* discard obsoleted states */ + lev = stream->states[(2 * i) + 1]; + if (lev > stream->level) + stream->nbState--; + if (lev <= stream->level) + break; + } + stream = stream->next; + } + return(0); +} + +/** + * xmlStreamWantsAnyNode: + * @streamCtxt: the stream context + * + * Query if the streaming pattern additionally needs to be fed with + * text-, cdata-section-, comment- and processing-instruction-nodes. + * If the result is 0 then only element-nodes and attribute-nodes + * need to be pushed. + * + * Returns: 1 in case of need of nodes of the above described types, + * 0 otherwise. -1 on API errors. + */ +int +xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt) +{ + if (streamCtxt == NULL) + return(-1); + while (streamCtxt != NULL) { + if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) + return(1); + streamCtxt = streamCtxt->next; + } + return(0); +} + +/************************************************************************ + * * + * The public interfaces * + * * + ************************************************************************/ + +/** + * xmlPatterncompile: + * @pattern: the pattern to compile + * @dict: an optional dictionary for interned strings + * @flags: compilation flags, see xmlPatternFlags + * @namespaces: the prefix definitions, array of [URI, prefix] or NULL + * + * Compile a pattern. + * + * Returns the compiled form of the pattern or NULL in case of error + */ +xmlPatternPtr +xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags, + const xmlChar **namespaces) { + xmlPatternPtr ret = NULL, cur; + xmlPatParserContextPtr ctxt = NULL; + const xmlChar *or, *start; + xmlChar *tmp = NULL; + int type = 0; + int streamable = 1; + + if (pattern == NULL) + return(NULL); + + start = pattern; + or = start; + while (*or != 0) { + tmp = NULL; + while ((*or != 0) && (*or != '|')) or++; + if (*or == 0) + ctxt = xmlNewPatParserContext(start, dict, namespaces); + else { + tmp = xmlStrndup(start, or - start); + if (tmp != NULL) { + ctxt = xmlNewPatParserContext(tmp, dict, namespaces); + } + or++; + } + if (ctxt == NULL) goto error; + cur = xmlNewPattern(); + if (cur == NULL) goto error; + /* + * Assign string dict. + */ + if (dict) { + cur->dict = dict; + xmlDictReference(dict); + } + if (ret == NULL) + ret = cur; + else { + cur->next = ret->next; + ret->next = cur; + } + cur->flags = flags; + ctxt->comp = cur; + + if (XML_STREAM_XS_IDC(cur)) + xmlCompileIDCXPathPath(ctxt); + else + xmlCompilePathPattern(ctxt); + if (ctxt->error != 0) + goto error; + xmlFreePatParserContext(ctxt); + ctxt = NULL; + + + if (streamable) { + if (type == 0) { + type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR); + } else if (type == PAT_FROM_ROOT) { + if (cur->flags & PAT_FROM_CUR) + streamable = 0; + } else if (type == PAT_FROM_CUR) { + if (cur->flags & PAT_FROM_ROOT) + streamable = 0; + } + } + if (streamable) + xmlStreamCompile(cur); + if (xmlReversePattern(cur) < 0) + goto error; + if (tmp != NULL) { + xmlFree(tmp); + tmp = NULL; + } + start = or; + } + if (streamable == 0) { + cur = ret; + while (cur != NULL) { + if (cur->stream != NULL) { + xmlFreeStreamComp(cur->stream); + cur->stream = NULL; + } + cur = cur->next; + } + } + + return(ret); +error: + if (ctxt != NULL) xmlFreePatParserContext(ctxt); + if (ret != NULL) xmlFreePattern(ret); + if (tmp != NULL) xmlFree(tmp); + return(NULL); +} + +/** + * xmlPatternMatch: + * @comp: the precompiled pattern + * @node: a node + * + * Test whether the node matches the pattern + * + * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure + */ +int +xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node) +{ + int ret = 0; + + if ((comp == NULL) || (node == NULL)) + return(-1); + + while (comp != NULL) { + ret = xmlPatMatch(comp, node); + if (ret != 0) + return(ret); + comp = comp->next; + } + return(ret); +} + +/** + * xmlPatternGetStreamCtxt: + * @comp: the precompiled pattern + * + * Get a streaming context for that pattern + * Use xmlFreeStreamCtxt to free the context. + * + * Returns a pointer to the context or NULL in case of failure + */ +xmlStreamCtxtPtr +xmlPatternGetStreamCtxt(xmlPatternPtr comp) +{ + xmlStreamCtxtPtr ret = NULL, cur; + + if ((comp == NULL) || (comp->stream == NULL)) + return(NULL); + + while (comp != NULL) { + if (comp->stream == NULL) + goto failed; + cur = xmlNewStreamCtxt(comp->stream); + if (cur == NULL) + goto failed; + if (ret == NULL) + ret = cur; + else { + cur->next = ret->next; + ret->next = cur; + } + cur->flags = comp->flags; + comp = comp->next; + } + return(ret); +failed: + xmlFreeStreamCtxt(ret); + return(NULL); +} + +/** + * xmlPatternStreamable: + * @comp: the precompiled pattern + * + * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt() + * should work. + * + * Returns 1 if streamable, 0 if not and -1 in case of error. + */ +int +xmlPatternStreamable(xmlPatternPtr comp) { + if (comp == NULL) + return(-1); + while (comp != NULL) { + if (comp->stream == NULL) + return(0); + comp = comp->next; + } + return(1); +} + +/** + * xmlPatternMaxDepth: + * @comp: the precompiled pattern + * + * Check the maximum depth reachable by a pattern + * + * Returns -2 if no limit (using //), otherwise the depth, + * and -1 in case of error + */ +int +xmlPatternMaxDepth(xmlPatternPtr comp) { + int ret = 0, i; + if (comp == NULL) + return(-1); + while (comp != NULL) { + if (comp->stream == NULL) + return(-1); + for (i = 0;i < comp->stream->nbStep;i++) + if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC) + return(-2); + if (comp->stream->nbStep > ret) + ret = comp->stream->nbStep; + comp = comp->next; + } + return(ret); +} + +/** + * xmlPatternMinDepth: + * @comp: the precompiled pattern + * + * Check the minimum depth reachable by a pattern, 0 mean the / or . are + * part of the set. + * + * Returns -1 in case of error otherwise the depth, + * + */ +int +xmlPatternMinDepth(xmlPatternPtr comp) { + int ret = 12345678; + if (comp == NULL) + return(-1); + while (comp != NULL) { + if (comp->stream == NULL) + return(-1); + if (comp->stream->nbStep < ret) + ret = comp->stream->nbStep; + if (ret == 0) + return(0); + comp = comp->next; + } + return(ret); +} + +/** + * xmlPatternFromRoot: + * @comp: the precompiled pattern + * + * Check if the pattern must be looked at from the root. + * + * Returns 1 if true, 0 if false and -1 in case of error + */ +int +xmlPatternFromRoot(xmlPatternPtr comp) { + if (comp == NULL) + return(-1); + while (comp != NULL) { + if (comp->stream == NULL) + return(-1); + if (comp->flags & PAT_FROM_ROOT) + return(1); + comp = comp->next; + } + return(0); + +} + +#define bottom_pattern +#include "elfgcchack.h" +#endif /* LIBXML_PATTERN_ENABLED */ |