Newer
Older
/*
* pattern.c: Implementation of selectors for nodes
*
* Reference:
* http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
* 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 .
Daniel Veillard
committed
* - 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>
Daniel Veillard
committed
#ifdef LIBXML_PATTERN_ENABLED
Daniel Veillard
committed
/* #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
Kasimier T. Buchcik
committed
#define XML_STREAM_STEP_NODE 16
Kasimier T. Buchcik
committed
#define XML_STREAM_STEP_IN_SET 32
/*
Kasimier T. Buchcik
committed
* 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
*/
Kasimier T. Buchcik
committed
#define XML_STREAM_FINAL_IS_ANY_NODE 1<<14
#define XML_STREAM_FROM_ROOT 1<<15
#define XML_STREAM_DESC 1<<16
Kasimier T. Buchcik
committed
/*
* 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)
Kasimier T. Buchcik
committed
#define XML_STREAM_XS_IDC(c) ((c)->flags & \
(XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
Kasimier T. Buchcik
committed
Kasimier T. Buchcik
committed
#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 */
Kasimier T. Buchcik
committed
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 */
Kasimier T. Buchcik
committed
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;
Daniel Veillard
committed
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 {
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 */
};
/************************************************************************
************************************************************************/
/**
* 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;
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)
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;
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
}
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 *
* *
************************************************************************/
Daniel Veillard
committed
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;
Daniel Veillard
committed
xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillard
committed
i = 0;
restart:
for (;i < comp->nbStep;i++) {
step = &comp->steps[i];
switch (step->op) {
case XML_OP_END:
Daniel Veillard
committed
goto found;
case XML_OP_ROOT:
if (node->type == XML_NAMESPACE_DECL)
Daniel Veillard
committed
goto rollback;
node = node->parent;
if ((node->type == XML_DOCUMENT_NODE) ||
(node->type == XML_HTML_DOCUMENT_NODE))
continue;
Daniel Veillard
committed
goto rollback;
case XML_OP_ELEM:
if (node->type != XML_ELEMENT_NODE)
Daniel Veillard
committed
goto rollback;
if (step->value == NULL)
continue;
if (step->value[0] != node->name[0])
Daniel Veillard
committed
goto rollback;
if (!xmlStrEqual(step->value, node->name))
Daniel Veillard
committed
goto rollback;
/* Namespace test */
if (node->ns == NULL) {
if (step->value2 != NULL)
Daniel Veillard
committed
goto rollback;
} else if (node->ns->href != NULL) {
if (step->value2 == NULL)
Daniel Veillard
committed
goto rollback;
if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillard
committed
goto rollback;
}
continue;
case XML_OP_CHILD: {
xmlNodePtr lst;
if ((node->type != XML_ELEMENT_NODE) &&
(node->type != XML_DOCUMENT_NODE) &&
(node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillard
committed
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;
}
Daniel Veillard
committed
goto rollback;
}
case XML_OP_ATTR:
if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillard
committed
goto rollback;
if (step->value != NULL) {
if (step->value[0] != node->name[0])
Daniel Veillard
committed
goto rollback;
if (!xmlStrEqual(step->value, node->name))
Daniel Veillard
committed
goto rollback;
}
/* Namespace test */
if (node->ns == NULL) {
if (step->value2 != NULL)
Daniel Veillard
committed
goto rollback;
} else if (step->value2 != NULL) {
if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillard
committed
goto rollback;
}
continue;
case XML_OP_PARENT:
if ((node->type == XML_DOCUMENT_NODE) ||
(node->type == XML_HTML_DOCUMENT_NODE) ||
(node->type == XML_NAMESPACE_DECL))
Daniel Veillard
committed
goto rollback;
node = node->parent;
if (node == NULL)
Daniel Veillard
committed
goto rollback;
if (step->value == NULL)
continue;
if (step->value[0] != node->name[0])
Daniel Veillard
committed
goto rollback;
if (!xmlStrEqual(step->value, node->name))
Daniel Veillard
committed
goto rollback;
/* Namespace test */
if (node->ns == NULL) {
if (step->value2 != NULL)
Daniel Veillard
committed
goto rollback;
} else if (node->ns->href != NULL) {
if (step->value2 == NULL)
Daniel Veillard
committed
goto rollback;
if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillard
committed
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)
Daniel Veillard
committed
goto found;
if (step->op != XML_OP_ELEM)
Daniel Veillard
committed
goto rollback;
if (step->value == NULL)
return(-1);
}
if (node == NULL)
Daniel Veillard
committed
goto rollback;
if ((node->type == XML_DOCUMENT_NODE) ||
(node->type == XML_HTML_DOCUMENT_NODE) ||
(node->type == XML_NAMESPACE_DECL))
Daniel Veillard
committed
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)
Daniel Veillard
committed
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)
Daniel Veillard
committed
goto rollback;
if (node->ns == NULL) {
if (step->value != NULL)
Daniel Veillard
committed
goto rollback;
} else if (node->ns->href != NULL) {
if (step->value == NULL)
Daniel Veillard
committed
goto rollback;
if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillard
committed
goto rollback;
}
break;
case XML_OP_ALL:
if (node->type != XML_ELEMENT_NODE)
Daniel Veillard
committed
goto rollback;
break;
}
}
Daniel Veillard
committed
found:
if (states.states != NULL) {
/* Free the rollback states */
xmlFree(states.states);
}
return(1);
Daniel Veillard
committed
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 *
* *
************************************************************************/
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)]
Kasimier T. Buchcik
committed
#define PEEKPREV(val) ctxt->cur[-(val)]
#define CUR_PTR ctxt->cur
while (IS_BLANK_CH(CUR)) NEXT
#define CURRENT (*ctxt->cur)
#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
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
*
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
*
* [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
}
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
}
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 == '-') ||
(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
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
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);
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
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;
Kasimier T. Buchcik
committed
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;
Kasimier T. Buchcik
committed
Kasimier T. Buchcik
committed
ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
XML_PAT_FREE_STRING(ctxt, prefix);
Kasimier T. Buchcik
committed
ctxt->error = 1;
goto error;
}
/*
* This is a namespace match
*/
token = xmlPatScanName(ctxt);
Kasimier T. Buchcik
committed
if ((prefix[0] == 'x') &&
(prefix[1] == 'm') &&
(prefix[2] == 'l') &&
(prefix[3] == 0))
{
XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
Kasimier T. Buchcik
committed
} 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])
Kasimier T. Buchcik
committed
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);
Kasimier T. Buchcik
committed
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)
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