blob: a76a58c55e12383d2ab527914ff79dff60090f5f [file] [log] [blame]
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001/*
2 * pattern.c: Implemetation of selectors for nodes
3 *
4 * Reference:
5 * http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
6 * to some extent
7 * http://www.w3.org/TR/1999/REC-xml-19991116
8 *
9 * See Copyright for the status of this software.
10 *
11 * daniel@veillard.com
12 */
13
Daniel Veillardf9d16912005-01-30 22:36:30 +000014/*
15 * TODO:
16 * - compilation flags to check for specific syntaxes
17 * using flags of xmlPatterncompile()
18 * - making clear how pattern starting with / or . need to be handled,
19 * currently push(NULL, NULL) means a reset of the streaming context
20 * and indicating we are on / (the document node), probably need
21 * something similar for .
Daniel Veillardd4301ab2005-02-03 22:24:10 +000022 * - get rid of the "compile" starting with lowercase
23 * - get rid of the Strdup/Strndup in case of dictionary
Daniel Veillardf9d16912005-01-30 22:36:30 +000024 */
25
Daniel Veillardb3de70c2003-12-02 22:32:15 +000026#define IN_LIBXML
27#include "libxml.h"
28
29#include <string.h>
30#include <libxml/xmlmemory.h>
31#include <libxml/tree.h>
32#include <libxml/hash.h>
33#include <libxml/dict.h>
34#include <libxml/xmlerror.h>
35#include <libxml/parserInternals.h>
36#include <libxml/pattern.h>
37
Daniel Veillardd4301ab2005-02-03 22:24:10 +000038#ifdef LIBXML_PATTERN_ENABLED
Daniel Veillardb3de70c2003-12-02 22:32:15 +000039
Daniel Veillardd4301ab2005-02-03 22:24:10 +000040/* #define DEBUG_STREAMING */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000041
Daniel Veillardb3de70c2003-12-02 22:32:15 +000042#define ERROR(a, b, c, d)
43#define ERROR5(a, b, c, d, e)
44
Daniel Veillard2fc6df92005-01-30 18:42:55 +000045#define XML_STREAM_STEP_DESC 1
46#define XML_STREAM_STEP_FINAL 2
47#define XML_STREAM_STEP_ROOT 4
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +000048#define XML_STREAM_STEP_ATTR 8
Daniel Veillard2fc6df92005-01-30 18:42:55 +000049
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000050/*
51* TODO: This is used on _xmlStreamCtxt, so don't use any values
52* from xmlPatternFlags.
53*/
54#define XML_STREAM_DESC 1<<16
55
William M. Brackea152c02005-06-09 18:12:28 +000056#define XML_PATTERN_NOTPATTERN (XML_PATTERN_XPATH | \
57 XML_PATTERN_XSSEL | \
58 XML_PATTERN_XSFIELD)
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000059
60#define XML_STREAM_XS_IDC(item) (item->flags & \
61 (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000062
Daniel Veillard2fc6df92005-01-30 18:42:55 +000063typedef struct _xmlStreamStep xmlStreamStep;
64typedef xmlStreamStep *xmlStreamStepPtr;
65struct _xmlStreamStep {
66 int flags; /* properties of that step */
67 const xmlChar *name; /* first string value if NULL accept all */
68 const xmlChar *ns; /* second string value */
69};
70
71typedef struct _xmlStreamComp xmlStreamComp;
72typedef xmlStreamComp *xmlStreamCompPtr;
73struct _xmlStreamComp {
William M. Brackfbb619f2005-06-06 13:49:18 +000074 xmlDict *dict; /* the dictionary if any */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000075 int nbStep; /* number of steps in the automata */
76 int maxStep; /* allocated number of steps */
77 xmlStreamStepPtr steps; /* the array of steps */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000078 int flags;
Daniel Veillard2fc6df92005-01-30 18:42:55 +000079};
80
81struct _xmlStreamCtxt {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +000082 struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000083 xmlStreamCompPtr comp; /* the compiled stream */
William M. Brackfbb619f2005-06-06 13:49:18 +000084 int nbState; /* number of states in the automata */
85 int maxState; /* allocated number of states */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000086 int level; /* how deep are we ? */
87 int *states; /* the array of step indexes */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000088 int flags; /* validation options */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000089 int blockLevel;
Daniel Veillard2fc6df92005-01-30 18:42:55 +000090};
91
92static void xmlFreeStreamComp(xmlStreamCompPtr comp);
93
Daniel Veillardb3de70c2003-12-02 22:32:15 +000094/*
95 * Types are private:
96 */
97
98typedef enum {
99 XML_OP_END=0,
100 XML_OP_ROOT,
101 XML_OP_ELEM,
102 XML_OP_CHILD,
103 XML_OP_ATTR,
104 XML_OP_PARENT,
105 XML_OP_ANCESTOR,
106 XML_OP_NS,
107 XML_OP_ALL
108} xmlPatOp;
109
110
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000111typedef struct _xmlStepState xmlStepState;
112typedef xmlStepState *xmlStepStatePtr;
113struct _xmlStepState {
114 int step;
115 xmlNodePtr node;
116};
117
118typedef struct _xmlStepStates xmlStepStates;
119typedef xmlStepStates *xmlStepStatesPtr;
120struct _xmlStepStates {
121 int nbstates;
122 int maxstates;
123 xmlStepStatePtr states;
124};
125
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000126typedef struct _xmlStepOp xmlStepOp;
127typedef xmlStepOp *xmlStepOpPtr;
128struct _xmlStepOp {
129 xmlPatOp op;
130 const xmlChar *value;
131 const xmlChar *value2;
132};
133
William M. Brackea152c02005-06-09 18:12:28 +0000134#define PAT_FROM_ROOT (1<<8)
135#define PAT_FROM_CUR (1<<9)
Daniel Veillard56de87e2005-02-16 00:22:29 +0000136
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000137struct _xmlPattern {
138 void *data; /* the associated template */
William M. Brackfbb619f2005-06-06 13:49:18 +0000139 xmlDictPtr dict; /* the optional dictionary */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000140 struct _xmlPattern *next; /* next pattern if | is used */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000141 const xmlChar *pattern; /* the pattern */
Daniel Veillardf5812c32005-09-03 13:43:20 +0000142 int flags; /* flags */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000143 int nbStep;
144 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000145 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000146 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000147};
148
149typedef struct _xmlPatParserContext xmlPatParserContext;
150typedef xmlPatParserContext *xmlPatParserContextPtr;
151struct _xmlPatParserContext {
152 const xmlChar *cur; /* the current char being parsed */
153 const xmlChar *base; /* the full expression */
154 int error; /* error code */
William M. Brackfbb619f2005-06-06 13:49:18 +0000155 xmlDictPtr dict; /* the dictionary if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000156 xmlPatternPtr comp; /* the result */
157 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000158 const xmlChar **namespaces; /* the namespaces definitions */
159 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000160};
161
162/************************************************************************
163 * *
164 * Type functions *
165 * *
166 ************************************************************************/
167
168/**
169 * xmlNewPattern:
170 *
171 * Create a new XSLT Pattern
172 *
173 * Returns the newly allocated xmlPatternPtr or NULL in case of error
174 */
175static xmlPatternPtr
176xmlNewPattern(void) {
177 xmlPatternPtr cur;
178
179 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
180 if (cur == NULL) {
181 ERROR(NULL, NULL, NULL,
182 "xmlNewPattern : malloc failed\n");
183 return(NULL);
184 }
185 memset(cur, 0, sizeof(xmlPattern));
186 cur->maxStep = 10;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000187 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
188 if (cur->steps == NULL) {
189 xmlFree(cur);
190 ERROR(NULL, NULL, NULL,
191 "xmlNewPattern : malloc failed\n");
192 return(NULL);
193 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000194 return(cur);
195}
196
197/**
198 * xmlFreePattern:
199 * @comp: an XSLT comp
200 *
201 * Free up the memory allocated by @comp
202 */
203void
204xmlFreePattern(xmlPatternPtr comp) {
205 xmlStepOpPtr op;
206 int i;
207
208 if (comp == NULL)
209 return;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000210 if (comp->next != NULL)
211 xmlFreePattern(comp->next);
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000212 if (comp->stream != NULL)
213 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000214 if (comp->pattern != NULL)
215 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000216 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000217 if (comp->dict == NULL) {
218 for (i = 0;i < comp->nbStep;i++) {
219 op = &comp->steps[i];
220 if (op->value != NULL)
221 xmlFree((xmlChar *) op->value);
222 if (op->value2 != NULL)
223 xmlFree((xmlChar *) op->value2);
224 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000225 }
226 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000227 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000228 if (comp->dict != NULL)
229 xmlDictFree(comp->dict);
230
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000231 memset(comp, -1, sizeof(xmlPattern));
232 xmlFree(comp);
233}
234
235/**
236 * xmlFreePatternList:
237 * @comp: an XSLT comp list
238 *
239 * Free up the memory allocated by all the elements of @comp
240 */
241void
242xmlFreePatternList(xmlPatternPtr comp) {
243 xmlPatternPtr cur;
244
245 while (comp != NULL) {
246 cur = comp;
247 comp = comp->next;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +0000248 cur->next = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000249 xmlFreePattern(cur);
250 }
251}
252
253/**
254 * xmlNewPatParserContext:
255 * @pattern: the pattern context
William M. Brackfbb619f2005-06-06 13:49:18 +0000256 * @dict: the inherited dictionary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000257 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
258 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000259 *
260 * Create a new XML pattern parser context
261 *
262 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
263 */
264static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000265xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
266 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000267 xmlPatParserContextPtr cur;
268
269 if (pattern == NULL)
270 return(NULL);
271
272 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
273 if (cur == NULL) {
274 ERROR(NULL, NULL, NULL,
275 "xmlNewPatParserContext : malloc failed\n");
276 return(NULL);
277 }
278 memset(cur, 0, sizeof(xmlPatParserContext));
279 cur->dict = dict;
280 cur->cur = pattern;
281 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000282 if (namespaces != NULL) {
283 int i;
284 for (i = 0;namespaces[2 * i] != NULL;i++);
285 cur->nb_namespaces = i;
286 } else {
287 cur->nb_namespaces = 0;
288 }
289 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000290 return(cur);
291}
292
293/**
294 * xmlFreePatParserContext:
295 * @ctxt: an XSLT parser context
296 *
297 * Free up the memory allocated by @ctxt
298 */
299static void
300xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
301 if (ctxt == NULL)
302 return;
303 memset(ctxt, -1, sizeof(xmlPatParserContext));
304 xmlFree(ctxt);
305}
306
307/**
308 * xmlPatternAdd:
309 * @comp: the compiled match expression
310 * @op: an op
311 * @value: the first value
312 * @value2: the second value
313 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000314 * Add a step to an XSLT Compiled Match
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000315 *
316 * Returns -1 in case of failure, 0 otherwise.
317 */
318static int
319xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
320 xmlPatternPtr comp,
321 xmlPatOp op, xmlChar * value, xmlChar * value2)
322{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000323 if (comp->nbStep >= comp->maxStep) {
324 xmlStepOpPtr temp;
325 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
326 sizeof(xmlStepOp));
327 if (temp == NULL) {
328 ERROR(ctxt, NULL, NULL,
329 "xmlPatternAdd: realloc failed\n");
330 return (-1);
331 }
332 comp->steps = temp;
333 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000334 }
335 comp->steps[comp->nbStep].op = op;
336 comp->steps[comp->nbStep].value = value;
337 comp->steps[comp->nbStep].value2 = value2;
338 comp->nbStep++;
339 return (0);
340}
341
342#if 0
343/**
344 * xsltSwapTopPattern:
345 * @comp: the compiled match expression
346 *
347 * reverse the two top steps.
348 */
349static void
350xsltSwapTopPattern(xmlPatternPtr comp) {
351 int i;
352 int j = comp->nbStep - 1;
353
354 if (j > 0) {
355 register const xmlChar *tmp;
356 register xmlPatOp op;
357 i = j - 1;
358 tmp = comp->steps[i].value;
359 comp->steps[i].value = comp->steps[j].value;
360 comp->steps[j].value = tmp;
361 tmp = comp->steps[i].value2;
362 comp->steps[i].value2 = comp->steps[j].value2;
363 comp->steps[j].value2 = tmp;
364 op = comp->steps[i].op;
365 comp->steps[i].op = comp->steps[j].op;
366 comp->steps[j].op = op;
367 }
368}
369#endif
370
371/**
372 * xmlReversePattern:
373 * @comp: the compiled match expression
374 *
375 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000376 *
377 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000378 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000379static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000380xmlReversePattern(xmlPatternPtr comp) {
Daniel Veillard56de87e2005-02-16 00:22:29 +0000381 int i, j;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000382
Daniel Veillard56de87e2005-02-16 00:22:29 +0000383 /*
384 * remove the leading // for //a or .//a
385 */
386 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
387 for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
388 comp->steps[i].value = comp->steps[j].value;
389 comp->steps[i].value2 = comp->steps[j].value2;
390 comp->steps[i].op = comp->steps[j].op;
391 }
392 comp->nbStep--;
393 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000394 if (comp->nbStep >= comp->maxStep) {
395 xmlStepOpPtr temp;
396 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
397 sizeof(xmlStepOp));
398 if (temp == NULL) {
399 ERROR(ctxt, NULL, NULL,
400 "xmlReversePattern: realloc failed\n");
401 return (-1);
402 }
403 comp->steps = temp;
404 comp->maxStep *= 2;
405 }
Daniel Veillard56de87e2005-02-16 00:22:29 +0000406 i = 0;
407 j = comp->nbStep - 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000408 while (j > i) {
409 register const xmlChar *tmp;
410 register xmlPatOp op;
411 tmp = comp->steps[i].value;
412 comp->steps[i].value = comp->steps[j].value;
413 comp->steps[j].value = tmp;
414 tmp = comp->steps[i].value2;
415 comp->steps[i].value2 = comp->steps[j].value2;
416 comp->steps[j].value2 = tmp;
417 op = comp->steps[i].op;
418 comp->steps[i].op = comp->steps[j].op;
419 comp->steps[j].op = op;
420 j--;
421 i++;
422 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000423 comp->steps[comp->nbStep].value = NULL;
424 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000425 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000426 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000427}
428
429/************************************************************************
430 * *
431 * The interpreter for the precompiled patterns *
432 * *
433 ************************************************************************/
434
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000435static int
436xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
437 if ((states->states == NULL) || (states->maxstates <= 0)) {
438 states->maxstates = 4;
439 states->nbstates = 0;
440 states->states = xmlMalloc(4 * sizeof(xmlStepState));
441 }
442 else if (states->maxstates <= states->nbstates) {
443 xmlStepState *tmp;
444
445 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
446 2 * states->maxstates * sizeof(xmlStepState));
447 if (tmp == NULL)
448 return(-1);
449 states->states = tmp;
450 states->maxstates *= 2;
451 }
452 states->states[states->nbstates].step = step;
453 states->states[states->nbstates++].node = node;
454#if 0
455 fprintf(stderr, "Push: %d, %s\n", step, node->name);
456#endif
457 return(0);
458}
459
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000460/**
461 * xmlPatMatch:
462 * @comp: the precompiled pattern
463 * @node: a node
464 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000465 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000466 *
467 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
468 */
469static int
470xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
471 int i;
472 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000473 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000474
475 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000476 i = 0;
477restart:
478 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000479 step = &comp->steps[i];
480 switch (step->op) {
481 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000482 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000483 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000484 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000485 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000486 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000487 if ((node->type == XML_DOCUMENT_NODE) ||
488#ifdef LIBXML_DOCB_ENABLED
489 (node->type == XML_DOCB_DOCUMENT_NODE) ||
490#endif
491 (node->type == XML_HTML_DOCUMENT_NODE))
492 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000493 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000494 case XML_OP_ELEM:
495 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000496 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000497 if (step->value == NULL)
498 continue;
499 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000500 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000501 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000502 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000503
504 /* Namespace test */
505 if (node->ns == NULL) {
506 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000507 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000508 } else if (node->ns->href != NULL) {
509 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000510 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000511 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000512 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000513 }
514 continue;
515 case XML_OP_CHILD: {
516 xmlNodePtr lst;
517
518 if ((node->type != XML_ELEMENT_NODE) &&
519 (node->type != XML_DOCUMENT_NODE) &&
520#ifdef LIBXML_DOCB_ENABLED
521 (node->type != XML_DOCB_DOCUMENT_NODE) &&
522#endif
523 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000524 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000525
526 lst = node->children;
527
528 if (step->value != NULL) {
529 while (lst != NULL) {
530 if ((lst->type == XML_ELEMENT_NODE) &&
531 (step->value[0] == lst->name[0]) &&
532 (xmlStrEqual(step->value, lst->name)))
533 break;
534 lst = lst->next;
535 }
536 if (lst != NULL)
537 continue;
538 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000539 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000540 }
541 case XML_OP_ATTR:
542 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000543 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000544 if (step->value != NULL) {
545 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000546 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000547 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000548 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000549 }
550 /* Namespace test */
551 if (node->ns == NULL) {
552 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000553 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000554 } else if (step->value2 != NULL) {
555 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000556 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000557 }
558 continue;
559 case XML_OP_PARENT:
560 if ((node->type == XML_DOCUMENT_NODE) ||
561 (node->type == XML_HTML_DOCUMENT_NODE) ||
562#ifdef LIBXML_DOCB_ENABLED
563 (node->type == XML_DOCB_DOCUMENT_NODE) ||
564#endif
565 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000566 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000567 node = node->parent;
568 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000569 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000570 if (step->value == NULL)
571 continue;
572 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000573 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000574 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000575 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000576 /* Namespace test */
577 if (node->ns == NULL) {
578 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000579 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000580 } else if (node->ns->href != NULL) {
581 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000582 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000583 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000584 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000585 }
586 continue;
587 case XML_OP_ANCESTOR:
588 /* TODO: implement coalescing of ANCESTOR/NODE ops */
589 if (step->value == NULL) {
590 i++;
591 step = &comp->steps[i];
592 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000593 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000594 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000595 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000596 if (step->value == NULL)
597 return(-1);
598 }
599 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000600 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000601 if ((node->type == XML_DOCUMENT_NODE) ||
602 (node->type == XML_HTML_DOCUMENT_NODE) ||
603#ifdef LIBXML_DOCB_ENABLED
604 (node->type == XML_DOCB_DOCUMENT_NODE) ||
605#endif
606 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000607 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000608 node = node->parent;
609 while (node != NULL) {
610 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000611 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000612 if ((node->type == XML_ELEMENT_NODE) &&
613 (step->value[0] == node->name[0]) &&
614 (xmlStrEqual(step->value, node->name))) {
615 /* Namespace test */
616 if (node->ns == NULL) {
617 if (step->value2 == NULL)
618 break;
619 } else if (node->ns->href != NULL) {
620 if ((step->value2 != NULL) &&
621 (xmlStrEqual(step->value2, node->ns->href)))
622 break;
623 }
624 }
625 node = node->parent;
626 }
627 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000628 goto rollback;
629 /*
630 * prepare a potential rollback from here
631 * for ancestors of that node.
632 */
633 if (step->op == XML_OP_ANCESTOR)
634 xmlPatPushState(&states, i, node);
635 else
636 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000637 continue;
638 case XML_OP_NS:
639 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000640 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000641 if (node->ns == NULL) {
642 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000643 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000644 } else if (node->ns->href != NULL) {
645 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000646 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000647 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000648 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000649 }
650 break;
651 case XML_OP_ALL:
652 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000653 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000654 break;
655 }
656 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000657found:
658 if (states.states != NULL) {
659 /* Free the rollback states */
660 xmlFree(states.states);
661 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000662 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000663rollback:
664 /* got an error try to rollback */
665 if (states.states == NULL)
666 return(0);
667 if (states.nbstates <= 0) {
668 xmlFree(states.states);
669 return(0);
670 }
671 states.nbstates--;
672 i = states.states[states.nbstates].step;
673 node = states.states[states.nbstates].node;
674#if 0
675 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
676#endif
677 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000678}
679
680/************************************************************************
681 * *
682 * Dedicated parser for templates *
683 * *
684 ************************************************************************/
685
686#define TODO \
687 xmlGenericError(xmlGenericErrorContext, \
688 "Unimplemented block at %s:%d\n", \
689 __FILE__, __LINE__);
690#define CUR (*ctxt->cur)
691#define SKIP(val) ctxt->cur += (val)
692#define NXT(val) ctxt->cur[(val)]
693#define CUR_PTR ctxt->cur
694
695#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000696 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000697
698#define CURRENT (*ctxt->cur)
699#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
700
701
702#define PUSH(op, val, val2) \
703 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
704
705#define XSLT_ERROR(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000706 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000707 ctxt->error = (X); return; }
708
709#define XSLT_ERROR0(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000710 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000711 ctxt->error = (X); return(0); }
712
713#if 0
714/**
715 * xmlPatScanLiteral:
716 * @ctxt: the XPath Parser context
717 *
718 * Parse an XPath Litteral:
719 *
720 * [29] Literal ::= '"' [^"]* '"'
721 * | "'" [^']* "'"
722 *
723 * Returns the Literal parsed or NULL
724 */
725
726static xmlChar *
727xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
728 const xmlChar *q, *cur;
729 xmlChar *ret = NULL;
730 int val, len;
731
732 SKIP_BLANKS;
733 if (CUR == '"') {
734 NEXT;
735 cur = q = CUR_PTR;
736 val = xmlStringCurrentChar(NULL, cur, &len);
737 while ((IS_CHAR(val)) && (val != '"')) {
738 cur += len;
739 val = xmlStringCurrentChar(NULL, cur, &len);
740 }
741 if (!IS_CHAR(val)) {
742 ctxt->error = 1;
743 return(NULL);
744 } else {
745 ret = xmlStrndup(q, cur - q);
746 }
747 cur += len;
748 CUR_PTR = cur;
749 } else if (CUR == '\'') {
750 NEXT;
751 cur = q = CUR_PTR;
752 val = xmlStringCurrentChar(NULL, cur, &len);
753 while ((IS_CHAR(val)) && (val != '\'')) {
754 cur += len;
755 val = xmlStringCurrentChar(NULL, cur, &len);
756 }
757 if (!IS_CHAR(val)) {
758 ctxt->error = 1;
759 return(NULL);
760 } else {
761 ret = xmlStrndup(q, cur - q);
762 }
763 cur += len;
764 CUR_PTR = cur;
765 } else {
766 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
767 ctxt->error = 1;
768 return(NULL);
769 }
770 return(ret);
771}
772#endif
773
774/**
775 * xmlPatScanName:
776 * @ctxt: the XPath Parser context
777 *
778 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
779 * CombiningChar | Extender
780 *
781 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
782 *
783 * [6] Names ::= Name (S Name)*
784 *
785 * Returns the Name parsed or NULL
786 */
787
788static xmlChar *
789xmlPatScanName(xmlPatParserContextPtr ctxt) {
790 const xmlChar *q, *cur;
791 xmlChar *ret = NULL;
792 int val, len;
793
794 SKIP_BLANKS;
795
796 cur = q = CUR_PTR;
797 val = xmlStringCurrentChar(NULL, cur, &len);
798 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
799 return(NULL);
800
801 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
802 (val == '.') || (val == '-') ||
803 (val == '_') ||
804 (IS_COMBINING(val)) ||
805 (IS_EXTENDER(val))) {
806 cur += len;
807 val = xmlStringCurrentChar(NULL, cur, &len);
808 }
809 ret = xmlStrndup(q, cur - q);
810 CUR_PTR = cur;
811 return(ret);
812}
813
814/**
815 * xmlPatScanNCName:
816 * @ctxt: the XPath Parser context
817 *
818 * Parses a non qualified name
819 *
820 * Returns the Name parsed or NULL
821 */
822
823static xmlChar *
824xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
825 const xmlChar *q, *cur;
826 xmlChar *ret = NULL;
827 int val, len;
828
829 SKIP_BLANKS;
830
831 cur = q = CUR_PTR;
832 val = xmlStringCurrentChar(NULL, cur, &len);
833 if (!IS_LETTER(val) && (val != '_'))
834 return(NULL);
835
836 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
837 (val == '.') || (val == '-') ||
838 (val == '_') ||
839 (IS_COMBINING(val)) ||
840 (IS_EXTENDER(val))) {
841 cur += len;
842 val = xmlStringCurrentChar(NULL, cur, &len);
843 }
844 ret = xmlStrndup(q, cur - q);
845 CUR_PTR = cur;
846 return(ret);
847}
848
849#if 0
850/**
851 * xmlPatScanQName:
852 * @ctxt: the XPath Parser context
853 * @prefix: the place to store the prefix
854 *
855 * Parse a qualified name
856 *
857 * Returns the Name parsed or NULL
858 */
859
860static xmlChar *
861xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
862 xmlChar *ret = NULL;
863
864 *prefix = NULL;
865 ret = xmlPatScanNCName(ctxt);
866 if (CUR == ':') {
867 *prefix = ret;
868 NEXT;
869 ret = xmlPatScanNCName(ctxt);
870 }
871 return(ret);
872}
873#endif
874
875/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000876 * xmlCompileAttributeTest:
877 * @ctxt: the compilation context
878 *
879 * Compile an attribute test.
880 */
881static void
882xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
883 xmlChar *token = NULL;
884 xmlChar *name = NULL;
885 xmlChar *URL = NULL;
886
887 name = xmlPatScanNCName(ctxt);
888 if (name == NULL) {
889 if (CUR == '*') {
890 PUSH(XML_OP_ATTR, NULL, NULL);
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +0000891 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000892 } else {
893 ERROR(NULL, NULL, NULL,
894 "xmlCompileAttributeTest : Name expected\n");
895 ctxt->error = 1;
896 }
897 return;
898 }
899 if (CUR == ':') {
900 int i;
901 xmlChar *prefix = name;
902
903 NEXT;
904 /*
905 * This is a namespace match
906 */
907 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000908 if ((prefix[0] == 'x') &&
909 (prefix[1] == 'm') &&
910 (prefix[2] == 'l') &&
911 (prefix[3] == 0)) {
912 URL = xmlStrdup(XML_XML_NAMESPACE);
913 } else {
914 for (i = 0;i < ctxt->nb_namespaces;i++) {
915 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
916 URL = xmlStrdup(ctxt->namespaces[2 * i]);
917 break;
918 }
919 }
920 if (i >= ctxt->nb_namespaces) {
921 ERROR5(NULL, NULL, NULL,
922 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
923 prefix);
924 ctxt->error = 1;
925 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000926 }
927 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000928 xmlFree(prefix);
929 if (token == NULL) {
930 if (CUR == '*') {
931 NEXT;
932 PUSH(XML_OP_ATTR, NULL, URL);
933 } else {
934 ERROR(NULL, NULL, NULL,
935 "xmlCompileAttributeTest : Name expected\n");
936 ctxt->error = 1;
937 goto error;
938 }
939 } else {
940 PUSH(XML_OP_ATTR, token, URL);
941 }
942 } else {
943 PUSH(XML_OP_ATTR, name, NULL);
944 }
945 return;
946error:
947 if (URL != NULL)
948 xmlFree(URL);
949 if (token != NULL)
950 xmlFree(token);
951}
952
953
954/**
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000955 * xmlCompileStepPattern:
956 * @ctxt: the compilation context
957 *
958 * Compile the Step Pattern and generates a precompiled
959 * form suitable for fast matching.
960 *
961 * [3] Step ::= '.' | NameTest
962 * [4] NameTest ::= QName | '*' | NCName ':' '*'
963 */
964
965static void
966xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
967 xmlChar *token = NULL;
968 xmlChar *name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000969 xmlChar *URL = NULL;
970
971 SKIP_BLANKS;
972 if (CUR == '.') {
973 NEXT;
974 PUSH(XML_OP_ELEM, NULL, NULL);
975 return;
976 }
977 name = xmlPatScanNCName(ctxt);
978 if (name == NULL) {
979 if (CUR == '*') {
980 NEXT;
981 PUSH(XML_OP_ALL, NULL, NULL);
982 return;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000983 } else if (CUR == '@') {
984 NEXT;
985 xmlCompileAttributeTest(ctxt);
986 if (ctxt->error != 0)
987 goto error;
988 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000989 } else {
990 ERROR(NULL, NULL, NULL,
991 "xmlCompileStepPattern : Name expected\n");
992 ctxt->error = 1;
993 return;
994 }
995 }
996 SKIP_BLANKS;
997 if (CUR == ':') {
998 NEXT;
999 if (CUR != ':') {
1000 xmlChar *prefix = name;
Daniel Veillardd4301ab2005-02-03 22:24:10 +00001001 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001002
1003 /*
1004 * This is a namespace match
1005 */
1006 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001007 if ((prefix[0] == 'x') &&
1008 (prefix[1] == 'm') &&
1009 (prefix[2] == 'l') &&
1010 (prefix[3] == 0)) {
1011 URL = xmlStrdup(XML_XML_NAMESPACE);
1012 } else {
1013 for (i = 0;i < ctxt->nb_namespaces;i++) {
1014 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1015 URL = xmlStrdup(ctxt->namespaces[2 * i]);
1016 break;
1017 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +00001018 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001019 if (i >= ctxt->nb_namespaces) {
1020 ERROR5(NULL, NULL, NULL,
1021 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1022 prefix);
1023 ctxt->error = 1;
1024 goto error;
1025 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001026 }
1027 xmlFree(prefix);
1028 if (token == NULL) {
1029 if (CUR == '*') {
1030 NEXT;
1031 PUSH(XML_OP_NS, URL, NULL);
1032 } else {
1033 ERROR(NULL, NULL, NULL,
1034 "xmlCompileStepPattern : Name expected\n");
1035 ctxt->error = 1;
1036 goto error;
1037 }
1038 } else {
1039 PUSH(XML_OP_ELEM, token, URL);
1040 }
1041 } else {
1042 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001043 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1044 xmlFree(name);
1045 name = xmlPatScanName(ctxt);
1046 if (name == NULL) {
1047 if (CUR == '*') {
1048 NEXT;
1049 PUSH(XML_OP_ALL, NULL, NULL);
1050 return;
1051 } else {
1052 ERROR(NULL, NULL, NULL,
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001053 "xmlCompileStepPattern : QName expected\n");
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001054 ctxt->error = 1;
1055 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001056 }
1057 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001058 if (CUR == ':') {
1059 xmlChar *prefix = name;
1060 int i;
1061
1062 NEXT;
1063 /*
1064 * This is a namespace match
1065 */
1066 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001067 if ((prefix[0] == 'x') &&
1068 (prefix[1] == 'm') &&
1069 (prefix[2] == 'l') &&
1070 (prefix[3] == 0)) {
1071 URL = xmlStrdup(XML_XML_NAMESPACE);
1072 } else {
1073 for (i = 0;i < ctxt->nb_namespaces;i++) {
1074 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1075 URL = xmlStrdup(ctxt->namespaces[2 * i]);
1076 break;
1077 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001078 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001079 if (i >= ctxt->nb_namespaces) {
1080 ERROR5(NULL, NULL, NULL,
1081 "xmlCompileStepPattern : no namespace bound "
1082 "to prefix %s\n", prefix);
1083 ctxt->error = 1;
1084 goto error;
1085 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001086 }
1087 xmlFree(prefix);
1088 if (token == NULL) {
1089 if (CUR == '*') {
1090 NEXT;
1091 PUSH(XML_OP_NS, URL, NULL);
1092 } else {
1093 ERROR(NULL, NULL, NULL,
1094 "xmlCompileStepPattern : Name expected\n");
1095 ctxt->error = 1;
1096 goto error;
1097 }
1098 } else {
1099 PUSH(XML_OP_CHILD, token, URL);
1100 }
1101 } else
1102 PUSH(XML_OP_CHILD, name, NULL);
1103 return;
1104 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1105 xmlFree(name);
1106 name = NULL;
1107 xmlCompileAttributeTest(ctxt);
1108 if (ctxt->error != 0)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001109 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001110 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001111 } else {
1112 ERROR(NULL, NULL, NULL,
1113 "xmlCompileStepPattern : 'child' or 'attribute' expected\n");
1114 ctxt->error = 1;
1115 goto error;
1116 }
Daniel Veillard0e460da2005-03-30 22:47:10 +00001117 /* NOT REACHED xmlFree(name); */
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001118 }
1119 } else if (CUR == '*') {
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001120 if (name != NULL) {
1121 ctxt->error = 1;
1122 goto error;
1123 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001124 NEXT;
1125 PUSH(XML_OP_ALL, token, NULL);
1126 } else {
1127 if (name == NULL) {
1128 ctxt->error = 1;
1129 goto error;
1130 }
1131 PUSH(XML_OP_ELEM, name, NULL);
1132 }
1133 return;
1134error:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001135 if (URL != NULL)
1136 xmlFree(URL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001137 if (token != NULL)
1138 xmlFree(token);
1139 if (name != NULL)
1140 xmlFree(name);
1141}
1142
1143/**
1144 * xmlCompilePathPattern:
1145 * @ctxt: the compilation context
1146 *
1147 * Compile the Path Pattern and generates a precompiled
1148 * form suitable for fast matching.
1149 *
1150 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1151 */
1152static void
1153xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1154 SKIP_BLANKS;
William M. Brack537f1172005-06-14 22:02:59 +00001155 if (CUR == '/') {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001156 ctxt->comp->flags |= PAT_FROM_ROOT;
William M. Brackea152c02005-06-09 18:12:28 +00001157 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001158 ctxt->comp->flags |= PAT_FROM_CUR;
1159 }
William M. Brackea152c02005-06-09 18:12:28 +00001160
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001161 if ((CUR == '/') && (NXT(1) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001162 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001163 NEXT;
1164 NEXT;
1165 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001166 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001167 NEXT;
1168 NEXT;
1169 NEXT;
1170 }
1171 if (CUR == '@') {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001172 NEXT;
1173 xmlCompileAttributeTest(ctxt);
1174 SKIP_BLANKS;
William M. Brackfbb619f2005-06-06 13:49:18 +00001175 if (CUR != 0) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001176 xmlCompileStepPattern(ctxt);
1177 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001178 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001179 if (CUR == '/') {
1180 PUSH(XML_OP_ROOT, NULL, NULL);
1181 NEXT;
1182 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001183 xmlCompileStepPattern(ctxt);
1184 SKIP_BLANKS;
1185 while (CUR == '/') {
William M. Brackfbb619f2005-06-06 13:49:18 +00001186 if (NXT(1) == '/') {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001187 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1188 NEXT;
1189 NEXT;
1190 SKIP_BLANKS;
1191 xmlCompileStepPattern(ctxt);
1192 } else {
1193 PUSH(XML_OP_PARENT, NULL, NULL);
1194 NEXT;
1195 SKIP_BLANKS;
William M. Brackfbb619f2005-06-06 13:49:18 +00001196 if (CUR != 0) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001197 xmlCompileStepPattern(ctxt);
1198 }
1199 }
1200 }
1201 }
Daniel Veillard56de87e2005-02-16 00:22:29 +00001202 if (CUR != 0) {
1203 ERROR5(NULL, NULL, NULL,
1204 "Failed to compile pattern %s\n", ctxt->base);
1205 ctxt->error = 1;
1206 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001207error:
1208 return;
1209}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001210
1211/************************************************************************
1212 * *
1213 * The streaming code *
1214 * *
1215 ************************************************************************/
1216
1217#ifdef DEBUG_STREAMING
1218static void
1219xmlDebugStreamComp(xmlStreamCompPtr stream) {
1220 int i;
1221
1222 if (stream == NULL) {
1223 printf("Stream: NULL\n");
1224 return;
1225 }
1226 printf("Stream: %d steps\n", stream->nbStep);
1227 for (i = 0;i < stream->nbStep;i++) {
1228 if (stream->steps[i].ns != NULL) {
1229 printf("{%s}", stream->steps[i].ns);
1230 }
1231 if (stream->steps[i].name == NULL) {
1232 printf("* ");
1233 } else {
1234 printf("%s ", stream->steps[i].name);
1235 }
1236 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1237 printf("root ");
1238 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1239 printf("// ");
1240 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1241 printf("final ");
1242 printf("\n");
1243 }
1244}
1245static void
1246xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1247 int i;
1248
1249 if (ctxt == NULL) {
1250 printf("Stream: NULL\n");
1251 return;
1252 }
1253 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1254 if (match)
1255 printf("matches\n");
1256 else
1257 printf("\n");
1258 for (i = 0;i < ctxt->nbState;i++) {
1259 if (ctxt->states[2 * i] < 0)
1260 printf(" %d: free\n", i);
1261 else {
1262 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1263 ctxt->states[(2 * i) + 1]);
1264 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1265 XML_STREAM_STEP_DESC)
1266 printf(" //\n");
1267 else
1268 printf("\n");
1269 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001270 }
1271}
1272#endif
1273/**
1274 * xmlNewStreamComp:
1275 * @size: the number of expected steps
1276 *
1277 * build a new compiled pattern for streaming
1278 *
1279 * Returns the new structure or NULL in case of error.
1280 */
1281static xmlStreamCompPtr
1282xmlNewStreamComp(int size) {
1283 xmlStreamCompPtr cur;
1284
1285 if (size < 4)
1286 size = 4;
1287
1288 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1289 if (cur == NULL) {
1290 ERROR(NULL, NULL, NULL,
1291 "xmlNewStreamComp: malloc failed\n");
1292 return(NULL);
1293 }
1294 memset(cur, 0, sizeof(xmlStreamComp));
1295 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1296 if (cur->steps == NULL) {
1297 xmlFree(cur);
1298 ERROR(NULL, NULL, NULL,
1299 "xmlNewStreamComp: malloc failed\n");
1300 return(NULL);
1301 }
1302 cur->nbStep = 0;
1303 cur->maxStep = size;
1304 return(cur);
1305}
1306
1307/**
1308 * xmlFreeStreamComp:
1309 * @comp: the compiled pattern for streaming
1310 *
1311 * Free the compiled pattern for streaming
1312 */
1313static void
1314xmlFreeStreamComp(xmlStreamCompPtr comp) {
1315 if (comp != NULL) {
1316 if (comp->steps != NULL)
1317 xmlFree(comp->steps);
1318 if (comp->dict != NULL)
1319 xmlDictFree(comp->dict);
1320 xmlFree(comp);
1321 }
1322}
1323
1324/**
1325 * xmlStreamCompAddStep:
1326 * @comp: the compiled pattern for streaming
1327 * @name: the first string, the name, or NULL for *
1328 * @ns: the second step, the namespace name
1329 * @flags: the flags for that step
1330 *
1331 * Add a new step to the compiled pattern
1332 *
1333 * Returns -1 in case of error or the step index if successful
1334 */
1335static int
1336xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1337 const xmlChar *ns, int flags) {
1338 xmlStreamStepPtr cur;
1339
1340 if (comp->nbStep >= comp->maxStep) {
1341 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1342 comp->maxStep * 2 * sizeof(xmlStreamStep));
1343 if (cur == NULL) {
1344 ERROR(NULL, NULL, NULL,
1345 "xmlNewStreamComp: malloc failed\n");
1346 return(-1);
1347 }
1348 comp->steps = cur;
1349 comp->maxStep *= 2;
1350 }
1351 cur = &comp->steps[comp->nbStep++];
1352 cur->flags = flags;
1353 cur->name = name;
1354 cur->ns = ns;
1355 return(comp->nbStep - 1);
1356}
1357
1358/**
1359 * xmlStreamCompile:
1360 * @comp: the precompiled pattern
1361 *
1362 * Tries to stream compile a pattern
1363 *
1364 * Returns -1 in case of failure and 0 in case of success.
1365 */
1366static int
1367xmlStreamCompile(xmlPatternPtr comp) {
1368 xmlStreamCompPtr stream;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001369 int i, s = 0, root = 0, flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001370
1371 if ((comp == NULL) || (comp->steps == NULL))
1372 return(-1);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001373 /*
1374 * special case for .
1375 */
1376 if ((comp->nbStep == 1) &&
1377 (comp->steps[0].op == XML_OP_ELEM) &&
1378 (comp->steps[0].value == NULL) &&
1379 (comp->steps[0].value2 == NULL)) {
1380 stream = xmlNewStreamComp(0);
1381 if (stream == NULL)
1382 return(-1);
1383 comp->stream = stream;
1384 return(0);
1385 }
1386
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001387 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1388 if (stream == NULL)
1389 return(-1);
1390 if (comp->dict != NULL) {
1391 stream->dict = comp->dict;
1392 xmlDictReference(stream->dict);
1393 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001394
1395 /*
1396 * Skip leading ./ on relative paths
1397 */
1398 i = 0;
1399 while ((comp->flags & PAT_FROM_CUR) && (comp->nbStep > i + 2) &&
1400 (comp->steps[i].op == XML_OP_ELEM) &&
1401 (comp->steps[i].value == NULL) &&
1402 (comp->steps[i].value2 == NULL) &&
1403 (comp->steps[i + 1].op == XML_OP_PARENT)) {
1404 i += 2;
1405 }
1406 for (;i < comp->nbStep;i++) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001407 switch (comp->steps[i].op) {
1408 case XML_OP_END:
1409 break;
1410 case XML_OP_ROOT:
1411 if (i != 0)
1412 goto error;
1413 root = 1;
1414 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001415 case XML_OP_NS:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001416 s = xmlStreamCompAddStep(stream, NULL,
1417 comp->steps[i].value, flags);
1418 flags = 0;
1419 if (s < 0)
1420 goto error;
1421 break;
1422 case XML_OP_ATTR:
1423 flags |= XML_STREAM_STEP_ATTR;
1424 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1425 comp->steps[i].value2, flags);
1426 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001427 if (s < 0)
1428 goto error;
1429 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001430 case XML_OP_ELEM:
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001431 if ((comp->steps[i].value == NULL) &&
1432 (comp->steps[i].value2 == NULL) &&
1433 (comp->nbStep > i + 2) &&
1434 (comp->steps[i + 1].op == XML_OP_PARENT)) {
1435 i++;
1436 continue;
1437 }
1438 case XML_OP_CHILD:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001439 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1440 comp->steps[i].value2, flags);
1441 flags = 0;
1442 if (s < 0)
1443 goto error;
1444 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001445 case XML_OP_ALL:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001446 s = xmlStreamCompAddStep(stream, NULL, NULL, flags);
1447 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001448 if (s < 0)
1449 goto error;
1450 break;
1451 case XML_OP_PARENT:
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001452 if ((comp->nbStep > i + 1) &&
1453 (comp->steps[i + 1].op == XML_OP_ELEM) &&
1454 (comp->steps[i + 1].value == NULL) &&
1455 (comp->steps[i + 1].value2 == NULL)) {
1456 i++;
1457 continue;
1458 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001459 break;
1460 case XML_OP_ANCESTOR:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001461 flags |= XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001462 /*
1463 * Mark the expression as having "//".
1464 */
1465 if ((stream->flags & XML_STREAM_DESC) == 0)
1466 stream->flags |= XML_STREAM_DESC;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001467 break;
1468 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001469 }
1470 if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1471 /*
1472 * If this should behave like a real pattern, we will mark
1473 * the first step as having "//", to be reentrant on every
1474 * tree level.
1475 */
1476 if ((stream->flags & XML_STREAM_DESC) == 0)
1477 stream->flags |= XML_STREAM_DESC;
1478
1479 if (stream->nbStep > 0) {
1480 if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0)
1481 stream->steps[0].flags |= XML_STREAM_STEP_DESC;
1482 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001483 }
1484 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1485 if (root)
1486 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1487#ifdef DEBUG_STREAMING
1488 xmlDebugStreamComp(stream);
1489#endif
1490 comp->stream = stream;
1491 return(0);
1492error:
1493 xmlFreeStreamComp(stream);
1494 return(0);
1495}
1496
1497/**
1498 * xmlNewStreamCtxt:
1499 * @size: the number of expected states
1500 *
1501 * build a new stream context
1502 *
1503 * Returns the new structure or NULL in case of error.
1504 */
1505static xmlStreamCtxtPtr
1506xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1507 xmlStreamCtxtPtr cur;
1508
1509 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1510 if (cur == NULL) {
1511 ERROR(NULL, NULL, NULL,
1512 "xmlNewStreamCtxt: malloc failed\n");
1513 return(NULL);
1514 }
1515 memset(cur, 0, sizeof(xmlStreamCtxt));
1516 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1517 if (cur->states == NULL) {
1518 xmlFree(cur);
1519 ERROR(NULL, NULL, NULL,
1520 "xmlNewStreamCtxt: malloc failed\n");
1521 return(NULL);
1522 }
1523 cur->nbState = 0;
1524 cur->maxState = 4;
1525 cur->level = 0;
1526 cur->comp = stream;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001527 cur->blockLevel = -1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001528 return(cur);
1529}
1530
1531/**
1532 * xmlFreeStreamCtxt:
1533 * @stream: the stream context
1534 *
1535 * Free the stream context
1536 */
1537void
1538xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001539 xmlStreamCtxtPtr next;
1540
1541 while (stream != NULL) {
1542 next = stream->next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001543 if (stream->states != NULL)
1544 xmlFree(stream->states);
1545 xmlFree(stream);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001546 stream = next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001547 }
1548}
1549
1550/**
1551 * xmlStreamCtxtAddState:
1552 * @comp: the stream context
1553 * @idx: the step index for that streaming state
1554 *
1555 * Add a new state to the stream context
1556 *
1557 * Returns -1 in case of error or the state index if successful
1558 */
1559static int
1560xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1561 int i;
1562 for (i = 0;i < comp->nbState;i++) {
1563 if (comp->states[2 * i] < 0) {
1564 comp->states[2 * i] = idx;
1565 comp->states[2 * i + 1] = level;
1566 return(i);
1567 }
1568 }
1569 if (comp->nbState >= comp->maxState) {
1570 int *cur;
1571
1572 cur = (int *) xmlRealloc(comp->states,
1573 comp->maxState * 4 * sizeof(int));
1574 if (cur == NULL) {
1575 ERROR(NULL, NULL, NULL,
1576 "xmlNewStreamCtxt: malloc failed\n");
1577 return(-1);
1578 }
1579 comp->states = cur;
1580 comp->maxState *= 2;
1581 }
1582 comp->states[2 * comp->nbState] = idx;
1583 comp->states[2 * comp->nbState++ + 1] = level;
1584 return(comp->nbState - 1);
1585}
1586
1587/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001588 * xmlStreamPushInternal:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001589 * @stream: the stream context
1590 * @name: the current name
1591 * @ns: the namespace name
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001592 * @nodeType: the type of the node
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001593 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001594 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1595 * indicated a dictionary, then strings for name and ns will be expected
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001596 * to come from the dictionary.
1597 * Both @name and @ns being NULL means the / i.e. the root of the document.
1598 * This can also act as a reset.
1599 *
1600 * Returns: -1 in case of error, 1 if the current state in the stream is a
1601 * match and 0 otherwise.
1602 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001603static int
1604xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1605 const xmlChar *name, const xmlChar *ns,
1606 xmlElementType nodeType) {
William M. Brack537f1172005-06-14 22:02:59 +00001607 int ret = 0, err = 0, final = 0, tmp, i, m, match, step, desc;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001608 xmlStreamCompPtr comp;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001609#ifdef DEBUG_STREAMING
1610 xmlStreamCtxtPtr orig = stream;
1611#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001612
1613 if ((stream == NULL) || (stream->nbState < 0))
1614 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001615
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001616 while (stream != NULL) {
1617 comp = stream->comp;
1618 if ((name == NULL) && (ns == NULL)) {
1619 stream->nbState = 0;
1620 stream->level = 0;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001621 stream->blockLevel = -1;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001622 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1623 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1624 if (tmp < 0)
1625 err++;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001626 if (comp->nbStep == 0)
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001627 ret = 1;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001628 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001629 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001630 continue; /* while */
1631 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001632
1633 /*
1634 * Fast check for ".".
1635 */
1636 if (comp->nbStep == 0) {
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001637 /*
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00001638 * / and . are handled at the XPath node set creation
1639 * level by checking min depth
1640 */
1641 if (stream->flags & XML_PATTERN_XPATH) {
1642 stream = stream->next;
1643 continue; /* while */
1644 }
1645 /*
William M. Brackea152c02005-06-09 18:12:28 +00001646 * For non-pattern like evaluation like XML Schema IDCs
1647 * or traditional XPath expressions, this will match if
1648 * we are at the first level only, otherwise on every level.
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001649 */
1650 if ((nodeType == XML_ELEMENT_NODE) &&
1651 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1652 (stream->level == 0))) {
1653 ret = 1;
1654 }
1655 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001656 goto stream_next;
1657 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001658 if (stream->blockLevel != -1) {
1659 /*
1660 * Skip blocked expressions.
1661 */
1662 stream->level++;
1663 goto stream_next;
William M. Brackea152c02005-06-09 18:12:28 +00001664 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001665 /*
1666 * Check evolution of existing states
1667 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001668 i = 0;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001669 m = stream->nbState;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001670 while (i < m) {
1671 if ((comp->flags & XML_STREAM_DESC) == 0) {
1672 /*
1673 * If there is no "//", then only the last
1674 * added state is of interest.
1675 */
1676 step = stream->states[2 * (stream->nbState -1)];
1677 /*
1678 * TODO: Security check, should not happen, remove it.
1679 */
1680 if (stream->states[(2 * (stream->nbState -1)) + 1] <
1681 stream->level) {
1682 return (-1);
1683 }
1684 desc = 0;
1685 /* loop-stopper */
1686 i = m;
1687 } else {
1688 /*
1689 * If there are "//", then we need to process every "//"
1690 * occuring in the states, plus any other state for this
1691 * level.
1692 */
1693 step = stream->states[2 * i];
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001694
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001695 /* TODO: should not happen anymore: dead states */
1696 if (step < 0)
1697 goto next_state;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001698
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001699 tmp = stream->states[(2 * i) + 1];
1700
1701 /* skip new states just added */
1702 if (tmp > stream->level)
1703 goto next_state;
1704
1705 /* skip states at ancestor levels, except if "//" */
1706 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1707 if ((tmp < stream->level) && (!desc))
1708 goto next_state;
1709 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001710 /*
1711 * Check for correct node-type.
1712 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001713 if ((nodeType == XML_ATTRIBUTE_NODE) &&
Kasimier T. Buchcik8798b732005-07-08 19:58:37 +00001714 ((comp->steps[step].flags & XML_STREAM_STEP_ATTR) == 0))
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001715 goto next_state;
1716 /*
1717 * Compare local/namespace-name.
1718 */
1719 match = 0;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001720 if (comp->dict) {
1721 if (comp->steps[step].name == NULL) {
1722 if (comp->steps[step].ns == NULL)
1723 match = 1;
1724 else
1725 match = (comp->steps[step].ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001726 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001727 match = ((comp->steps[step].name == name) &&
1728 (comp->steps[step].ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001729 }
1730 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001731 if (comp->steps[step].name == NULL) {
1732 if (comp->steps[step].ns == NULL)
1733 match = 1;
1734 else
1735 match = xmlStrEqual(comp->steps[step].ns, ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001736 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001737 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
1738 (xmlStrEqual(comp->steps[step].ns, ns)));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001739 }
1740 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001741 if (match) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001742 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
1743 if (desc) {
1744 if (final) {
1745 ret = 1;
1746 } else {
1747 /* descending match create a new state */
1748 xmlStreamCtxtAddState(stream, step + 1,
1749 stream->level + 1);
1750 }
1751 } else {
1752 if (final) {
1753 ret = 1;
1754 } else {
1755 xmlStreamCtxtAddState(stream, step + 1,
1756 stream->level + 1);
1757 }
1758 }
1759 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001760 if (((comp->flags & XML_STREAM_DESC) == 0) &&
1761 ((! match) || final)) {
1762 /*
1763 * Mark this expression as blocked for any evaluation at
1764 * deeper levels. Note that this includes "/foo"
1765 * expressions if the *pattern* behaviour is used.
1766 */
1767 stream->blockLevel = stream->level +1;
1768 }
1769next_state:
1770 i++;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001771 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001772
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001773 stream->level++;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001774
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001775 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001776 * Re/enter the expression.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001777 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001778 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT)
1779 goto stream_next;
1780
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001781 desc = comp->steps[0].flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001782 if (stream->flags & XML_PATTERN_NOTPATTERN) {
1783 /*
1784 * Re/enter the expression if it is a "descendant" one,
1785 * or if we are at the 1st level of evaluation.
1786 */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001787
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001788 if (stream->level == 1) {
1789 if (XML_STREAM_XS_IDC(stream)) {
William M. Brackea152c02005-06-09 18:12:28 +00001790 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001791 * XS-IDC: The missing "self::node()" will always
1792 * match the first given node.
1793 */
William M. Brackea152c02005-06-09 18:12:28 +00001794 goto stream_next;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001795 } else
1796 goto compare;
1797 }
1798 /*
1799 * A "//" is always reentrant.
1800 */
1801 if (desc)
1802 goto compare;
1803
1804 /*
1805 * XS-IDC: Process the 2nd level, since the missing
1806 * "self::node()" is responsible for the 2nd level being
1807 * the real start level.
1808 */
1809 if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
1810 goto compare;
1811
1812 goto stream_next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001813 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001814
1815compare:
1816 /*
1817 * Check expected node-type.
1818 */
1819 if ((nodeType == XML_ATTRIBUTE_NODE) &&
1820 ((comp->steps[0].flags & XML_STREAM_STEP_ATTR) == 0))
1821 goto stream_next;
1822 /*
1823 * Compare local/namespace-name.
1824 */
1825 match = 0;
1826 if (comp->steps[0].name == NULL) {
1827 if (comp->steps[0].ns == NULL)
1828 match = 1;
1829 else {
1830 if (comp->dict)
1831 match = (comp->steps[0].ns == ns);
1832 else
1833 match = xmlStrEqual(comp->steps[0].ns, ns);
1834 }
1835 } else {
1836 if (comp->dict)
1837 match = ((comp->steps[0].name == name) &&
1838 (comp->steps[0].ns == ns));
1839 else
1840 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1841 (xmlStrEqual(comp->steps[0].ns, ns)));
1842 }
1843 if (match) {
1844 final = comp->steps[0].flags & XML_STREAM_STEP_FINAL;
1845 if (final)
1846 ret = 1;
1847 else
1848 xmlStreamCtxtAddState(stream, 1, stream->level);
1849 }
1850 if (((comp->flags & XML_STREAM_DESC) == 0) &&
1851 ((! match) || final)) {
1852 /*
1853 * Mark this expression as blocked for any evaluation at
1854 * deeper levels.
1855 */
1856 stream->blockLevel = stream->level;
1857 }
1858
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001859stream_next:
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001860 stream = stream->next;
1861 } /* while stream != NULL */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001862
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001863 if (err > 0)
1864 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001865#ifdef DEBUG_STREAMING
1866 xmlDebugStreamCtxt(orig, ret);
1867#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001868 return(ret);
1869}
1870
1871/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001872 * xmlStreamPush:
1873 * @stream: the stream context
1874 * @name: the current name
1875 * @ns: the namespace name
1876 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001877 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1878 * indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001879 * to come from the dictionary.
1880 * Both @name and @ns being NULL means the / i.e. the root of the document.
1881 * This can also act as a reset.
1882 *
1883 * Returns: -1 in case of error, 1 if the current state in the stream is a
1884 * match and 0 otherwise.
1885 */
1886int
1887xmlStreamPush(xmlStreamCtxtPtr stream,
1888 const xmlChar *name, const xmlChar *ns) {
1889 return (xmlStreamPushInternal(stream, name, ns, XML_ELEMENT_NODE));
1890}
1891
1892/**
1893* xmlStreamPushAttr:
1894* @stream: the stream context
1895* @name: the current name
1896* @ns: the namespace name
1897*
William M. Brackfbb619f2005-06-06 13:49:18 +00001898* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
1899* indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001900* to come from the dictionary.
1901* Both @name and @ns being NULL means the / i.e. the root of the document.
1902* This can also act as a reset.
1903*
1904* Returns: -1 in case of error, 1 if the current state in the stream is a
1905* match and 0 otherwise.
1906*/
1907int
1908xmlStreamPushAttr(xmlStreamCtxtPtr stream,
1909 const xmlChar *name, const xmlChar *ns) {
1910 return (xmlStreamPushInternal(stream, name, ns, XML_ATTRIBUTE_NODE));
1911}
1912
1913/**
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001914 * xmlStreamPop:
1915 * @stream: the stream context
1916 *
1917 * push one level from the stream.
1918 *
1919 * Returns: -1 in case of error, 0 otherwise.
1920 */
1921int
1922xmlStreamPop(xmlStreamCtxtPtr stream) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001923 int i, lev;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001924 int ret;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001925
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001926 if (stream == NULL)
1927 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001928 ret = 0;
1929 while (stream != NULL) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001930 /*
1931 * Reset block-level.
1932 */
1933 if (stream->blockLevel == stream->level)
1934 stream->blockLevel = -1;
1935
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001936 stream->level--;
1937 if (stream->level < 0)
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001938 ret = -1;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001939 /*
1940 * Check evolution of existing states
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001941 */
1942 for (i = stream->nbState -1; i >= 0; i--) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001943 /* discard obsoleted states */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001944 lev = stream->states[(2 * i) + 1];
1945 if (lev > stream->level)
1946 stream->nbState--;
1947 if (lev <= stream->level)
1948 break;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001949 }
1950 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001951 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001952 return(0);
1953}
1954
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001955/************************************************************************
1956 * *
1957 * The public interfaces *
1958 * *
1959 ************************************************************************/
1960
1961/**
1962 * xmlPatterncompile:
1963 * @pattern: the pattern to compile
William M. Brackfbb619f2005-06-06 13:49:18 +00001964 * @dict: an optional dictionary for interned strings
Daniel Veillarded6c5492005-07-23 15:00:22 +00001965 * @flags: compilation flags, see xmlPatternFlags
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001966 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001967 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001968 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001969 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001970 * Returns the compiled form of the pattern or NULL in case of error
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001971 */
1972xmlPatternPtr
Daniel Veillarded6c5492005-07-23 15:00:22 +00001973xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001974 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001975 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001976 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001977 const xmlChar *or, *start;
1978 xmlChar *tmp = NULL;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001979 int type = 0;
1980 int streamable = 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001981
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001982 if (pattern == NULL)
1983 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001984
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001985 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001986 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001987 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001988 tmp = NULL;
1989 while ((*or != 0) && (*or != '|')) or++;
1990 if (*or == 0)
1991 ctxt = xmlNewPatParserContext(start, dict, namespaces);
1992 else {
1993 tmp = xmlStrndup(start, or - start);
1994 if (tmp != NULL) {
1995 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
1996 }
1997 or++;
1998 }
1999 if (ctxt == NULL) goto error;
2000 cur = xmlNewPattern();
2001 if (cur == NULL) goto error;
2002 if (ret == NULL)
2003 ret = cur;
2004 else {
2005 cur->next = ret->next;
2006 ret->next = cur;
2007 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002008 cur->flags = flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002009 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002010
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002011 xmlCompilePathPattern(ctxt);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002012 if (ctxt->error != 0)
2013 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002014 xmlFreePatParserContext(ctxt);
William M. Brackfbb619f2005-06-06 13:49:18 +00002015 ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002016
2017
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002018 if (streamable) {
2019 if (type == 0) {
2020 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2021 } else if (type == PAT_FROM_ROOT) {
2022 if (cur->flags & PAT_FROM_CUR)
2023 streamable = 0;
2024 } else if (type == PAT_FROM_CUR) {
2025 if (cur->flags & PAT_FROM_ROOT)
2026 streamable = 0;
2027 }
2028 }
2029 if (streamable)
2030 xmlStreamCompile(cur);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002031 if (xmlReversePattern(cur) < 0)
2032 goto error;
William M. Brackea152c02005-06-09 18:12:28 +00002033 if (tmp != NULL) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002034 xmlFree(tmp);
William M. Brackea152c02005-06-09 18:12:28 +00002035 tmp = NULL;
2036 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002037 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002038 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002039 if (streamable == 0) {
2040 cur = ret;
2041 while (cur != NULL) {
2042 if (cur->stream != NULL) {
2043 xmlFreeStreamComp(cur->stream);
2044 cur->stream = NULL;
2045 }
2046 cur = cur->next;
2047 }
2048 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002049
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002050 return(ret);
2051error:
2052 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2053 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002054 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002055 return(NULL);
2056}
2057
2058/**
2059 * xmlPatternMatch:
2060 * @comp: the precompiled pattern
2061 * @node: a node
2062 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002063 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002064 *
2065 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2066 */
2067int
2068xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2069{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002070 int ret = 0;
2071
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002072 if ((comp == NULL) || (node == NULL))
2073 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002074
2075 while (comp != NULL) {
2076 ret = xmlPatMatch(comp, node);
2077 if (ret != 0)
2078 return(ret);
2079 comp = comp->next;
2080 }
2081 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002082}
2083
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002084/**
2085 * xmlPatternGetStreamCtxt:
2086 * @comp: the precompiled pattern
2087 *
2088 * Get a streaming context for that pattern
2089 * Use xmlFreeStreamCtxt to free the context.
2090 *
2091 * Returns a pointer to the context or NULL in case of failure
2092 */
2093xmlStreamCtxtPtr
2094xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2095{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002096 xmlStreamCtxtPtr ret = NULL, cur;
2097
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002098 if ((comp == NULL) || (comp->stream == NULL))
2099 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002100
2101 while (comp != NULL) {
2102 if (comp->stream == NULL)
2103 goto failed;
2104 cur = xmlNewStreamCtxt(comp->stream);
2105 if (cur == NULL)
2106 goto failed;
2107 if (ret == NULL)
2108 ret = cur;
2109 else {
2110 cur->next = ret->next;
2111 ret->next = cur;
2112 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002113 cur->flags = comp->flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002114 comp = comp->next;
2115 }
2116 return(ret);
2117failed:
2118 xmlFreeStreamCtxt(ret);
2119 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002120}
2121
Daniel Veillard56de87e2005-02-16 00:22:29 +00002122/**
2123 * xmlPatternStreamable:
2124 * @comp: the precompiled pattern
2125 *
2126 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2127 * should work.
2128 *
2129 * Returns 1 if streamable, 0 if not and -1 in case of error.
2130 */
2131int
2132xmlPatternStreamable(xmlPatternPtr comp) {
2133 if (comp == NULL)
2134 return(-1);
2135 while (comp != NULL) {
2136 if (comp->stream == NULL)
2137 return(0);
2138 comp = comp->next;
2139 }
2140 return(1);
2141}
2142
2143/**
2144 * xmlPatternMaxDepth:
2145 * @comp: the precompiled pattern
2146 *
2147 * Check the maximum depth reachable by a pattern
2148 *
2149 * Returns -2 if no limit (using //), otherwise the depth,
2150 * and -1 in case of error
2151 */
2152int
2153xmlPatternMaxDepth(xmlPatternPtr comp) {
2154 int ret = 0, i;
2155 if (comp == NULL)
2156 return(-1);
2157 while (comp != NULL) {
2158 if (comp->stream == NULL)
2159 return(-1);
2160 for (i = 0;i < comp->stream->nbStep;i++)
2161 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2162 return(-2);
2163 if (comp->stream->nbStep > ret)
2164 ret = comp->stream->nbStep;
2165 comp = comp->next;
2166 }
2167 return(ret);
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002168}
Daniel Veillard56de87e2005-02-16 00:22:29 +00002169
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002170/**
2171 * xmlPatternMinDepth:
2172 * @comp: the precompiled pattern
2173 *
2174 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2175 * part of the set.
2176 *
2177 * Returns -1 in case of error otherwise the depth,
2178 *
2179 */
2180int
2181xmlPatternMinDepth(xmlPatternPtr comp) {
2182 int ret = 12345678;
2183 if (comp == NULL)
2184 return(-1);
2185 while (comp != NULL) {
2186 if (comp->stream == NULL)
2187 return(-1);
2188 if (comp->stream->nbStep < ret)
2189 ret = comp->stream->nbStep;
2190 if (ret == 0)
2191 return(0);
2192 comp = comp->next;
2193 }
2194 return(ret);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002195}
2196
2197/**
2198 * xmlPatternFromRoot:
2199 * @comp: the precompiled pattern
2200 *
2201 * Check if the pattern must be looked at from the root.
2202 *
2203 * Returns 1 if true, 0 if false and -1 in case of error
2204 */
2205int
2206xmlPatternFromRoot(xmlPatternPtr comp) {
2207 if (comp == NULL)
2208 return(-1);
2209 while (comp != NULL) {
2210 if (comp->stream == NULL)
2211 return(-1);
2212 if (comp->flags & PAT_FROM_ROOT)
2213 return(1);
2214 comp = comp->next;
2215 }
2216 return(0);
2217
2218}
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002219
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002220#define bottom_pattern
2221#include "elfgcchack.h"
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002222#endif /* LIBXML_PATTERN_ENABLED */