blob: 8cd69ae93aa56abbd7acb8253107b6ca0574f121 [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
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +000023 * - DONE (2006-05-16): 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
Kasimier T. Buchcik97258712006-01-05 12:30:43 +000049#define XML_STREAM_STEP_NODE 16
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +000050#define XML_STREAM_STEP_IN_SET 32
Daniel Veillard2fc6df92005-01-30 18:42:55 +000051
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000052/*
Kasimier T. Buchcik97258712006-01-05 12:30:43 +000053* NOTE: Those private flags (XML_STREAM_xxx) are used
54* in _xmlStreamCtxt->flag. They extend the public
55* xmlPatternFlags, so be carefull not to interfere with the
56* reserved values for xmlPatternFlags.
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000057*/
Kasimier T. Buchcik97258712006-01-05 12:30:43 +000058#define XML_STREAM_FINAL_IS_ANY_NODE 1<<14
59#define XML_STREAM_FROM_ROOT 1<<15
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000060#define XML_STREAM_DESC 1<<16
61
Kasimier T. Buchcik97258712006-01-05 12:30:43 +000062/*
63* XML_STREAM_ANY_NODE is used for comparison against
64* xmlElementType enums, to indicate a node of any type.
65*/
66#define XML_STREAM_ANY_NODE 100
67
William M. Brackea152c02005-06-09 18:12:28 +000068#define XML_PATTERN_NOTPATTERN (XML_PATTERN_XPATH | \
69 XML_PATTERN_XSSEL | \
70 XML_PATTERN_XSFIELD)
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000071
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +000072#define XML_STREAM_XS_IDC(c) ((c)->flags & \
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000073 (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000074
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +000075#define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL)
76
77#define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD)
78
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +000079#define XML_PAT_COPY_NSNAME(c, r, nsname) \
80 if ((c)->comp->dict) \
81 r = (xmlChar *) xmlDictLookup((c)->comp->dict, BAD_CAST nsname, -1); \
82 else r = xmlStrdup(BAD_CAST nsname);
83
84#define XML_PAT_FREE_STRING(c, r) if ((c)->comp->dict == NULL) xmlFree(r);
85
Daniel Veillard2fc6df92005-01-30 18:42:55 +000086typedef struct _xmlStreamStep xmlStreamStep;
87typedef xmlStreamStep *xmlStreamStepPtr;
88struct _xmlStreamStep {
89 int flags; /* properties of that step */
90 const xmlChar *name; /* first string value if NULL accept all */
91 const xmlChar *ns; /* second string value */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +000092 int nodeType; /* type of node */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000093};
94
95typedef struct _xmlStreamComp xmlStreamComp;
96typedef xmlStreamComp *xmlStreamCompPtr;
97struct _xmlStreamComp {
William M. Brackfbb619f2005-06-06 13:49:18 +000098 xmlDict *dict; /* the dictionary if any */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000099 int nbStep; /* number of steps in the automata */
100 int maxStep; /* allocated number of steps */
101 xmlStreamStepPtr steps; /* the array of steps */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +0000102 int flags;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000103};
104
105struct _xmlStreamCtxt {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000106 struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000107 xmlStreamCompPtr comp; /* the compiled stream */
William M. Brackfbb619f2005-06-06 13:49:18 +0000108 int nbState; /* number of states in the automata */
109 int maxState; /* allocated number of states */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000110 int level; /* how deep are we ? */
111 int *states; /* the array of step indexes */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +0000112 int flags; /* validation options */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +0000113 int blockLevel;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000114};
115
116static void xmlFreeStreamComp(xmlStreamCompPtr comp);
117
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000118/*
119 * Types are private:
120 */
121
122typedef enum {
123 XML_OP_END=0,
124 XML_OP_ROOT,
125 XML_OP_ELEM,
126 XML_OP_CHILD,
127 XML_OP_ATTR,
128 XML_OP_PARENT,
129 XML_OP_ANCESTOR,
130 XML_OP_NS,
131 XML_OP_ALL
132} xmlPatOp;
133
134
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000135typedef struct _xmlStepState xmlStepState;
136typedef xmlStepState *xmlStepStatePtr;
137struct _xmlStepState {
138 int step;
139 xmlNodePtr node;
140};
141
142typedef struct _xmlStepStates xmlStepStates;
143typedef xmlStepStates *xmlStepStatesPtr;
144struct _xmlStepStates {
145 int nbstates;
146 int maxstates;
147 xmlStepStatePtr states;
148};
149
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000150typedef struct _xmlStepOp xmlStepOp;
151typedef xmlStepOp *xmlStepOpPtr;
152struct _xmlStepOp {
153 xmlPatOp op;
154 const xmlChar *value;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000155 const xmlChar *value2; /* The namespace name */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000156};
157
William M. Brackea152c02005-06-09 18:12:28 +0000158#define PAT_FROM_ROOT (1<<8)
159#define PAT_FROM_CUR (1<<9)
Daniel Veillard56de87e2005-02-16 00:22:29 +0000160
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000161struct _xmlPattern {
162 void *data; /* the associated template */
William M. Brackfbb619f2005-06-06 13:49:18 +0000163 xmlDictPtr dict; /* the optional dictionary */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000164 struct _xmlPattern *next; /* next pattern if | is used */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000165 const xmlChar *pattern; /* the pattern */
Daniel Veillardf5812c32005-09-03 13:43:20 +0000166 int flags; /* flags */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000167 int nbStep;
168 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000169 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000170 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000171};
172
173typedef struct _xmlPatParserContext xmlPatParserContext;
174typedef xmlPatParserContext *xmlPatParserContextPtr;
175struct _xmlPatParserContext {
176 const xmlChar *cur; /* the current char being parsed */
177 const xmlChar *base; /* the full expression */
178 int error; /* error code */
William M. Brackfbb619f2005-06-06 13:49:18 +0000179 xmlDictPtr dict; /* the dictionary if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000180 xmlPatternPtr comp; /* the result */
181 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000182 const xmlChar **namespaces; /* the namespaces definitions */
183 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000184};
185
186/************************************************************************
187 * *
188 * Type functions *
189 * *
190 ************************************************************************/
191
192/**
193 * xmlNewPattern:
194 *
195 * Create a new XSLT Pattern
196 *
197 * Returns the newly allocated xmlPatternPtr or NULL in case of error
198 */
199static xmlPatternPtr
200xmlNewPattern(void) {
201 xmlPatternPtr cur;
202
203 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
204 if (cur == NULL) {
205 ERROR(NULL, NULL, NULL,
206 "xmlNewPattern : malloc failed\n");
207 return(NULL);
208 }
209 memset(cur, 0, sizeof(xmlPattern));
210 cur->maxStep = 10;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000211 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
212 if (cur->steps == NULL) {
213 xmlFree(cur);
214 ERROR(NULL, NULL, NULL,
215 "xmlNewPattern : malloc failed\n");
216 return(NULL);
217 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000218 return(cur);
219}
220
221/**
222 * xmlFreePattern:
223 * @comp: an XSLT comp
224 *
225 * Free up the memory allocated by @comp
226 */
227void
228xmlFreePattern(xmlPatternPtr comp) {
229 xmlStepOpPtr op;
230 int i;
231
232 if (comp == NULL)
233 return;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000234 if (comp->next != NULL)
235 xmlFreePattern(comp->next);
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000236 if (comp->stream != NULL)
237 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000238 if (comp->pattern != NULL)
239 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000240 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000241 if (comp->dict == NULL) {
242 for (i = 0;i < comp->nbStep;i++) {
243 op = &comp->steps[i];
244 if (op->value != NULL)
245 xmlFree((xmlChar *) op->value);
246 if (op->value2 != NULL)
247 xmlFree((xmlChar *) op->value2);
248 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000249 }
250 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000251 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000252 if (comp->dict != NULL)
253 xmlDictFree(comp->dict);
254
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000255 memset(comp, -1, sizeof(xmlPattern));
256 xmlFree(comp);
257}
258
259/**
260 * xmlFreePatternList:
261 * @comp: an XSLT comp list
262 *
263 * Free up the memory allocated by all the elements of @comp
264 */
265void
266xmlFreePatternList(xmlPatternPtr comp) {
267 xmlPatternPtr cur;
268
269 while (comp != NULL) {
270 cur = comp;
271 comp = comp->next;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +0000272 cur->next = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000273 xmlFreePattern(cur);
274 }
275}
276
277/**
278 * xmlNewPatParserContext:
279 * @pattern: the pattern context
William M. Brackfbb619f2005-06-06 13:49:18 +0000280 * @dict: the inherited dictionary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000281 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
282 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000283 *
284 * Create a new XML pattern parser context
285 *
286 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
287 */
288static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000289xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
290 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000291 xmlPatParserContextPtr cur;
292
293 if (pattern == NULL)
294 return(NULL);
295
296 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
297 if (cur == NULL) {
298 ERROR(NULL, NULL, NULL,
299 "xmlNewPatParserContext : malloc failed\n");
300 return(NULL);
301 }
302 memset(cur, 0, sizeof(xmlPatParserContext));
303 cur->dict = dict;
304 cur->cur = pattern;
305 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000306 if (namespaces != NULL) {
307 int i;
Nico Webercedf84d2012-03-05 16:36:59 +0800308 for (i = 0;namespaces[2 * i] != NULL;i++)
309 ;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000310 cur->nb_namespaces = i;
311 } else {
312 cur->nb_namespaces = 0;
313 }
314 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000315 return(cur);
316}
317
318/**
319 * xmlFreePatParserContext:
320 * @ctxt: an XSLT parser context
321 *
322 * Free up the memory allocated by @ctxt
323 */
324static void
325xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
326 if (ctxt == NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000327 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000328 memset(ctxt, -1, sizeof(xmlPatParserContext));
329 xmlFree(ctxt);
330}
331
332/**
333 * xmlPatternAdd:
334 * @comp: the compiled match expression
335 * @op: an op
336 * @value: the first value
337 * @value2: the second value
338 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000339 * Add a step to an XSLT Compiled Match
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000340 *
341 * Returns -1 in case of failure, 0 otherwise.
342 */
343static int
344xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
345 xmlPatternPtr comp,
346 xmlPatOp op, xmlChar * value, xmlChar * value2)
347{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000348 if (comp->nbStep >= comp->maxStep) {
349 xmlStepOpPtr temp;
350 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
351 sizeof(xmlStepOp));
352 if (temp == NULL) {
353 ERROR(ctxt, NULL, NULL,
354 "xmlPatternAdd: realloc failed\n");
355 return (-1);
356 }
357 comp->steps = temp;
358 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000359 }
360 comp->steps[comp->nbStep].op = op;
361 comp->steps[comp->nbStep].value = value;
362 comp->steps[comp->nbStep].value2 = value2;
363 comp->nbStep++;
364 return (0);
365}
366
367#if 0
368/**
369 * xsltSwapTopPattern:
370 * @comp: the compiled match expression
371 *
372 * reverse the two top steps.
373 */
374static void
375xsltSwapTopPattern(xmlPatternPtr comp) {
376 int i;
377 int j = comp->nbStep - 1;
378
379 if (j > 0) {
380 register const xmlChar *tmp;
381 register xmlPatOp op;
382 i = j - 1;
383 tmp = comp->steps[i].value;
384 comp->steps[i].value = comp->steps[j].value;
385 comp->steps[j].value = tmp;
386 tmp = comp->steps[i].value2;
387 comp->steps[i].value2 = comp->steps[j].value2;
388 comp->steps[j].value2 = tmp;
389 op = comp->steps[i].op;
390 comp->steps[i].op = comp->steps[j].op;
391 comp->steps[j].op = op;
392 }
393}
394#endif
395
396/**
397 * xmlReversePattern:
398 * @comp: the compiled match expression
399 *
400 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000401 *
402 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000403 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000404static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000405xmlReversePattern(xmlPatternPtr comp) {
Daniel Veillard56de87e2005-02-16 00:22:29 +0000406 int i, j;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000407
Daniel Veillard56de87e2005-02-16 00:22:29 +0000408 /*
409 * remove the leading // for //a or .//a
410 */
411 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
412 for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
413 comp->steps[i].value = comp->steps[j].value;
414 comp->steps[i].value2 = comp->steps[j].value2;
415 comp->steps[i].op = comp->steps[j].op;
416 }
417 comp->nbStep--;
418 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000419 if (comp->nbStep >= comp->maxStep) {
420 xmlStepOpPtr temp;
421 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
422 sizeof(xmlStepOp));
423 if (temp == NULL) {
424 ERROR(ctxt, NULL, NULL,
425 "xmlReversePattern: realloc failed\n");
426 return (-1);
427 }
428 comp->steps = temp;
429 comp->maxStep *= 2;
430 }
Daniel Veillard56de87e2005-02-16 00:22:29 +0000431 i = 0;
432 j = comp->nbStep - 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000433 while (j > i) {
434 register const xmlChar *tmp;
435 register xmlPatOp op;
436 tmp = comp->steps[i].value;
437 comp->steps[i].value = comp->steps[j].value;
438 comp->steps[j].value = tmp;
439 tmp = comp->steps[i].value2;
440 comp->steps[i].value2 = comp->steps[j].value2;
441 comp->steps[j].value2 = tmp;
442 op = comp->steps[i].op;
443 comp->steps[i].op = comp->steps[j].op;
444 comp->steps[j].op = op;
445 j--;
446 i++;
447 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000448 comp->steps[comp->nbStep].value = NULL;
449 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000450 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000451 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000452}
453
454/************************************************************************
455 * *
456 * The interpreter for the precompiled patterns *
457 * *
458 ************************************************************************/
459
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000460static int
461xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
462 if ((states->states == NULL) || (states->maxstates <= 0)) {
463 states->maxstates = 4;
464 states->nbstates = 0;
465 states->states = xmlMalloc(4 * sizeof(xmlStepState));
466 }
467 else if (states->maxstates <= states->nbstates) {
468 xmlStepState *tmp;
469
470 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
471 2 * states->maxstates * sizeof(xmlStepState));
472 if (tmp == NULL)
473 return(-1);
474 states->states = tmp;
475 states->maxstates *= 2;
476 }
477 states->states[states->nbstates].step = step;
478 states->states[states->nbstates++].node = node;
479#if 0
480 fprintf(stderr, "Push: %d, %s\n", step, node->name);
481#endif
482 return(0);
483}
484
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000485/**
486 * xmlPatMatch:
487 * @comp: the precompiled pattern
488 * @node: a node
489 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000490 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000491 *
492 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
493 */
494static int
495xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
496 int i;
497 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000498 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000499
500 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000501 i = 0;
502restart:
503 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000504 step = &comp->steps[i];
505 switch (step->op) {
506 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000507 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000508 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000509 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000510 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000511 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000512 if ((node->type == XML_DOCUMENT_NODE) ||
513#ifdef LIBXML_DOCB_ENABLED
514 (node->type == XML_DOCB_DOCUMENT_NODE) ||
515#endif
516 (node->type == XML_HTML_DOCUMENT_NODE))
517 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000518 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000519 case XML_OP_ELEM:
520 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000521 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000522 if (step->value == NULL)
523 continue;
524 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000525 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000526 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000527 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000528
529 /* Namespace test */
530 if (node->ns == NULL) {
531 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000532 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000533 } else if (node->ns->href != NULL) {
534 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000535 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000536 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000537 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000538 }
539 continue;
540 case XML_OP_CHILD: {
541 xmlNodePtr lst;
542
543 if ((node->type != XML_ELEMENT_NODE) &&
544 (node->type != XML_DOCUMENT_NODE) &&
545#ifdef LIBXML_DOCB_ENABLED
546 (node->type != XML_DOCB_DOCUMENT_NODE) &&
547#endif
548 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000549 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000550
551 lst = node->children;
552
553 if (step->value != NULL) {
554 while (lst != NULL) {
555 if ((lst->type == XML_ELEMENT_NODE) &&
556 (step->value[0] == lst->name[0]) &&
557 (xmlStrEqual(step->value, lst->name)))
558 break;
559 lst = lst->next;
560 }
561 if (lst != NULL)
562 continue;
563 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000564 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000565 }
566 case XML_OP_ATTR:
567 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000568 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000569 if (step->value != NULL) {
570 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000571 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000572 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000573 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000574 }
575 /* Namespace test */
576 if (node->ns == NULL) {
577 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000578 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000579 } else if (step->value2 != NULL) {
580 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000581 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000582 }
583 continue;
584 case XML_OP_PARENT:
585 if ((node->type == XML_DOCUMENT_NODE) ||
586 (node->type == XML_HTML_DOCUMENT_NODE) ||
587#ifdef LIBXML_DOCB_ENABLED
588 (node->type == XML_DOCB_DOCUMENT_NODE) ||
589#endif
590 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000591 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000592 node = node->parent;
593 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000594 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000595 if (step->value == NULL)
596 continue;
597 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000598 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000599 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000600 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000601 /* Namespace test */
602 if (node->ns == NULL) {
603 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000604 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000605 } else if (node->ns->href != NULL) {
606 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000607 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000608 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000609 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000610 }
611 continue;
612 case XML_OP_ANCESTOR:
613 /* TODO: implement coalescing of ANCESTOR/NODE ops */
614 if (step->value == NULL) {
615 i++;
616 step = &comp->steps[i];
617 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000618 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000619 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000620 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000621 if (step->value == NULL)
622 return(-1);
623 }
624 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000625 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000626 if ((node->type == XML_DOCUMENT_NODE) ||
627 (node->type == XML_HTML_DOCUMENT_NODE) ||
628#ifdef LIBXML_DOCB_ENABLED
629 (node->type == XML_DOCB_DOCUMENT_NODE) ||
630#endif
631 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000632 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000633 node = node->parent;
634 while (node != NULL) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000635 if ((node->type == XML_ELEMENT_NODE) &&
636 (step->value[0] == node->name[0]) &&
637 (xmlStrEqual(step->value, node->name))) {
638 /* Namespace test */
639 if (node->ns == NULL) {
640 if (step->value2 == NULL)
641 break;
642 } else if (node->ns->href != NULL) {
643 if ((step->value2 != NULL) &&
644 (xmlStrEqual(step->value2, node->ns->href)))
645 break;
646 }
647 }
648 node = node->parent;
649 }
650 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000651 goto rollback;
652 /*
653 * prepare a potential rollback from here
654 * for ancestors of that node.
655 */
656 if (step->op == XML_OP_ANCESTOR)
657 xmlPatPushState(&states, i, node);
658 else
659 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000660 continue;
661 case XML_OP_NS:
662 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000663 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000664 if (node->ns == NULL) {
665 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000666 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000667 } else if (node->ns->href != NULL) {
668 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000669 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000670 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000671 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000672 }
673 break;
674 case XML_OP_ALL:
675 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000676 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000677 break;
678 }
679 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000680found:
681 if (states.states != NULL) {
682 /* Free the rollback states */
683 xmlFree(states.states);
684 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000685 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000686rollback:
687 /* got an error try to rollback */
688 if (states.states == NULL)
689 return(0);
690 if (states.nbstates <= 0) {
691 xmlFree(states.states);
692 return(0);
693 }
694 states.nbstates--;
695 i = states.states[states.nbstates].step;
696 node = states.states[states.nbstates].node;
697#if 0
698 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
699#endif
700 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000701}
702
703/************************************************************************
704 * *
705 * Dedicated parser for templates *
706 * *
707 ************************************************************************/
708
709#define TODO \
710 xmlGenericError(xmlGenericErrorContext, \
711 "Unimplemented block at %s:%d\n", \
712 __FILE__, __LINE__);
713#define CUR (*ctxt->cur)
714#define SKIP(val) ctxt->cur += (val)
715#define NXT(val) ctxt->cur[(val)]
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000716#define PEEKPREV(val) ctxt->cur[-(val)]
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000717#define CUR_PTR ctxt->cur
718
719#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000720 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000721
722#define CURRENT (*ctxt->cur)
723#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
724
725
726#define PUSH(op, val, val2) \
727 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
728
729#define XSLT_ERROR(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000730 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000731 ctxt->error = (X); return; }
732
733#define XSLT_ERROR0(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000734 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000735 ctxt->error = (X); return(0); }
736
737#if 0
738/**
739 * xmlPatScanLiteral:
740 * @ctxt: the XPath Parser context
741 *
742 * Parse an XPath Litteral:
743 *
744 * [29] Literal ::= '"' [^"]* '"'
745 * | "'" [^']* "'"
746 *
747 * Returns the Literal parsed or NULL
748 */
749
750static xmlChar *
751xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
752 const xmlChar *q, *cur;
753 xmlChar *ret = NULL;
754 int val, len;
755
756 SKIP_BLANKS;
757 if (CUR == '"') {
758 NEXT;
759 cur = q = CUR_PTR;
760 val = xmlStringCurrentChar(NULL, cur, &len);
761 while ((IS_CHAR(val)) && (val != '"')) {
762 cur += len;
763 val = xmlStringCurrentChar(NULL, cur, &len);
764 }
765 if (!IS_CHAR(val)) {
766 ctxt->error = 1;
767 return(NULL);
768 } else {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000769 if (ctxt->dict)
770 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
771 else
772 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000773 }
774 cur += len;
775 CUR_PTR = cur;
776 } else if (CUR == '\'') {
777 NEXT;
778 cur = q = CUR_PTR;
779 val = xmlStringCurrentChar(NULL, cur, &len);
780 while ((IS_CHAR(val)) && (val != '\'')) {
781 cur += len;
782 val = xmlStringCurrentChar(NULL, cur, &len);
783 }
784 if (!IS_CHAR(val)) {
785 ctxt->error = 1;
786 return(NULL);
787 } else {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000788 if (ctxt->dict)
789 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
790 else
791 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000792 }
793 cur += len;
794 CUR_PTR = cur;
795 } else {
796 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
797 ctxt->error = 1;
798 return(NULL);
799 }
800 return(ret);
801}
802#endif
803
804/**
805 * xmlPatScanName:
806 * @ctxt: the XPath Parser context
807 *
808 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
809 * CombiningChar | Extender
810 *
811 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
812 *
813 * [6] Names ::= Name (S Name)*
814 *
815 * Returns the Name parsed or NULL
816 */
817
818static xmlChar *
819xmlPatScanName(xmlPatParserContextPtr ctxt) {
820 const xmlChar *q, *cur;
821 xmlChar *ret = NULL;
822 int val, len;
823
824 SKIP_BLANKS;
825
826 cur = q = CUR_PTR;
827 val = xmlStringCurrentChar(NULL, cur, &len);
828 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
829 return(NULL);
830
831 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
832 (val == '.') || (val == '-') ||
833 (val == '_') ||
834 (IS_COMBINING(val)) ||
835 (IS_EXTENDER(val))) {
836 cur += len;
837 val = xmlStringCurrentChar(NULL, cur, &len);
838 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000839 if (ctxt->dict)
840 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
841 else
842 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000843 CUR_PTR = cur;
844 return(ret);
845}
846
847/**
848 * xmlPatScanNCName:
849 * @ctxt: the XPath Parser context
850 *
851 * Parses a non qualified name
852 *
853 * Returns the Name parsed or NULL
854 */
855
856static xmlChar *
857xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
858 const xmlChar *q, *cur;
859 xmlChar *ret = NULL;
860 int val, len;
861
862 SKIP_BLANKS;
863
864 cur = q = CUR_PTR;
865 val = xmlStringCurrentChar(NULL, cur, &len);
866 if (!IS_LETTER(val) && (val != '_'))
867 return(NULL);
868
869 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
870 (val == '.') || (val == '-') ||
871 (val == '_') ||
872 (IS_COMBINING(val)) ||
873 (IS_EXTENDER(val))) {
874 cur += len;
875 val = xmlStringCurrentChar(NULL, cur, &len);
876 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000877 if (ctxt->dict)
878 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
879 else
880 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000881 CUR_PTR = cur;
882 return(ret);
883}
884
885#if 0
886/**
887 * xmlPatScanQName:
888 * @ctxt: the XPath Parser context
889 * @prefix: the place to store the prefix
890 *
891 * Parse a qualified name
892 *
893 * Returns the Name parsed or NULL
894 */
895
896static xmlChar *
897xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
898 xmlChar *ret = NULL;
899
900 *prefix = NULL;
901 ret = xmlPatScanNCName(ctxt);
902 if (CUR == ':') {
903 *prefix = ret;
904 NEXT;
905 ret = xmlPatScanNCName(ctxt);
906 }
907 return(ret);
908}
909#endif
910
911/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000912 * xmlCompileAttributeTest:
913 * @ctxt: the compilation context
914 *
915 * Compile an attribute test.
916 */
917static void
918xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
919 xmlChar *token = NULL;
920 xmlChar *name = NULL;
921 xmlChar *URL = NULL;
922
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000923 SKIP_BLANKS;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000924 name = xmlPatScanNCName(ctxt);
925 if (name == NULL) {
926 if (CUR == '*') {
927 PUSH(XML_OP_ATTR, NULL, NULL);
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +0000928 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000929 } else {
930 ERROR(NULL, NULL, NULL,
931 "xmlCompileAttributeTest : Name expected\n");
932 ctxt->error = 1;
933 }
934 return;
935 }
936 if (CUR == ':') {
937 int i;
938 xmlChar *prefix = name;
939
940 NEXT;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000941
942 if (IS_BLANK_CH(CUR)) {
943 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000944 XML_PAT_FREE_STRING(ctxt, prefix);
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000945 ctxt->error = 1;
946 goto error;
947 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000948 /*
949 * This is a namespace match
950 */
951 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000952 if ((prefix[0] == 'x') &&
953 (prefix[1] == 'm') &&
954 (prefix[2] == 'l') &&
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000955 (prefix[3] == 0))
956 {
957 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000958 } else {
959 for (i = 0;i < ctxt->nb_namespaces;i++) {
960 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000961 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000962 break;
963 }
964 }
965 if (i >= ctxt->nb_namespaces) {
966 ERROR5(NULL, NULL, NULL,
967 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
968 prefix);
969 ctxt->error = 1;
970 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000971 }
972 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000973 XML_PAT_FREE_STRING(ctxt, prefix);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000974 if (token == NULL) {
975 if (CUR == '*') {
976 NEXT;
977 PUSH(XML_OP_ATTR, NULL, URL);
978 } else {
979 ERROR(NULL, NULL, NULL,
980 "xmlCompileAttributeTest : Name expected\n");
981 ctxt->error = 1;
982 goto error;
983 }
984 } else {
985 PUSH(XML_OP_ATTR, token, URL);
986 }
987 } else {
988 PUSH(XML_OP_ATTR, name, NULL);
989 }
990 return;
991error:
992 if (URL != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000993 XML_PAT_FREE_STRING(ctxt, URL)
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000994 if (token != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000995 XML_PAT_FREE_STRING(ctxt, token);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000996}
997
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000998/**
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000999 * xmlCompileStepPattern:
1000 * @ctxt: the compilation context
1001 *
1002 * Compile the Step Pattern and generates a precompiled
1003 * form suitable for fast matching.
1004 *
1005 * [3] Step ::= '.' | NameTest
1006 * [4] NameTest ::= QName | '*' | NCName ':' '*'
1007 */
1008
1009static void
1010xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
1011 xmlChar *token = NULL;
1012 xmlChar *name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001013 xmlChar *URL = NULL;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001014 int hasBlanks = 0;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001015
1016 SKIP_BLANKS;
1017 if (CUR == '.') {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001018 /*
1019 * Context node.
1020 */
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001021 NEXT;
1022 PUSH(XML_OP_ELEM, NULL, NULL);
1023 return;
1024 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001025 if (CUR == '@') {
1026 /*
1027 * Attribute test.
1028 */
1029 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1030 ERROR5(NULL, NULL, NULL,
1031 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1032 ctxt->error = 1;
1033 return;
1034 }
1035 NEXT;
1036 xmlCompileAttributeTest(ctxt);
1037 if (ctxt->error != 0)
1038 goto error;
1039 return;
1040 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001041 name = xmlPatScanNCName(ctxt);
1042 if (name == NULL) {
1043 if (CUR == '*') {
1044 NEXT;
1045 PUSH(XML_OP_ALL, NULL, NULL);
1046 return;
1047 } else {
1048 ERROR(NULL, NULL, NULL,
1049 "xmlCompileStepPattern : Name expected\n");
1050 ctxt->error = 1;
1051 return;
1052 }
1053 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001054 if (IS_BLANK_CH(CUR)) {
1055 hasBlanks = 1;
1056 SKIP_BLANKS;
1057 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001058 if (CUR == ':') {
1059 NEXT;
1060 if (CUR != ':') {
1061 xmlChar *prefix = name;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001062 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001063
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001064 if (hasBlanks || IS_BLANK_CH(CUR)) {
1065 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1066 ctxt->error = 1;
1067 goto error;
1068 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001069 /*
1070 * This is a namespace match
1071 */
1072 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001073 if ((prefix[0] == 'x') &&
1074 (prefix[1] == 'm') &&
1075 (prefix[2] == 'l') &&
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001076 (prefix[3] == 0))
1077 {
1078 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001079 } else {
1080 for (i = 0;i < ctxt->nb_namespaces;i++) {
1081 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001082 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001083 break;
1084 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +00001085 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001086 if (i >= ctxt->nb_namespaces) {
1087 ERROR5(NULL, NULL, NULL,
1088 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1089 prefix);
1090 ctxt->error = 1;
1091 goto error;
1092 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001093 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001094 XML_PAT_FREE_STRING(ctxt, prefix);
Rob Richards3108ba92007-12-06 10:08:52 +00001095 name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001096 if (token == NULL) {
1097 if (CUR == '*') {
1098 NEXT;
1099 PUSH(XML_OP_NS, URL, NULL);
1100 } else {
1101 ERROR(NULL, NULL, NULL,
1102 "xmlCompileStepPattern : Name expected\n");
1103 ctxt->error = 1;
1104 goto error;
1105 }
1106 } else {
1107 PUSH(XML_OP_ELEM, token, URL);
1108 }
1109 } else {
1110 NEXT;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001111 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1112 XML_PAT_FREE_STRING(ctxt, name);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001113 name = xmlPatScanName(ctxt);
1114 if (name == NULL) {
1115 if (CUR == '*') {
1116 NEXT;
1117 PUSH(XML_OP_ALL, NULL, NULL);
1118 return;
1119 } else {
1120 ERROR(NULL, NULL, NULL,
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001121 "xmlCompileStepPattern : QName expected\n");
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001122 ctxt->error = 1;
1123 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001124 }
1125 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001126 if (CUR == ':') {
1127 xmlChar *prefix = name;
1128 int i;
1129
1130 NEXT;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001131 if (IS_BLANK_CH(CUR)) {
1132 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1133 ctxt->error = 1;
1134 goto error;
1135 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001136 /*
1137 * This is a namespace match
1138 */
1139 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001140 if ((prefix[0] == 'x') &&
1141 (prefix[1] == 'm') &&
1142 (prefix[2] == 'l') &&
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001143 (prefix[3] == 0))
1144 {
1145 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001146 } else {
1147 for (i = 0;i < ctxt->nb_namespaces;i++) {
1148 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001149 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001150 break;
1151 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001152 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001153 if (i >= ctxt->nb_namespaces) {
1154 ERROR5(NULL, NULL, NULL,
1155 "xmlCompileStepPattern : no namespace bound "
1156 "to prefix %s\n", prefix);
1157 ctxt->error = 1;
1158 goto error;
1159 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001160 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001161 XML_PAT_FREE_STRING(ctxt, prefix);
Rob Richards3108ba92007-12-06 10:08:52 +00001162 name = NULL;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001163 if (token == NULL) {
1164 if (CUR == '*') {
1165 NEXT;
1166 PUSH(XML_OP_NS, URL, NULL);
1167 } else {
1168 ERROR(NULL, NULL, NULL,
1169 "xmlCompileStepPattern : Name expected\n");
1170 ctxt->error = 1;
1171 goto error;
1172 }
1173 } else {
1174 PUSH(XML_OP_CHILD, token, URL);
1175 }
1176 } else
1177 PUSH(XML_OP_CHILD, name, NULL);
1178 return;
1179 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001180 XML_PAT_FREE_STRING(ctxt, name)
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001181 name = NULL;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001182 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1183 ERROR5(NULL, NULL, NULL,
1184 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1185 ctxt->error = 1;
1186 goto error;
1187 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001188 xmlCompileAttributeTest(ctxt);
1189 if (ctxt->error != 0)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001190 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001191 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001192 } else {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001193 ERROR5(NULL, NULL, NULL,
1194 "The 'element' or 'attribute' axis is expected.\n", NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001195 ctxt->error = 1;
1196 goto error;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001197 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001198 }
1199 } else if (CUR == '*') {
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001200 if (name != NULL) {
1201 ctxt->error = 1;
1202 goto error;
1203 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001204 NEXT;
1205 PUSH(XML_OP_ALL, token, NULL);
1206 } else {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001207 PUSH(XML_OP_ELEM, name, NULL);
1208 }
1209 return;
1210error:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001211 if (URL != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001212 XML_PAT_FREE_STRING(ctxt, URL)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001213 if (token != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001214 XML_PAT_FREE_STRING(ctxt, token)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001215 if (name != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001216 XML_PAT_FREE_STRING(ctxt, name)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001217}
1218
1219/**
1220 * xmlCompilePathPattern:
1221 * @ctxt: the compilation context
1222 *
1223 * Compile the Path Pattern and generates a precompiled
1224 * form suitable for fast matching.
1225 *
1226 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1227 */
1228static void
1229xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1230 SKIP_BLANKS;
William M. Brack537f1172005-06-14 22:02:59 +00001231 if (CUR == '/') {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001232 ctxt->comp->flags |= PAT_FROM_ROOT;
William M. Brackea152c02005-06-09 18:12:28 +00001233 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001234 ctxt->comp->flags |= PAT_FROM_CUR;
1235 }
William M. Brackea152c02005-06-09 18:12:28 +00001236
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001237 if ((CUR == '/') && (NXT(1) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001238 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001239 NEXT;
1240 NEXT;
1241 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001242 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001243 NEXT;
1244 NEXT;
1245 NEXT;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001246 /* Check for incompleteness. */
1247 SKIP_BLANKS;
1248 if (CUR == 0) {
1249 ERROR5(NULL, NULL, NULL,
1250 "Incomplete expression '%s'.\n", ctxt->base);
1251 ctxt->error = 1;
1252 goto error;
1253 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001254 }
1255 if (CUR == '@') {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001256 NEXT;
1257 xmlCompileAttributeTest(ctxt);
1258 SKIP_BLANKS;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001259 /* TODO: check for incompleteness */
William M. Brackfbb619f2005-06-06 13:49:18 +00001260 if (CUR != 0) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001261 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001262 if (ctxt->error != 0)
1263 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001264 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001265 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001266 if (CUR == '/') {
1267 PUSH(XML_OP_ROOT, NULL, NULL);
1268 NEXT;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001269 /* Check for incompleteness. */
1270 SKIP_BLANKS;
1271 if (CUR == 0) {
1272 ERROR5(NULL, NULL, NULL,
1273 "Incomplete expression '%s'.\n", ctxt->base);
1274 ctxt->error = 1;
1275 goto error;
1276 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001277 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001278 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001279 if (ctxt->error != 0)
1280 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001281 SKIP_BLANKS;
1282 while (CUR == '/') {
William M. Brackfbb619f2005-06-06 13:49:18 +00001283 if (NXT(1) == '/') {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001284 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1285 NEXT;
1286 NEXT;
1287 SKIP_BLANKS;
1288 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001289 if (ctxt->error != 0)
1290 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001291 } else {
1292 PUSH(XML_OP_PARENT, NULL, NULL);
1293 NEXT;
1294 SKIP_BLANKS;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001295 if (CUR == 0) {
1296 ERROR5(NULL, NULL, NULL,
1297 "Incomplete expression '%s'.\n", ctxt->base);
1298 ctxt->error = 1;
1299 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001300 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001301 xmlCompileStepPattern(ctxt);
1302 if (ctxt->error != 0)
1303 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001304 }
1305 }
1306 }
Daniel Veillard56de87e2005-02-16 00:22:29 +00001307 if (CUR != 0) {
1308 ERROR5(NULL, NULL, NULL,
1309 "Failed to compile pattern %s\n", ctxt->base);
1310 ctxt->error = 1;
1311 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001312error:
1313 return;
1314}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001315
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001316/**
1317 * xmlCompileIDCXPathPath:
1318 * @ctxt: the compilation context
1319 *
1320 * Compile the Path Pattern and generates a precompiled
1321 * form suitable for fast matching.
1322 *
1323 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1324 */
1325static void
1326xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
1327 SKIP_BLANKS;
1328 if (CUR == '/') {
1329 ERROR5(NULL, NULL, NULL,
1330 "Unexpected selection of the document root in '%s'.\n",
1331 ctxt->base);
1332 goto error;
1333 }
1334 ctxt->comp->flags |= PAT_FROM_CUR;
1335
1336 if (CUR == '.') {
1337 /* "." - "self::node()" */
1338 NEXT;
1339 SKIP_BLANKS;
1340 if (CUR == 0) {
1341 /*
1342 * Selection of the context node.
1343 */
1344 PUSH(XML_OP_ELEM, NULL, NULL);
1345 return;
1346 }
1347 if (CUR != '/') {
1348 /* TODO: A more meaningful error message. */
1349 ERROR5(NULL, NULL, NULL,
1350 "Unexpected token after '.' in '%s'.\n", ctxt->base);
1351 goto error;
1352 }
1353 /* "./" - "self::node()/" */
1354 NEXT;
1355 SKIP_BLANKS;
1356 if (CUR == '/') {
1357 if (IS_BLANK_CH(PEEKPREV(1))) {
1358 /*
1359 * Disallow "./ /"
1360 */
1361 ERROR5(NULL, NULL, NULL,
1362 "Unexpected '/' token in '%s'.\n", ctxt->base);
1363 goto error;
1364 }
1365 /* ".//" - "self:node()/descendant-or-self::node()/" */
1366 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1367 NEXT;
1368 SKIP_BLANKS;
1369 }
1370 if (CUR == 0)
1371 goto error_unfinished;
1372 }
1373 /*
1374 * Process steps.
1375 */
1376 do {
1377 xmlCompileStepPattern(ctxt);
1378 if (ctxt->error != 0)
1379 goto error;
1380 SKIP_BLANKS;
1381 if (CUR != '/')
1382 break;
1383 PUSH(XML_OP_PARENT, NULL, NULL);
1384 NEXT;
1385 SKIP_BLANKS;
1386 if (CUR == '/') {
1387 /*
1388 * Disallow subsequent '//'.
1389 */
1390 ERROR5(NULL, NULL, NULL,
1391 "Unexpected subsequent '//' in '%s'.\n",
1392 ctxt->base);
1393 goto error;
1394 }
1395 if (CUR == 0)
1396 goto error_unfinished;
1397
1398 } while (CUR != 0);
1399
1400 if (CUR != 0) {
1401 ERROR5(NULL, NULL, NULL,
1402 "Failed to compile expression '%s'.\n", ctxt->base);
1403 ctxt->error = 1;
1404 }
1405 return;
1406error:
1407 ctxt->error = 1;
1408 return;
1409
1410error_unfinished:
1411 ctxt->error = 1;
1412 ERROR5(NULL, NULL, NULL,
1413 "Unfinished expression '%s'.\n", ctxt->base);
1414 return;
1415}
1416
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001417/************************************************************************
1418 * *
1419 * The streaming code *
1420 * *
1421 ************************************************************************/
1422
1423#ifdef DEBUG_STREAMING
1424static void
1425xmlDebugStreamComp(xmlStreamCompPtr stream) {
1426 int i;
1427
1428 if (stream == NULL) {
1429 printf("Stream: NULL\n");
1430 return;
1431 }
1432 printf("Stream: %d steps\n", stream->nbStep);
1433 for (i = 0;i < stream->nbStep;i++) {
1434 if (stream->steps[i].ns != NULL) {
1435 printf("{%s}", stream->steps[i].ns);
1436 }
1437 if (stream->steps[i].name == NULL) {
1438 printf("* ");
1439 } else {
1440 printf("%s ", stream->steps[i].name);
1441 }
1442 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1443 printf("root ");
1444 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1445 printf("// ");
1446 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1447 printf("final ");
1448 printf("\n");
1449 }
1450}
1451static void
1452xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1453 int i;
1454
1455 if (ctxt == NULL) {
1456 printf("Stream: NULL\n");
1457 return;
1458 }
1459 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1460 if (match)
1461 printf("matches\n");
1462 else
1463 printf("\n");
1464 for (i = 0;i < ctxt->nbState;i++) {
1465 if (ctxt->states[2 * i] < 0)
1466 printf(" %d: free\n", i);
1467 else {
1468 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1469 ctxt->states[(2 * i) + 1]);
1470 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1471 XML_STREAM_STEP_DESC)
1472 printf(" //\n");
1473 else
1474 printf("\n");
1475 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001476 }
1477}
1478#endif
1479/**
1480 * xmlNewStreamComp:
1481 * @size: the number of expected steps
1482 *
1483 * build a new compiled pattern for streaming
1484 *
1485 * Returns the new structure or NULL in case of error.
1486 */
1487static xmlStreamCompPtr
1488xmlNewStreamComp(int size) {
1489 xmlStreamCompPtr cur;
1490
1491 if (size < 4)
1492 size = 4;
1493
1494 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1495 if (cur == NULL) {
1496 ERROR(NULL, NULL, NULL,
1497 "xmlNewStreamComp: malloc failed\n");
1498 return(NULL);
1499 }
1500 memset(cur, 0, sizeof(xmlStreamComp));
1501 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1502 if (cur->steps == NULL) {
1503 xmlFree(cur);
1504 ERROR(NULL, NULL, NULL,
1505 "xmlNewStreamComp: malloc failed\n");
1506 return(NULL);
1507 }
1508 cur->nbStep = 0;
1509 cur->maxStep = size;
1510 return(cur);
1511}
1512
1513/**
1514 * xmlFreeStreamComp:
1515 * @comp: the compiled pattern for streaming
1516 *
1517 * Free the compiled pattern for streaming
1518 */
1519static void
1520xmlFreeStreamComp(xmlStreamCompPtr comp) {
1521 if (comp != NULL) {
1522 if (comp->steps != NULL)
1523 xmlFree(comp->steps);
1524 if (comp->dict != NULL)
1525 xmlDictFree(comp->dict);
1526 xmlFree(comp);
1527 }
1528}
1529
1530/**
1531 * xmlStreamCompAddStep:
1532 * @comp: the compiled pattern for streaming
1533 * @name: the first string, the name, or NULL for *
1534 * @ns: the second step, the namespace name
1535 * @flags: the flags for that step
1536 *
1537 * Add a new step to the compiled pattern
1538 *
1539 * Returns -1 in case of error or the step index if successful
1540 */
1541static int
1542xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001543 const xmlChar *ns, int nodeType, int flags) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001544 xmlStreamStepPtr cur;
1545
1546 if (comp->nbStep >= comp->maxStep) {
1547 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1548 comp->maxStep * 2 * sizeof(xmlStreamStep));
1549 if (cur == NULL) {
1550 ERROR(NULL, NULL, NULL,
1551 "xmlNewStreamComp: malloc failed\n");
1552 return(-1);
1553 }
1554 comp->steps = cur;
1555 comp->maxStep *= 2;
1556 }
1557 cur = &comp->steps[comp->nbStep++];
1558 cur->flags = flags;
1559 cur->name = name;
1560 cur->ns = ns;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001561 cur->nodeType = nodeType;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001562 return(comp->nbStep - 1);
1563}
1564
1565/**
1566 * xmlStreamCompile:
1567 * @comp: the precompiled pattern
1568 *
1569 * Tries to stream compile a pattern
1570 *
1571 * Returns -1 in case of failure and 0 in case of success.
1572 */
1573static int
1574xmlStreamCompile(xmlPatternPtr comp) {
1575 xmlStreamCompPtr stream;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001576 int i, s = 0, root = 0, flags = 0, prevs = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001577 xmlStepOp step;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001578
1579 if ((comp == NULL) || (comp->steps == NULL))
1580 return(-1);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001581 /*
1582 * special case for .
1583 */
1584 if ((comp->nbStep == 1) &&
1585 (comp->steps[0].op == XML_OP_ELEM) &&
1586 (comp->steps[0].value == NULL) &&
1587 (comp->steps[0].value2 == NULL)) {
1588 stream = xmlNewStreamComp(0);
1589 if (stream == NULL)
1590 return(-1);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001591 /* Note that the stream will have no steps in this case. */
1592 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001593 comp->stream = stream;
1594 return(0);
1595 }
1596
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001597 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1598 if (stream == NULL)
1599 return(-1);
1600 if (comp->dict != NULL) {
1601 stream->dict = comp->dict;
1602 xmlDictReference(stream->dict);
1603 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001604
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001605 i = 0;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001606 if (comp->flags & PAT_FROM_ROOT)
1607 stream->flags |= XML_STREAM_FROM_ROOT;
1608
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001609 for (;i < comp->nbStep;i++) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001610 step = comp->steps[i];
1611 switch (step.op) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001612 case XML_OP_END:
1613 break;
1614 case XML_OP_ROOT:
1615 if (i != 0)
1616 goto error;
1617 root = 1;
1618 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001619 case XML_OP_NS:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001620 s = xmlStreamCompAddStep(stream, NULL, step.value,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001621 XML_ELEMENT_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001622 if (s < 0)
1623 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001624 prevs = s;
1625 flags = 0;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001626 break;
1627 case XML_OP_ATTR:
1628 flags |= XML_STREAM_STEP_ATTR;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001629 prevs = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001630 s = xmlStreamCompAddStep(stream,
1631 step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001632 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001633 if (s < 0)
1634 goto error;
1635 break;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001636 case XML_OP_ELEM:
1637 if ((step.value == NULL) && (step.value2 == NULL)) {
1638 /*
1639 * We have a "." or "self::node()" here.
1640 * Eliminate redundant self::node() tests like in "/./."
1641 * or "//./"
1642 * The only case we won't eliminate is "//.", i.e. if
1643 * self::node() is the last node test and we had
1644 * continuation somewhere beforehand.
1645 */
1646 if ((comp->nbStep == i + 1) &&
1647 (flags & XML_STREAM_STEP_DESC)) {
1648 /*
1649 * Mark the special case where the expression resolves
1650 * to any type of node.
1651 */
1652 if (comp->nbStep == i + 1) {
1653 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1654 }
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001655 flags |= XML_STREAM_STEP_NODE;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001656 s = xmlStreamCompAddStep(stream, NULL, NULL,
1657 XML_STREAM_ANY_NODE, flags);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001658 if (s < 0)
1659 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001660 flags = 0;
1661 /*
1662 * If there was a previous step, mark it to be added to
1663 * the result node-set; this is needed since only
1664 * the last step will be marked as "final" and only
1665 * "final" nodes are added to the resulting set.
1666 */
1667 if (prevs != -1) {
1668 stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1669 prevs = -1;
1670 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001671 break;
1672
1673 } else {
1674 /* Just skip this one. */
1675 continue;
1676 }
1677 }
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001678 /* An element node. */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001679 s = xmlStreamCompAddStep(stream, step.value, step.value2,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001680 XML_ELEMENT_NODE, flags);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001681 if (s < 0)
1682 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001683 prevs = s;
1684 flags = 0;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001685 break;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001686 case XML_OP_CHILD:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001687 /* An element node child. */
1688 s = xmlStreamCompAddStep(stream, step.value, step.value2,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001689 XML_ELEMENT_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001690 if (s < 0)
1691 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001692 prevs = s;
1693 flags = 0;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001694 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001695 case XML_OP_ALL:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001696 s = xmlStreamCompAddStep(stream, NULL, NULL,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001697 XML_ELEMENT_NODE, flags);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001698 if (s < 0)
1699 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001700 prevs = s;
1701 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001702 break;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001703 case XML_OP_PARENT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001704 break;
1705 case XML_OP_ANCESTOR:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001706 /* Skip redundant continuations. */
1707 if (flags & XML_STREAM_STEP_DESC)
1708 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001709 flags |= XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001710 /*
1711 * Mark the expression as having "//".
1712 */
1713 if ((stream->flags & XML_STREAM_DESC) == 0)
1714 stream->flags |= XML_STREAM_DESC;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001715 break;
1716 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001717 }
1718 if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1719 /*
1720 * If this should behave like a real pattern, we will mark
1721 * the first step as having "//", to be reentrant on every
1722 * tree level.
1723 */
1724 if ((stream->flags & XML_STREAM_DESC) == 0)
1725 stream->flags |= XML_STREAM_DESC;
1726
1727 if (stream->nbStep > 0) {
1728 if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0)
1729 stream->steps[0].flags |= XML_STREAM_STEP_DESC;
1730 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001731 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001732 if (stream->nbStep <= s)
1733 goto error;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001734 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1735 if (root)
1736 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1737#ifdef DEBUG_STREAMING
1738 xmlDebugStreamComp(stream);
1739#endif
1740 comp->stream = stream;
1741 return(0);
1742error:
1743 xmlFreeStreamComp(stream);
1744 return(0);
1745}
1746
1747/**
1748 * xmlNewStreamCtxt:
1749 * @size: the number of expected states
1750 *
1751 * build a new stream context
1752 *
1753 * Returns the new structure or NULL in case of error.
1754 */
1755static xmlStreamCtxtPtr
1756xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1757 xmlStreamCtxtPtr cur;
1758
1759 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1760 if (cur == NULL) {
1761 ERROR(NULL, NULL, NULL,
1762 "xmlNewStreamCtxt: malloc failed\n");
1763 return(NULL);
1764 }
1765 memset(cur, 0, sizeof(xmlStreamCtxt));
1766 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1767 if (cur->states == NULL) {
1768 xmlFree(cur);
1769 ERROR(NULL, NULL, NULL,
1770 "xmlNewStreamCtxt: malloc failed\n");
1771 return(NULL);
1772 }
1773 cur->nbState = 0;
1774 cur->maxState = 4;
1775 cur->level = 0;
1776 cur->comp = stream;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001777 cur->blockLevel = -1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001778 return(cur);
1779}
1780
1781/**
1782 * xmlFreeStreamCtxt:
1783 * @stream: the stream context
1784 *
1785 * Free the stream context
1786 */
1787void
1788xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001789 xmlStreamCtxtPtr next;
1790
1791 while (stream != NULL) {
1792 next = stream->next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001793 if (stream->states != NULL)
1794 xmlFree(stream->states);
1795 xmlFree(stream);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001796 stream = next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001797 }
1798}
1799
1800/**
1801 * xmlStreamCtxtAddState:
1802 * @comp: the stream context
1803 * @idx: the step index for that streaming state
1804 *
1805 * Add a new state to the stream context
1806 *
1807 * Returns -1 in case of error or the state index if successful
1808 */
1809static int
1810xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1811 int i;
1812 for (i = 0;i < comp->nbState;i++) {
1813 if (comp->states[2 * i] < 0) {
1814 comp->states[2 * i] = idx;
1815 comp->states[2 * i + 1] = level;
1816 return(i);
1817 }
1818 }
1819 if (comp->nbState >= comp->maxState) {
1820 int *cur;
1821
1822 cur = (int *) xmlRealloc(comp->states,
1823 comp->maxState * 4 * sizeof(int));
1824 if (cur == NULL) {
1825 ERROR(NULL, NULL, NULL,
1826 "xmlNewStreamCtxt: malloc failed\n");
1827 return(-1);
1828 }
1829 comp->states = cur;
1830 comp->maxState *= 2;
1831 }
1832 comp->states[2 * comp->nbState] = idx;
1833 comp->states[2 * comp->nbState++ + 1] = level;
1834 return(comp->nbState - 1);
1835}
1836
1837/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001838 * xmlStreamPushInternal:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001839 * @stream: the stream context
1840 * @name: the current name
1841 * @ns: the namespace name
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001842 * @nodeType: the type of the node
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001843 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001844 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1845 * indicated a dictionary, then strings for name and ns will be expected
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001846 * to come from the dictionary.
1847 * Both @name and @ns being NULL means the / i.e. the root of the document.
1848 * This can also act as a reset.
1849 *
1850 * Returns: -1 in case of error, 1 if the current state in the stream is a
1851 * match and 0 otherwise.
1852 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001853static int
1854xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1855 const xmlChar *name, const xmlChar *ns,
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001856 int nodeType) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001857 int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001858 xmlStreamCompPtr comp;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001859 xmlStreamStep step;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001860#ifdef DEBUG_STREAMING
1861 xmlStreamCtxtPtr orig = stream;
1862#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001863
1864 if ((stream == NULL) || (stream->nbState < 0))
1865 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001866
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001867 while (stream != NULL) {
1868 comp = stream->comp;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001869
1870 if ((nodeType == XML_ELEMENT_NODE) &&
1871 (name == NULL) && (ns == NULL)) {
1872 /* We have a document node here (or a reset). */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001873 stream->nbState = 0;
1874 stream->level = 0;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001875 stream->blockLevel = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001876 if (comp->flags & XML_STREAM_FROM_ROOT) {
1877 if (comp->nbStep == 0) {
1878 /* TODO: We have a "/." here? */
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001879 ret = 1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001880 } else {
1881 if ((comp->nbStep == 1) &&
1882 (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
1883 (comp->steps[0].flags & XML_STREAM_STEP_DESC))
1884 {
1885 /*
1886 * In the case of "//." the document node will match
1887 * as well.
1888 */
1889 ret = 1;
1890 } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1891 /* TODO: Do we need this ? */
1892 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1893 if (tmp < 0)
1894 err++;
1895 }
1896 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001897 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001898 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001899 continue; /* while */
1900 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001901
1902 /*
1903 * Fast check for ".".
1904 */
1905 if (comp->nbStep == 0) {
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001906 /*
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00001907 * / and . are handled at the XPath node set creation
1908 * level by checking min depth
1909 */
1910 if (stream->flags & XML_PATTERN_XPATH) {
1911 stream = stream->next;
1912 continue; /* while */
1913 }
1914 /*
William M. Brackea152c02005-06-09 18:12:28 +00001915 * For non-pattern like evaluation like XML Schema IDCs
1916 * or traditional XPath expressions, this will match if
1917 * we are at the first level only, otherwise on every level.
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001918 */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001919 if ((nodeType != XML_ATTRIBUTE_NODE) &&
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001920 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1921 (stream->level == 0))) {
1922 ret = 1;
1923 }
1924 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001925 goto stream_next;
1926 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001927 if (stream->blockLevel != -1) {
1928 /*
1929 * Skip blocked expressions.
1930 */
1931 stream->level++;
1932 goto stream_next;
William M. Brackea152c02005-06-09 18:12:28 +00001933 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001934
1935 if ((nodeType != XML_ELEMENT_NODE) &&
1936 (nodeType != XML_ATTRIBUTE_NODE) &&
1937 ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) {
1938 /*
1939 * No need to process nodes of other types if we don't
1940 * resolve to those types.
1941 * TODO: Do we need to block the context here?
1942 */
1943 stream->level++;
1944 goto stream_next;
1945 }
1946
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001947 /*
1948 * Check evolution of existing states
1949 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001950 i = 0;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001951 m = stream->nbState;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001952 while (i < m) {
1953 if ((comp->flags & XML_STREAM_DESC) == 0) {
1954 /*
1955 * If there is no "//", then only the last
1956 * added state is of interest.
1957 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001958 stepNr = stream->states[2 * (stream->nbState -1)];
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001959 /*
1960 * TODO: Security check, should not happen, remove it.
1961 */
1962 if (stream->states[(2 * (stream->nbState -1)) + 1] <
1963 stream->level) {
1964 return (-1);
1965 }
1966 desc = 0;
1967 /* loop-stopper */
1968 i = m;
1969 } else {
1970 /*
1971 * If there are "//", then we need to process every "//"
1972 * occuring in the states, plus any other state for this
1973 * level.
1974 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001975 stepNr = stream->states[2 * i];
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001976
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001977 /* TODO: should not happen anymore: dead states */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001978 if (stepNr < 0)
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001979 goto next_state;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001980
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001981 tmp = stream->states[(2 * i) + 1];
1982
1983 /* skip new states just added */
1984 if (tmp > stream->level)
1985 goto next_state;
1986
1987 /* skip states at ancestor levels, except if "//" */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001988 desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001989 if ((tmp < stream->level) && (!desc))
1990 goto next_state;
1991 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001992 /*
1993 * Check for correct node-type.
1994 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001995 step = comp->steps[stepNr];
1996 if (step.nodeType != nodeType) {
1997 if (step.nodeType == XML_ATTRIBUTE_NODE) {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001998 /*
1999 * Block this expression for deeper evaluation.
2000 */
2001 if ((comp->flags & XML_STREAM_DESC) == 0)
2002 stream->blockLevel = stream->level +1;
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002003 goto next_state;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002004 } else if (step.nodeType != XML_STREAM_ANY_NODE)
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002005 goto next_state;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002006 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002007 /*
2008 * Compare local/namespace-name.
2009 */
2010 match = 0;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002011 if (step.nodeType == XML_STREAM_ANY_NODE) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002012 match = 1;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002013 } else if (step.name == NULL) {
2014 if (step.ns == NULL) {
2015 /*
2016 * This lets through all elements/attributes.
2017 */
2018 match = 1;
2019 } else if (ns != NULL)
2020 match = xmlStrEqual(step.ns, ns);
2021 } else if (((step.ns != NULL) == (ns != NULL)) &&
2022 (name != NULL) &&
2023 (step.name[0] == name[0]) &&
2024 xmlStrEqual(step.name, name) &&
2025 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2026 {
2027 match = 1;
2028 }
2029#if 0
2030/*
2031* TODO: Pointer comparison won't work, since not guaranteed that the given
2032* values are in the same dict; especially if it's the namespace name,
2033* normally coming from ns->href. We need a namespace dict mechanism !
2034*/
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002035 } else if (comp->dict) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002036 if (step.name == NULL) {
2037 if (step.ns == NULL)
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002038 match = 1;
2039 else
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002040 match = (step.ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002041 } else {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002042 match = ((step.name == name) && (step.ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002043 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002044#endif /* if 0 ------------------------------------------------------- */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002045 if (match) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002046 final = step.flags & XML_STREAM_STEP_FINAL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002047 if (desc) {
2048 if (final) {
2049 ret = 1;
2050 } else {
2051 /* descending match create a new state */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002052 xmlStreamCtxtAddState(stream, stepNr + 1,
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002053 stream->level + 1);
2054 }
2055 } else {
2056 if (final) {
2057 ret = 1;
2058 } else {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002059 xmlStreamCtxtAddState(stream, stepNr + 1,
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002060 stream->level + 1);
2061 }
2062 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002063 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00002064 /*
2065 * Check if we have a special case like "foo/bar//.", where
2066 * "foo" is selected as well.
2067 */
2068 ret = 1;
2069 }
2070 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002071 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2072 ((! match) || final)) {
2073 /*
2074 * Mark this expression as blocked for any evaluation at
2075 * deeper levels. Note that this includes "/foo"
2076 * expressions if the *pattern* behaviour is used.
2077 */
2078 stream->blockLevel = stream->level +1;
2079 }
2080next_state:
2081 i++;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002082 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002083
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002084 stream->level++;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002085
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002086 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002087 * Re/enter the expression.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002088 * Don't reenter if it's an absolute expression like "/foo",
2089 * except "//foo".
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002090 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002091 step = comp->steps[0];
2092 if (step.flags & XML_STREAM_STEP_ROOT)
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002093 goto stream_next;
2094
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002095 desc = step.flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002096 if (stream->flags & XML_PATTERN_NOTPATTERN) {
2097 /*
2098 * Re/enter the expression if it is a "descendant" one,
2099 * or if we are at the 1st level of evaluation.
2100 */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002101
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002102 if (stream->level == 1) {
2103 if (XML_STREAM_XS_IDC(stream)) {
William M. Brackea152c02005-06-09 18:12:28 +00002104 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002105 * XS-IDC: The missing "self::node()" will always
2106 * match the first given node.
2107 */
William M. Brackea152c02005-06-09 18:12:28 +00002108 goto stream_next;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002109 } else
2110 goto compare;
2111 }
2112 /*
2113 * A "//" is always reentrant.
2114 */
2115 if (desc)
2116 goto compare;
2117
2118 /*
2119 * XS-IDC: Process the 2nd level, since the missing
2120 * "self::node()" is responsible for the 2nd level being
2121 * the real start level.
2122 */
2123 if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
2124 goto compare;
2125
2126 goto stream_next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002127 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002128
2129compare:
2130 /*
2131 * Check expected node-type.
2132 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002133 if (step.nodeType != nodeType) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002134 if (nodeType == XML_ATTRIBUTE_NODE)
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002135 goto stream_next;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002136 else if (step.nodeType != XML_STREAM_ANY_NODE)
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002137 goto stream_next;
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002138 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002139 /*
2140 * Compare local/namespace-name.
2141 */
2142 match = 0;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002143 if (step.nodeType == XML_STREAM_ANY_NODE) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002144 match = 1;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002145 } else if (step.name == NULL) {
2146 if (step.ns == NULL) {
2147 /*
2148 * This lets through all elements/attributes.
2149 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002150 match = 1;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002151 } else if (ns != NULL)
2152 match = xmlStrEqual(step.ns, ns);
2153 } else if (((step.ns != NULL) == (ns != NULL)) &&
2154 (name != NULL) &&
2155 (step.name[0] == name[0]) &&
2156 xmlStrEqual(step.name, name) &&
2157 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2158 {
2159 match = 1;
2160 }
2161 final = step.flags & XML_STREAM_STEP_FINAL;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002162 if (match) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002163 if (final)
2164 ret = 1;
2165 else
2166 xmlStreamCtxtAddState(stream, 1, stream->level);
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002167 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00002168 /*
2169 * Check if we have a special case like "foo//.", where
2170 * "foo" is selected as well.
2171 */
2172 ret = 1;
2173 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002174 }
2175 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2176 ((! match) || final)) {
2177 /*
2178 * Mark this expression as blocked for any evaluation at
2179 * deeper levels.
2180 */
2181 stream->blockLevel = stream->level;
2182 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002183
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002184stream_next:
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002185 stream = stream->next;
2186 } /* while stream != NULL */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002187
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002188 if (err > 0)
2189 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002190#ifdef DEBUG_STREAMING
2191 xmlDebugStreamCtxt(orig, ret);
2192#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002193 return(ret);
2194}
2195
2196/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002197 * xmlStreamPush:
2198 * @stream: the stream context
2199 * @name: the current name
2200 * @ns: the namespace name
2201 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002202 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2203 * indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002204 * to come from the dictionary.
2205 * Both @name and @ns being NULL means the / i.e. the root of the document.
2206 * This can also act as a reset.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002207 * Otherwise the function will act as if it has been given an element-node.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002208 *
2209 * Returns: -1 in case of error, 1 if the current state in the stream is a
2210 * match and 0 otherwise.
2211 */
2212int
2213xmlStreamPush(xmlStreamCtxtPtr stream,
2214 const xmlChar *name, const xmlChar *ns) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002215 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE));
2216}
2217
2218/**
Daniel Veillard67952602006-01-05 15:29:44 +00002219 * xmlStreamPushNode:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002220 * @stream: the stream context
2221 * @name: the current name
2222 * @ns: the namespace name
2223 * @nodeType: the type of the node being pushed
2224 *
2225 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2226 * indicated a dictionary, then strings for name and ns will be expected
2227 * to come from the dictionary.
2228 * Both @name and @ns being NULL means the / i.e. the root of the document.
2229 * This can also act as a reset.
2230 * Different from xmlStreamPush() this function can be fed with nodes of type:
2231 * element-, attribute-, text-, cdata-section-, comment- and
2232 * processing-instruction-node.
2233 *
2234 * Returns: -1 in case of error, 1 if the current state in the stream is a
2235 * match and 0 otherwise.
2236 */
2237int
2238xmlStreamPushNode(xmlStreamCtxtPtr stream,
2239 const xmlChar *name, const xmlChar *ns,
2240 int nodeType)
2241{
2242 return (xmlStreamPushInternal(stream, name, ns,
2243 nodeType));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002244}
2245
2246/**
2247* xmlStreamPushAttr:
2248* @stream: the stream context
2249* @name: the current name
2250* @ns: the namespace name
2251*
William M. Brackfbb619f2005-06-06 13:49:18 +00002252* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2253* indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002254* to come from the dictionary.
2255* Both @name and @ns being NULL means the / i.e. the root of the document.
2256* This can also act as a reset.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002257* Otherwise the function will act as if it has been given an attribute-node.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002258*
2259* Returns: -1 in case of error, 1 if the current state in the stream is a
2260* match and 0 otherwise.
2261*/
2262int
2263xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2264 const xmlChar *name, const xmlChar *ns) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002265 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002266}
2267
2268/**
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002269 * xmlStreamPop:
2270 * @stream: the stream context
2271 *
2272 * push one level from the stream.
2273 *
2274 * Returns: -1 in case of error, 0 otherwise.
2275 */
2276int
2277xmlStreamPop(xmlStreamCtxtPtr stream) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002278 int i, lev;
Kasimier T. Buchcik65c2f1d2005-10-17 12:39:58 +00002279
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002280 if (stream == NULL)
2281 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002282 while (stream != NULL) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002283 /*
2284 * Reset block-level.
2285 */
2286 if (stream->blockLevel == stream->level)
2287 stream->blockLevel = -1;
2288
William M. Brackf8477002008-07-17 05:29:16 +00002289 /*
2290 * stream->level can be zero when XML_FINAL_IS_ANY_NODE is set
2291 * (see the thread at
2292 * http://mail.gnome.org/archives/xslt/2008-July/msg00027.html)
2293 */
2294 if (stream->level)
2295 stream->level--;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002296 /*
2297 * Check evolution of existing states
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002298 */
2299 for (i = stream->nbState -1; i >= 0; i--) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002300 /* discard obsoleted states */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002301 lev = stream->states[(2 * i) + 1];
2302 if (lev > stream->level)
2303 stream->nbState--;
2304 if (lev <= stream->level)
2305 break;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002306 }
2307 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00002308 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002309 return(0);
2310}
2311
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002312/**
2313 * xmlStreamWantsAnyNode:
Daniel Veillard67952602006-01-05 15:29:44 +00002314 * @streamCtxt: the stream context
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002315 *
2316 * Query if the streaming pattern additionally needs to be fed with
2317 * text-, cdata-section-, comment- and processing-instruction-nodes.
2318 * If the result is 0 then only element-nodes and attribute-nodes
2319 * need to be pushed.
2320 *
2321 * Returns: 1 in case of need of nodes of the above described types,
2322 * 0 otherwise. -1 on API errors.
2323 */
2324int
2325xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
2326{
2327 if (streamCtxt == NULL)
2328 return(-1);
2329 while (streamCtxt != NULL) {
2330 if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
2331 return(1);
2332 streamCtxt = streamCtxt->next;
2333 }
2334 return(0);
2335}
2336
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002337/************************************************************************
2338 * *
2339 * The public interfaces *
2340 * *
2341 ************************************************************************/
2342
2343/**
2344 * xmlPatterncompile:
2345 * @pattern: the pattern to compile
William M. Brackfbb619f2005-06-06 13:49:18 +00002346 * @dict: an optional dictionary for interned strings
Daniel Veillarded6c5492005-07-23 15:00:22 +00002347 * @flags: compilation flags, see xmlPatternFlags
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002348 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002349 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002350 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002351 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002352 * Returns the compiled form of the pattern or NULL in case of error
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002353 */
2354xmlPatternPtr
Daniel Veillarded6c5492005-07-23 15:00:22 +00002355xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002356 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002357 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002358 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002359 const xmlChar *or, *start;
2360 xmlChar *tmp = NULL;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002361 int type = 0;
2362 int streamable = 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002363
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002364 if (pattern == NULL)
2365 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002366
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002367 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002368 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002369 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002370 tmp = NULL;
2371 while ((*or != 0) && (*or != '|')) or++;
2372 if (*or == 0)
2373 ctxt = xmlNewPatParserContext(start, dict, namespaces);
2374 else {
2375 tmp = xmlStrndup(start, or - start);
2376 if (tmp != NULL) {
2377 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2378 }
2379 or++;
2380 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002381 if (ctxt == NULL) goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002382 cur = xmlNewPattern();
2383 if (cur == NULL) goto error;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002384 /*
2385 * Assign string dict.
2386 */
2387 if (dict) {
2388 cur->dict = dict;
2389 xmlDictReference(dict);
2390 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002391 if (ret == NULL)
2392 ret = cur;
2393 else {
2394 cur->next = ret->next;
2395 ret->next = cur;
2396 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002397 cur->flags = flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002398 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002399
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002400 if (XML_STREAM_XS_IDC(cur))
2401 xmlCompileIDCXPathPath(ctxt);
2402 else
2403 xmlCompilePathPattern(ctxt);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002404 if (ctxt->error != 0)
2405 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002406 xmlFreePatParserContext(ctxt);
William M. Brackfbb619f2005-06-06 13:49:18 +00002407 ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002408
2409
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002410 if (streamable) {
2411 if (type == 0) {
2412 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2413 } else if (type == PAT_FROM_ROOT) {
2414 if (cur->flags & PAT_FROM_CUR)
2415 streamable = 0;
2416 } else if (type == PAT_FROM_CUR) {
2417 if (cur->flags & PAT_FROM_ROOT)
2418 streamable = 0;
2419 }
2420 }
2421 if (streamable)
2422 xmlStreamCompile(cur);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002423 if (xmlReversePattern(cur) < 0)
2424 goto error;
William M. Brackea152c02005-06-09 18:12:28 +00002425 if (tmp != NULL) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002426 xmlFree(tmp);
William M. Brackea152c02005-06-09 18:12:28 +00002427 tmp = NULL;
2428 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002429 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002430 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002431 if (streamable == 0) {
2432 cur = ret;
2433 while (cur != NULL) {
2434 if (cur->stream != NULL) {
2435 xmlFreeStreamComp(cur->stream);
2436 cur->stream = NULL;
2437 }
2438 cur = cur->next;
2439 }
2440 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002441
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002442 return(ret);
2443error:
2444 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2445 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002446 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002447 return(NULL);
2448}
2449
2450/**
2451 * xmlPatternMatch:
2452 * @comp: the precompiled pattern
2453 * @node: a node
2454 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002455 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002456 *
2457 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2458 */
2459int
2460xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2461{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002462 int ret = 0;
2463
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002464 if ((comp == NULL) || (node == NULL))
2465 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002466
2467 while (comp != NULL) {
2468 ret = xmlPatMatch(comp, node);
2469 if (ret != 0)
2470 return(ret);
2471 comp = comp->next;
2472 }
2473 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002474}
2475
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002476/**
2477 * xmlPatternGetStreamCtxt:
2478 * @comp: the precompiled pattern
2479 *
2480 * Get a streaming context for that pattern
2481 * Use xmlFreeStreamCtxt to free the context.
2482 *
2483 * Returns a pointer to the context or NULL in case of failure
2484 */
2485xmlStreamCtxtPtr
2486xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2487{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002488 xmlStreamCtxtPtr ret = NULL, cur;
2489
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002490 if ((comp == NULL) || (comp->stream == NULL))
2491 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002492
2493 while (comp != NULL) {
2494 if (comp->stream == NULL)
2495 goto failed;
2496 cur = xmlNewStreamCtxt(comp->stream);
2497 if (cur == NULL)
2498 goto failed;
2499 if (ret == NULL)
2500 ret = cur;
2501 else {
2502 cur->next = ret->next;
2503 ret->next = cur;
2504 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002505 cur->flags = comp->flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002506 comp = comp->next;
2507 }
2508 return(ret);
2509failed:
2510 xmlFreeStreamCtxt(ret);
2511 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002512}
2513
Daniel Veillard56de87e2005-02-16 00:22:29 +00002514/**
2515 * xmlPatternStreamable:
2516 * @comp: the precompiled pattern
2517 *
2518 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2519 * should work.
2520 *
2521 * Returns 1 if streamable, 0 if not and -1 in case of error.
2522 */
2523int
2524xmlPatternStreamable(xmlPatternPtr comp) {
2525 if (comp == NULL)
2526 return(-1);
2527 while (comp != NULL) {
2528 if (comp->stream == NULL)
2529 return(0);
2530 comp = comp->next;
2531 }
2532 return(1);
2533}
2534
2535/**
2536 * xmlPatternMaxDepth:
2537 * @comp: the precompiled pattern
2538 *
2539 * Check the maximum depth reachable by a pattern
2540 *
2541 * Returns -2 if no limit (using //), otherwise the depth,
2542 * and -1 in case of error
2543 */
2544int
2545xmlPatternMaxDepth(xmlPatternPtr comp) {
2546 int ret = 0, i;
2547 if (comp == NULL)
2548 return(-1);
2549 while (comp != NULL) {
2550 if (comp->stream == NULL)
2551 return(-1);
2552 for (i = 0;i < comp->stream->nbStep;i++)
2553 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2554 return(-2);
2555 if (comp->stream->nbStep > ret)
2556 ret = comp->stream->nbStep;
2557 comp = comp->next;
2558 }
2559 return(ret);
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002560}
Daniel Veillard56de87e2005-02-16 00:22:29 +00002561
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002562/**
2563 * xmlPatternMinDepth:
2564 * @comp: the precompiled pattern
2565 *
2566 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2567 * part of the set.
2568 *
2569 * Returns -1 in case of error otherwise the depth,
2570 *
2571 */
2572int
2573xmlPatternMinDepth(xmlPatternPtr comp) {
2574 int ret = 12345678;
2575 if (comp == NULL)
2576 return(-1);
2577 while (comp != NULL) {
2578 if (comp->stream == NULL)
2579 return(-1);
2580 if (comp->stream->nbStep < ret)
2581 ret = comp->stream->nbStep;
2582 if (ret == 0)
2583 return(0);
2584 comp = comp->next;
2585 }
2586 return(ret);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002587}
2588
2589/**
2590 * xmlPatternFromRoot:
2591 * @comp: the precompiled pattern
2592 *
2593 * Check if the pattern must be looked at from the root.
2594 *
2595 * Returns 1 if true, 0 if false and -1 in case of error
2596 */
2597int
2598xmlPatternFromRoot(xmlPatternPtr comp) {
2599 if (comp == NULL)
2600 return(-1);
2601 while (comp != NULL) {
2602 if (comp->stream == NULL)
2603 return(-1);
2604 if (comp->flags & PAT_FROM_ROOT)
2605 return(1);
2606 comp = comp->next;
2607 }
2608 return(0);
2609
2610}
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002611
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002612#define bottom_pattern
2613#include "elfgcchack.h"
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002614#endif /* LIBXML_PATTERN_ENABLED */