blob: cf416064b1a15c1499fc9f453facd9e1927ea57f [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;
308 for (i = 0;namespaces[2 * i] != NULL;i++);
309 cur->nb_namespaces = i;
310 } else {
311 cur->nb_namespaces = 0;
312 }
313 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000314 return(cur);
315}
316
317/**
318 * xmlFreePatParserContext:
319 * @ctxt: an XSLT parser context
320 *
321 * Free up the memory allocated by @ctxt
322 */
323static void
324xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
325 if (ctxt == NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000326 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000327 memset(ctxt, -1, sizeof(xmlPatParserContext));
328 xmlFree(ctxt);
329}
330
331/**
332 * xmlPatternAdd:
333 * @comp: the compiled match expression
334 * @op: an op
335 * @value: the first value
336 * @value2: the second value
337 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000338 * Add a step to an XSLT Compiled Match
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000339 *
340 * Returns -1 in case of failure, 0 otherwise.
341 */
342static int
343xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
344 xmlPatternPtr comp,
345 xmlPatOp op, xmlChar * value, xmlChar * value2)
346{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000347 if (comp->nbStep >= comp->maxStep) {
348 xmlStepOpPtr temp;
349 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
350 sizeof(xmlStepOp));
351 if (temp == NULL) {
352 ERROR(ctxt, NULL, NULL,
353 "xmlPatternAdd: realloc failed\n");
354 return (-1);
355 }
356 comp->steps = temp;
357 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000358 }
359 comp->steps[comp->nbStep].op = op;
360 comp->steps[comp->nbStep].value = value;
361 comp->steps[comp->nbStep].value2 = value2;
362 comp->nbStep++;
363 return (0);
364}
365
366#if 0
367/**
368 * xsltSwapTopPattern:
369 * @comp: the compiled match expression
370 *
371 * reverse the two top steps.
372 */
373static void
374xsltSwapTopPattern(xmlPatternPtr comp) {
375 int i;
376 int j = comp->nbStep - 1;
377
378 if (j > 0) {
379 register const xmlChar *tmp;
380 register xmlPatOp op;
381 i = j - 1;
382 tmp = comp->steps[i].value;
383 comp->steps[i].value = comp->steps[j].value;
384 comp->steps[j].value = tmp;
385 tmp = comp->steps[i].value2;
386 comp->steps[i].value2 = comp->steps[j].value2;
387 comp->steps[j].value2 = tmp;
388 op = comp->steps[i].op;
389 comp->steps[i].op = comp->steps[j].op;
390 comp->steps[j].op = op;
391 }
392}
393#endif
394
395/**
396 * xmlReversePattern:
397 * @comp: the compiled match expression
398 *
399 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000400 *
401 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000402 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000403static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000404xmlReversePattern(xmlPatternPtr comp) {
Daniel Veillard56de87e2005-02-16 00:22:29 +0000405 int i, j;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000406
Daniel Veillard56de87e2005-02-16 00:22:29 +0000407 /*
408 * remove the leading // for //a or .//a
409 */
410 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
411 for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
412 comp->steps[i].value = comp->steps[j].value;
413 comp->steps[i].value2 = comp->steps[j].value2;
414 comp->steps[i].op = comp->steps[j].op;
415 }
416 comp->nbStep--;
417 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000418 if (comp->nbStep >= comp->maxStep) {
419 xmlStepOpPtr temp;
420 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
421 sizeof(xmlStepOp));
422 if (temp == NULL) {
423 ERROR(ctxt, NULL, NULL,
424 "xmlReversePattern: realloc failed\n");
425 return (-1);
426 }
427 comp->steps = temp;
428 comp->maxStep *= 2;
429 }
Daniel Veillard56de87e2005-02-16 00:22:29 +0000430 i = 0;
431 j = comp->nbStep - 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000432 while (j > i) {
433 register const xmlChar *tmp;
434 register xmlPatOp op;
435 tmp = comp->steps[i].value;
436 comp->steps[i].value = comp->steps[j].value;
437 comp->steps[j].value = tmp;
438 tmp = comp->steps[i].value2;
439 comp->steps[i].value2 = comp->steps[j].value2;
440 comp->steps[j].value2 = tmp;
441 op = comp->steps[i].op;
442 comp->steps[i].op = comp->steps[j].op;
443 comp->steps[j].op = op;
444 j--;
445 i++;
446 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000447 comp->steps[comp->nbStep].value = NULL;
448 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000449 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000450 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000451}
452
453/************************************************************************
454 * *
455 * The interpreter for the precompiled patterns *
456 * *
457 ************************************************************************/
458
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000459static int
460xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
461 if ((states->states == NULL) || (states->maxstates <= 0)) {
462 states->maxstates = 4;
463 states->nbstates = 0;
464 states->states = xmlMalloc(4 * sizeof(xmlStepState));
465 }
466 else if (states->maxstates <= states->nbstates) {
467 xmlStepState *tmp;
468
469 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
470 2 * states->maxstates * sizeof(xmlStepState));
471 if (tmp == NULL)
472 return(-1);
473 states->states = tmp;
474 states->maxstates *= 2;
475 }
476 states->states[states->nbstates].step = step;
477 states->states[states->nbstates++].node = node;
478#if 0
479 fprintf(stderr, "Push: %d, %s\n", step, node->name);
480#endif
481 return(0);
482}
483
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000484/**
485 * xmlPatMatch:
486 * @comp: the precompiled pattern
487 * @node: a node
488 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000489 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000490 *
491 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
492 */
493static int
494xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
495 int i;
496 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000497 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000498
499 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000500 i = 0;
501restart:
502 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000503 step = &comp->steps[i];
504 switch (step->op) {
505 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000506 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000507 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000508 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000509 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000510 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000511 if ((node->type == XML_DOCUMENT_NODE) ||
512#ifdef LIBXML_DOCB_ENABLED
513 (node->type == XML_DOCB_DOCUMENT_NODE) ||
514#endif
515 (node->type == XML_HTML_DOCUMENT_NODE))
516 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000517 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000518 case XML_OP_ELEM:
519 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000520 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000521 if (step->value == NULL)
522 continue;
523 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000524 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000525 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000526 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000527
528 /* Namespace test */
529 if (node->ns == NULL) {
530 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000531 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000532 } else if (node->ns->href != NULL) {
533 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000534 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000535 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000536 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000537 }
538 continue;
539 case XML_OP_CHILD: {
540 xmlNodePtr lst;
541
542 if ((node->type != XML_ELEMENT_NODE) &&
543 (node->type != XML_DOCUMENT_NODE) &&
544#ifdef LIBXML_DOCB_ENABLED
545 (node->type != XML_DOCB_DOCUMENT_NODE) &&
546#endif
547 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000548 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000549
550 lst = node->children;
551
552 if (step->value != NULL) {
553 while (lst != NULL) {
554 if ((lst->type == XML_ELEMENT_NODE) &&
555 (step->value[0] == lst->name[0]) &&
556 (xmlStrEqual(step->value, lst->name)))
557 break;
558 lst = lst->next;
559 }
560 if (lst != NULL)
561 continue;
562 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000563 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000564 }
565 case XML_OP_ATTR:
566 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000567 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000568 if (step->value != NULL) {
569 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000570 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000571 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000572 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000573 }
574 /* Namespace test */
575 if (node->ns == NULL) {
576 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000577 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000578 } else if (step->value2 != NULL) {
579 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000580 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000581 }
582 continue;
583 case XML_OP_PARENT:
584 if ((node->type == XML_DOCUMENT_NODE) ||
585 (node->type == XML_HTML_DOCUMENT_NODE) ||
586#ifdef LIBXML_DOCB_ENABLED
587 (node->type == XML_DOCB_DOCUMENT_NODE) ||
588#endif
589 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000590 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000591 node = node->parent;
592 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000593 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000594 if (step->value == NULL)
595 continue;
596 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000597 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000598 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000599 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000600 /* Namespace test */
601 if (node->ns == NULL) {
602 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000603 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000604 } else if (node->ns->href != NULL) {
605 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000606 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000607 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000608 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000609 }
610 continue;
611 case XML_OP_ANCESTOR:
612 /* TODO: implement coalescing of ANCESTOR/NODE ops */
613 if (step->value == NULL) {
614 i++;
615 step = &comp->steps[i];
616 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000617 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000618 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000619 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000620 if (step->value == NULL)
621 return(-1);
622 }
623 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000624 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000625 if ((node->type == XML_DOCUMENT_NODE) ||
626 (node->type == XML_HTML_DOCUMENT_NODE) ||
627#ifdef LIBXML_DOCB_ENABLED
628 (node->type == XML_DOCB_DOCUMENT_NODE) ||
629#endif
630 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000631 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000632 node = node->parent;
633 while (node != NULL) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000634 if ((node->type == XML_ELEMENT_NODE) &&
635 (step->value[0] == node->name[0]) &&
636 (xmlStrEqual(step->value, node->name))) {
637 /* Namespace test */
638 if (node->ns == NULL) {
639 if (step->value2 == NULL)
640 break;
641 } else if (node->ns->href != NULL) {
642 if ((step->value2 != NULL) &&
643 (xmlStrEqual(step->value2, node->ns->href)))
644 break;
645 }
646 }
647 node = node->parent;
648 }
649 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000650 goto rollback;
651 /*
652 * prepare a potential rollback from here
653 * for ancestors of that node.
654 */
655 if (step->op == XML_OP_ANCESTOR)
656 xmlPatPushState(&states, i, node);
657 else
658 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000659 continue;
660 case XML_OP_NS:
661 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000662 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000663 if (node->ns == NULL) {
664 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000665 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000666 } else if (node->ns->href != NULL) {
667 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000668 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000669 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000670 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000671 }
672 break;
673 case XML_OP_ALL:
674 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000675 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000676 break;
677 }
678 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000679found:
680 if (states.states != NULL) {
681 /* Free the rollback states */
682 xmlFree(states.states);
683 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000684 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000685rollback:
686 /* got an error try to rollback */
687 if (states.states == NULL)
688 return(0);
689 if (states.nbstates <= 0) {
690 xmlFree(states.states);
691 return(0);
692 }
693 states.nbstates--;
694 i = states.states[states.nbstates].step;
695 node = states.states[states.nbstates].node;
696#if 0
697 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
698#endif
699 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000700}
701
702/************************************************************************
703 * *
704 * Dedicated parser for templates *
705 * *
706 ************************************************************************/
707
708#define TODO \
709 xmlGenericError(xmlGenericErrorContext, \
710 "Unimplemented block at %s:%d\n", \
711 __FILE__, __LINE__);
712#define CUR (*ctxt->cur)
713#define SKIP(val) ctxt->cur += (val)
714#define NXT(val) ctxt->cur[(val)]
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000715#define PEEKPREV(val) ctxt->cur[-(val)]
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000716#define CUR_PTR ctxt->cur
717
718#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000719 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000720
721#define CURRENT (*ctxt->cur)
722#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
723
724
725#define PUSH(op, val, val2) \
726 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
727
728#define XSLT_ERROR(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000729 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000730 ctxt->error = (X); return; }
731
732#define XSLT_ERROR0(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000733 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000734 ctxt->error = (X); return(0); }
735
736#if 0
737/**
738 * xmlPatScanLiteral:
739 * @ctxt: the XPath Parser context
740 *
741 * Parse an XPath Litteral:
742 *
743 * [29] Literal ::= '"' [^"]* '"'
744 * | "'" [^']* "'"
745 *
746 * Returns the Literal parsed or NULL
747 */
748
749static xmlChar *
750xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
751 const xmlChar *q, *cur;
752 xmlChar *ret = NULL;
753 int val, len;
754
755 SKIP_BLANKS;
756 if (CUR == '"') {
757 NEXT;
758 cur = q = CUR_PTR;
759 val = xmlStringCurrentChar(NULL, cur, &len);
760 while ((IS_CHAR(val)) && (val != '"')) {
761 cur += len;
762 val = xmlStringCurrentChar(NULL, cur, &len);
763 }
764 if (!IS_CHAR(val)) {
765 ctxt->error = 1;
766 return(NULL);
767 } else {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000768 if (ctxt->dict)
769 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
770 else
771 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000772 }
773 cur += len;
774 CUR_PTR = cur;
775 } else if (CUR == '\'') {
776 NEXT;
777 cur = q = CUR_PTR;
778 val = xmlStringCurrentChar(NULL, cur, &len);
779 while ((IS_CHAR(val)) && (val != '\'')) {
780 cur += len;
781 val = xmlStringCurrentChar(NULL, cur, &len);
782 }
783 if (!IS_CHAR(val)) {
784 ctxt->error = 1;
785 return(NULL);
786 } else {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000787 if (ctxt->dict)
788 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
789 else
790 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000791 }
792 cur += len;
793 CUR_PTR = cur;
794 } else {
795 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
796 ctxt->error = 1;
797 return(NULL);
798 }
799 return(ret);
800}
801#endif
802
803/**
804 * xmlPatScanName:
805 * @ctxt: the XPath Parser context
806 *
807 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
808 * CombiningChar | Extender
809 *
810 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
811 *
812 * [6] Names ::= Name (S Name)*
813 *
814 * Returns the Name parsed or NULL
815 */
816
817static xmlChar *
818xmlPatScanName(xmlPatParserContextPtr ctxt) {
819 const xmlChar *q, *cur;
820 xmlChar *ret = NULL;
821 int val, len;
822
823 SKIP_BLANKS;
824
825 cur = q = CUR_PTR;
826 val = xmlStringCurrentChar(NULL, cur, &len);
827 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
828 return(NULL);
829
830 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
831 (val == '.') || (val == '-') ||
832 (val == '_') ||
833 (IS_COMBINING(val)) ||
834 (IS_EXTENDER(val))) {
835 cur += len;
836 val = xmlStringCurrentChar(NULL, cur, &len);
837 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000838 if (ctxt->dict)
839 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
840 else
841 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000842 CUR_PTR = cur;
843 return(ret);
844}
845
846/**
847 * xmlPatScanNCName:
848 * @ctxt: the XPath Parser context
849 *
850 * Parses a non qualified name
851 *
852 * Returns the Name parsed or NULL
853 */
854
855static xmlChar *
856xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
857 const xmlChar *q, *cur;
858 xmlChar *ret = NULL;
859 int val, len;
860
861 SKIP_BLANKS;
862
863 cur = q = CUR_PTR;
864 val = xmlStringCurrentChar(NULL, cur, &len);
865 if (!IS_LETTER(val) && (val != '_'))
866 return(NULL);
867
868 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
869 (val == '.') || (val == '-') ||
870 (val == '_') ||
871 (IS_COMBINING(val)) ||
872 (IS_EXTENDER(val))) {
873 cur += len;
874 val = xmlStringCurrentChar(NULL, cur, &len);
875 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000876 if (ctxt->dict)
877 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
878 else
879 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000880 CUR_PTR = cur;
881 return(ret);
882}
883
884#if 0
885/**
886 * xmlPatScanQName:
887 * @ctxt: the XPath Parser context
888 * @prefix: the place to store the prefix
889 *
890 * Parse a qualified name
891 *
892 * Returns the Name parsed or NULL
893 */
894
895static xmlChar *
896xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
897 xmlChar *ret = NULL;
898
899 *prefix = NULL;
900 ret = xmlPatScanNCName(ctxt);
901 if (CUR == ':') {
902 *prefix = ret;
903 NEXT;
904 ret = xmlPatScanNCName(ctxt);
905 }
906 return(ret);
907}
908#endif
909
910/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000911 * xmlCompileAttributeTest:
912 * @ctxt: the compilation context
913 *
914 * Compile an attribute test.
915 */
916static void
917xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
918 xmlChar *token = NULL;
919 xmlChar *name = NULL;
920 xmlChar *URL = NULL;
921
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000922 SKIP_BLANKS;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000923 name = xmlPatScanNCName(ctxt);
924 if (name == NULL) {
925 if (CUR == '*') {
926 PUSH(XML_OP_ATTR, NULL, NULL);
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +0000927 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000928 } else {
929 ERROR(NULL, NULL, NULL,
930 "xmlCompileAttributeTest : Name expected\n");
931 ctxt->error = 1;
932 }
933 return;
934 }
935 if (CUR == ':') {
936 int i;
937 xmlChar *prefix = name;
938
939 NEXT;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000940
941 if (IS_BLANK_CH(CUR)) {
942 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000943 XML_PAT_FREE_STRING(ctxt, prefix);
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000944 ctxt->error = 1;
945 goto error;
946 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000947 /*
948 * This is a namespace match
949 */
950 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000951 if ((prefix[0] == 'x') &&
952 (prefix[1] == 'm') &&
953 (prefix[2] == 'l') &&
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000954 (prefix[3] == 0))
955 {
956 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000957 } else {
958 for (i = 0;i < ctxt->nb_namespaces;i++) {
959 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000960 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000961 break;
962 }
963 }
964 if (i >= ctxt->nb_namespaces) {
965 ERROR5(NULL, NULL, NULL,
966 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
967 prefix);
968 ctxt->error = 1;
969 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000970 }
971 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000972 XML_PAT_FREE_STRING(ctxt, prefix);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000973 if (token == NULL) {
974 if (CUR == '*') {
975 NEXT;
976 PUSH(XML_OP_ATTR, NULL, URL);
977 } else {
978 ERROR(NULL, NULL, NULL,
979 "xmlCompileAttributeTest : Name expected\n");
980 ctxt->error = 1;
981 goto error;
982 }
983 } else {
984 PUSH(XML_OP_ATTR, token, URL);
985 }
986 } else {
987 PUSH(XML_OP_ATTR, name, NULL);
988 }
989 return;
990error:
991 if (URL != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000992 XML_PAT_FREE_STRING(ctxt, URL)
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000993 if (token != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000994 XML_PAT_FREE_STRING(ctxt, token);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000995}
996
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000997/**
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000998 * xmlCompileStepPattern:
999 * @ctxt: the compilation context
1000 *
1001 * Compile the Step Pattern and generates a precompiled
1002 * form suitable for fast matching.
1003 *
1004 * [3] Step ::= '.' | NameTest
1005 * [4] NameTest ::= QName | '*' | NCName ':' '*'
1006 */
1007
1008static void
1009xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
1010 xmlChar *token = NULL;
1011 xmlChar *name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001012 xmlChar *URL = NULL;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001013 int hasBlanks = 0;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001014
1015 SKIP_BLANKS;
1016 if (CUR == '.') {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001017 /*
1018 * Context node.
1019 */
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001020 NEXT;
1021 PUSH(XML_OP_ELEM, NULL, NULL);
1022 return;
1023 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001024 if (CUR == '@') {
1025 /*
1026 * Attribute test.
1027 */
1028 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1029 ERROR5(NULL, NULL, NULL,
1030 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1031 ctxt->error = 1;
1032 return;
1033 }
1034 NEXT;
1035 xmlCompileAttributeTest(ctxt);
1036 if (ctxt->error != 0)
1037 goto error;
1038 return;
1039 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001040 name = xmlPatScanNCName(ctxt);
1041 if (name == NULL) {
1042 if (CUR == '*') {
1043 NEXT;
1044 PUSH(XML_OP_ALL, NULL, NULL);
1045 return;
1046 } else {
1047 ERROR(NULL, NULL, NULL,
1048 "xmlCompileStepPattern : Name expected\n");
1049 ctxt->error = 1;
1050 return;
1051 }
1052 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001053 if (IS_BLANK_CH(CUR)) {
1054 hasBlanks = 1;
1055 SKIP_BLANKS;
1056 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001057 if (CUR == ':') {
1058 NEXT;
1059 if (CUR != ':') {
1060 xmlChar *prefix = name;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001061 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001062
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001063 if (hasBlanks || IS_BLANK_CH(CUR)) {
1064 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1065 ctxt->error = 1;
1066 goto error;
1067 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001068 /*
1069 * This is a namespace match
1070 */
1071 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001072 if ((prefix[0] == 'x') &&
1073 (prefix[1] == 'm') &&
1074 (prefix[2] == 'l') &&
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001075 (prefix[3] == 0))
1076 {
1077 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001078 } else {
1079 for (i = 0;i < ctxt->nb_namespaces;i++) {
1080 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001081 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001082 break;
1083 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +00001084 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001085 if (i >= ctxt->nb_namespaces) {
1086 ERROR5(NULL, NULL, NULL,
1087 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1088 prefix);
1089 ctxt->error = 1;
1090 goto error;
1091 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001092 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001093 XML_PAT_FREE_STRING(ctxt, prefix);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001094 if (token == NULL) {
1095 if (CUR == '*') {
1096 NEXT;
1097 PUSH(XML_OP_NS, URL, NULL);
1098 } else {
1099 ERROR(NULL, NULL, NULL,
1100 "xmlCompileStepPattern : Name expected\n");
1101 ctxt->error = 1;
1102 goto error;
1103 }
1104 } else {
1105 PUSH(XML_OP_ELEM, token, URL);
1106 }
1107 } else {
1108 NEXT;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001109 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1110 XML_PAT_FREE_STRING(ctxt, name);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001111 name = xmlPatScanName(ctxt);
1112 if (name == NULL) {
1113 if (CUR == '*') {
1114 NEXT;
1115 PUSH(XML_OP_ALL, NULL, NULL);
1116 return;
1117 } else {
1118 ERROR(NULL, NULL, NULL,
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001119 "xmlCompileStepPattern : QName expected\n");
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001120 ctxt->error = 1;
1121 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001122 }
1123 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001124 if (CUR == ':') {
1125 xmlChar *prefix = name;
1126 int i;
1127
1128 NEXT;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001129 if (IS_BLANK_CH(CUR)) {
1130 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1131 ctxt->error = 1;
1132 goto error;
1133 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001134 /*
1135 * This is a namespace match
1136 */
1137 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001138 if ((prefix[0] == 'x') &&
1139 (prefix[1] == 'm') &&
1140 (prefix[2] == 'l') &&
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001141 (prefix[3] == 0))
1142 {
1143 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001144 } else {
1145 for (i = 0;i < ctxt->nb_namespaces;i++) {
1146 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001147 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001148 break;
1149 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001150 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001151 if (i >= ctxt->nb_namespaces) {
1152 ERROR5(NULL, NULL, NULL,
1153 "xmlCompileStepPattern : no namespace bound "
1154 "to prefix %s\n", prefix);
1155 ctxt->error = 1;
1156 goto error;
1157 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001158 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001159 XML_PAT_FREE_STRING(ctxt, prefix);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001160 if (token == NULL) {
1161 if (CUR == '*') {
1162 NEXT;
1163 PUSH(XML_OP_NS, URL, NULL);
1164 } else {
1165 ERROR(NULL, NULL, NULL,
1166 "xmlCompileStepPattern : Name expected\n");
1167 ctxt->error = 1;
1168 goto error;
1169 }
1170 } else {
1171 PUSH(XML_OP_CHILD, token, URL);
1172 }
1173 } else
1174 PUSH(XML_OP_CHILD, name, NULL);
1175 return;
1176 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001177 XML_PAT_FREE_STRING(ctxt, name)
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001178 name = NULL;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001179 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1180 ERROR5(NULL, NULL, NULL,
1181 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1182 ctxt->error = 1;
1183 goto error;
1184 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001185 xmlCompileAttributeTest(ctxt);
1186 if (ctxt->error != 0)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001187 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001188 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001189 } else {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001190 ERROR5(NULL, NULL, NULL,
1191 "The 'element' or 'attribute' axis is expected.\n", NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001192 ctxt->error = 1;
1193 goto error;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001194 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001195 }
1196 } else if (CUR == '*') {
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001197 if (name != NULL) {
1198 ctxt->error = 1;
1199 goto error;
1200 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001201 NEXT;
1202 PUSH(XML_OP_ALL, token, NULL);
1203 } else {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001204 PUSH(XML_OP_ELEM, name, NULL);
1205 }
1206 return;
1207error:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001208 if (URL != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001209 XML_PAT_FREE_STRING(ctxt, URL)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001210 if (token != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001211 XML_PAT_FREE_STRING(ctxt, token)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001212 if (name != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001213 XML_PAT_FREE_STRING(ctxt, name)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001214}
1215
1216/**
1217 * xmlCompilePathPattern:
1218 * @ctxt: the compilation context
1219 *
1220 * Compile the Path Pattern and generates a precompiled
1221 * form suitable for fast matching.
1222 *
1223 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1224 */
1225static void
1226xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1227 SKIP_BLANKS;
William M. Brack537f1172005-06-14 22:02:59 +00001228 if (CUR == '/') {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001229 ctxt->comp->flags |= PAT_FROM_ROOT;
William M. Brackea152c02005-06-09 18:12:28 +00001230 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001231 ctxt->comp->flags |= PAT_FROM_CUR;
1232 }
William M. Brackea152c02005-06-09 18:12:28 +00001233
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001234 if ((CUR == '/') && (NXT(1) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001235 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001236 NEXT;
1237 NEXT;
1238 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001239 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001240 NEXT;
1241 NEXT;
1242 NEXT;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001243 /* Check for incompleteness. */
1244 SKIP_BLANKS;
1245 if (CUR == 0) {
1246 ERROR5(NULL, NULL, NULL,
1247 "Incomplete expression '%s'.\n", ctxt->base);
1248 ctxt->error = 1;
1249 goto error;
1250 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001251 }
1252 if (CUR == '@') {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001253 NEXT;
1254 xmlCompileAttributeTest(ctxt);
1255 SKIP_BLANKS;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001256 /* TODO: check for incompleteness */
William M. Brackfbb619f2005-06-06 13:49:18 +00001257 if (CUR != 0) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001258 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001259 if (ctxt->error != 0)
1260 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001261 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001262 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001263 if (CUR == '/') {
1264 PUSH(XML_OP_ROOT, NULL, NULL);
1265 NEXT;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001266 /* Check for incompleteness. */
1267 SKIP_BLANKS;
1268 if (CUR == 0) {
1269 ERROR5(NULL, NULL, NULL,
1270 "Incomplete expression '%s'.\n", ctxt->base);
1271 ctxt->error = 1;
1272 goto error;
1273 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001274 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001275 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001276 if (ctxt->error != 0)
1277 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001278 SKIP_BLANKS;
1279 while (CUR == '/') {
William M. Brackfbb619f2005-06-06 13:49:18 +00001280 if (NXT(1) == '/') {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001281 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1282 NEXT;
1283 NEXT;
1284 SKIP_BLANKS;
1285 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001286 if (ctxt->error != 0)
1287 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001288 } else {
1289 PUSH(XML_OP_PARENT, NULL, NULL);
1290 NEXT;
1291 SKIP_BLANKS;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001292 if (CUR == 0) {
1293 ERROR5(NULL, NULL, NULL,
1294 "Incomplete expression '%s'.\n", ctxt->base);
1295 ctxt->error = 1;
1296 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001297 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001298 xmlCompileStepPattern(ctxt);
1299 if (ctxt->error != 0)
1300 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001301 }
1302 }
1303 }
Daniel Veillard56de87e2005-02-16 00:22:29 +00001304 if (CUR != 0) {
1305 ERROR5(NULL, NULL, NULL,
1306 "Failed to compile pattern %s\n", ctxt->base);
1307 ctxt->error = 1;
1308 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001309error:
1310 return;
1311}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001312
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001313/**
1314 * xmlCompileIDCXPathPath:
1315 * @ctxt: the compilation context
1316 *
1317 * Compile the Path Pattern and generates a precompiled
1318 * form suitable for fast matching.
1319 *
1320 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1321 */
1322static void
1323xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
1324 SKIP_BLANKS;
1325 if (CUR == '/') {
1326 ERROR5(NULL, NULL, NULL,
1327 "Unexpected selection of the document root in '%s'.\n",
1328 ctxt->base);
1329 goto error;
1330 }
1331 ctxt->comp->flags |= PAT_FROM_CUR;
1332
1333 if (CUR == '.') {
1334 /* "." - "self::node()" */
1335 NEXT;
1336 SKIP_BLANKS;
1337 if (CUR == 0) {
1338 /*
1339 * Selection of the context node.
1340 */
1341 PUSH(XML_OP_ELEM, NULL, NULL);
1342 return;
1343 }
1344 if (CUR != '/') {
1345 /* TODO: A more meaningful error message. */
1346 ERROR5(NULL, NULL, NULL,
1347 "Unexpected token after '.' in '%s'.\n", ctxt->base);
1348 goto error;
1349 }
1350 /* "./" - "self::node()/" */
1351 NEXT;
1352 SKIP_BLANKS;
1353 if (CUR == '/') {
1354 if (IS_BLANK_CH(PEEKPREV(1))) {
1355 /*
1356 * Disallow "./ /"
1357 */
1358 ERROR5(NULL, NULL, NULL,
1359 "Unexpected '/' token in '%s'.\n", ctxt->base);
1360 goto error;
1361 }
1362 /* ".//" - "self:node()/descendant-or-self::node()/" */
1363 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1364 NEXT;
1365 SKIP_BLANKS;
1366 }
1367 if (CUR == 0)
1368 goto error_unfinished;
1369 }
1370 /*
1371 * Process steps.
1372 */
1373 do {
1374 xmlCompileStepPattern(ctxt);
1375 if (ctxt->error != 0)
1376 goto error;
1377 SKIP_BLANKS;
1378 if (CUR != '/')
1379 break;
1380 PUSH(XML_OP_PARENT, NULL, NULL);
1381 NEXT;
1382 SKIP_BLANKS;
1383 if (CUR == '/') {
1384 /*
1385 * Disallow subsequent '//'.
1386 */
1387 ERROR5(NULL, NULL, NULL,
1388 "Unexpected subsequent '//' in '%s'.\n",
1389 ctxt->base);
1390 goto error;
1391 }
1392 if (CUR == 0)
1393 goto error_unfinished;
1394
1395 } while (CUR != 0);
1396
1397 if (CUR != 0) {
1398 ERROR5(NULL, NULL, NULL,
1399 "Failed to compile expression '%s'.\n", ctxt->base);
1400 ctxt->error = 1;
1401 }
1402 return;
1403error:
1404 ctxt->error = 1;
1405 return;
1406
1407error_unfinished:
1408 ctxt->error = 1;
1409 ERROR5(NULL, NULL, NULL,
1410 "Unfinished expression '%s'.\n", ctxt->base);
1411 return;
1412}
1413
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001414/************************************************************************
1415 * *
1416 * The streaming code *
1417 * *
1418 ************************************************************************/
1419
1420#ifdef DEBUG_STREAMING
1421static void
1422xmlDebugStreamComp(xmlStreamCompPtr stream) {
1423 int i;
1424
1425 if (stream == NULL) {
1426 printf("Stream: NULL\n");
1427 return;
1428 }
1429 printf("Stream: %d steps\n", stream->nbStep);
1430 for (i = 0;i < stream->nbStep;i++) {
1431 if (stream->steps[i].ns != NULL) {
1432 printf("{%s}", stream->steps[i].ns);
1433 }
1434 if (stream->steps[i].name == NULL) {
1435 printf("* ");
1436 } else {
1437 printf("%s ", stream->steps[i].name);
1438 }
1439 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1440 printf("root ");
1441 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1442 printf("// ");
1443 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1444 printf("final ");
1445 printf("\n");
1446 }
1447}
1448static void
1449xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1450 int i;
1451
1452 if (ctxt == NULL) {
1453 printf("Stream: NULL\n");
1454 return;
1455 }
1456 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1457 if (match)
1458 printf("matches\n");
1459 else
1460 printf("\n");
1461 for (i = 0;i < ctxt->nbState;i++) {
1462 if (ctxt->states[2 * i] < 0)
1463 printf(" %d: free\n", i);
1464 else {
1465 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1466 ctxt->states[(2 * i) + 1]);
1467 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1468 XML_STREAM_STEP_DESC)
1469 printf(" //\n");
1470 else
1471 printf("\n");
1472 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001473 }
1474}
1475#endif
1476/**
1477 * xmlNewStreamComp:
1478 * @size: the number of expected steps
1479 *
1480 * build a new compiled pattern for streaming
1481 *
1482 * Returns the new structure or NULL in case of error.
1483 */
1484static xmlStreamCompPtr
1485xmlNewStreamComp(int size) {
1486 xmlStreamCompPtr cur;
1487
1488 if (size < 4)
1489 size = 4;
1490
1491 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1492 if (cur == NULL) {
1493 ERROR(NULL, NULL, NULL,
1494 "xmlNewStreamComp: malloc failed\n");
1495 return(NULL);
1496 }
1497 memset(cur, 0, sizeof(xmlStreamComp));
1498 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1499 if (cur->steps == NULL) {
1500 xmlFree(cur);
1501 ERROR(NULL, NULL, NULL,
1502 "xmlNewStreamComp: malloc failed\n");
1503 return(NULL);
1504 }
1505 cur->nbStep = 0;
1506 cur->maxStep = size;
1507 return(cur);
1508}
1509
1510/**
1511 * xmlFreeStreamComp:
1512 * @comp: the compiled pattern for streaming
1513 *
1514 * Free the compiled pattern for streaming
1515 */
1516static void
1517xmlFreeStreamComp(xmlStreamCompPtr comp) {
1518 if (comp != NULL) {
1519 if (comp->steps != NULL)
1520 xmlFree(comp->steps);
1521 if (comp->dict != NULL)
1522 xmlDictFree(comp->dict);
1523 xmlFree(comp);
1524 }
1525}
1526
1527/**
1528 * xmlStreamCompAddStep:
1529 * @comp: the compiled pattern for streaming
1530 * @name: the first string, the name, or NULL for *
1531 * @ns: the second step, the namespace name
1532 * @flags: the flags for that step
1533 *
1534 * Add a new step to the compiled pattern
1535 *
1536 * Returns -1 in case of error or the step index if successful
1537 */
1538static int
1539xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001540 const xmlChar *ns, int nodeType, int flags) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001541 xmlStreamStepPtr cur;
1542
1543 if (comp->nbStep >= comp->maxStep) {
1544 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1545 comp->maxStep * 2 * sizeof(xmlStreamStep));
1546 if (cur == NULL) {
1547 ERROR(NULL, NULL, NULL,
1548 "xmlNewStreamComp: malloc failed\n");
1549 return(-1);
1550 }
1551 comp->steps = cur;
1552 comp->maxStep *= 2;
1553 }
1554 cur = &comp->steps[comp->nbStep++];
1555 cur->flags = flags;
1556 cur->name = name;
1557 cur->ns = ns;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001558 cur->nodeType = nodeType;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001559 return(comp->nbStep - 1);
1560}
1561
1562/**
1563 * xmlStreamCompile:
1564 * @comp: the precompiled pattern
1565 *
1566 * Tries to stream compile a pattern
1567 *
1568 * Returns -1 in case of failure and 0 in case of success.
1569 */
1570static int
1571xmlStreamCompile(xmlPatternPtr comp) {
1572 xmlStreamCompPtr stream;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001573 int i, s = 0, root = 0, flags = 0, prevs = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001574 xmlStepOp step;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001575
1576 if ((comp == NULL) || (comp->steps == NULL))
1577 return(-1);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001578 /*
1579 * special case for .
1580 */
1581 if ((comp->nbStep == 1) &&
1582 (comp->steps[0].op == XML_OP_ELEM) &&
1583 (comp->steps[0].value == NULL) &&
1584 (comp->steps[0].value2 == NULL)) {
1585 stream = xmlNewStreamComp(0);
1586 if (stream == NULL)
1587 return(-1);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001588 /* Note that the stream will have no steps in this case. */
1589 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001590 comp->stream = stream;
1591 return(0);
1592 }
1593
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001594 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1595 if (stream == NULL)
1596 return(-1);
1597 if (comp->dict != NULL) {
1598 stream->dict = comp->dict;
1599 xmlDictReference(stream->dict);
1600 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001601
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001602 i = 0;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001603 if (comp->flags & PAT_FROM_ROOT)
1604 stream->flags |= XML_STREAM_FROM_ROOT;
1605
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001606 for (;i < comp->nbStep;i++) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001607 step = comp->steps[i];
1608 switch (step.op) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001609 case XML_OP_END:
1610 break;
1611 case XML_OP_ROOT:
1612 if (i != 0)
1613 goto error;
1614 root = 1;
1615 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001616 case XML_OP_NS:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001617 s = xmlStreamCompAddStep(stream, NULL, step.value,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001618 XML_ELEMENT_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001619 if (s < 0)
1620 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001621 prevs = s;
1622 flags = 0;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001623 break;
1624 case XML_OP_ATTR:
1625 flags |= XML_STREAM_STEP_ATTR;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001626 prevs = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001627 s = xmlStreamCompAddStep(stream,
1628 step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001629 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001630 if (s < 0)
1631 goto error;
1632 break;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001633 case XML_OP_ELEM:
1634 if ((step.value == NULL) && (step.value2 == NULL)) {
1635 /*
1636 * We have a "." or "self::node()" here.
1637 * Eliminate redundant self::node() tests like in "/./."
1638 * or "//./"
1639 * The only case we won't eliminate is "//.", i.e. if
1640 * self::node() is the last node test and we had
1641 * continuation somewhere beforehand.
1642 */
1643 if ((comp->nbStep == i + 1) &&
1644 (flags & XML_STREAM_STEP_DESC)) {
1645 /*
1646 * Mark the special case where the expression resolves
1647 * to any type of node.
1648 */
1649 if (comp->nbStep == i + 1) {
1650 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1651 }
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001652 flags |= XML_STREAM_STEP_NODE;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001653 s = xmlStreamCompAddStep(stream, NULL, NULL,
1654 XML_STREAM_ANY_NODE, flags);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001655 if (s < 0)
1656 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001657 flags = 0;
1658 /*
1659 * If there was a previous step, mark it to be added to
1660 * the result node-set; this is needed since only
1661 * the last step will be marked as "final" and only
1662 * "final" nodes are added to the resulting set.
1663 */
1664 if (prevs != -1) {
1665 stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1666 prevs = -1;
1667 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001668 break;
1669
1670 } else {
1671 /* Just skip this one. */
1672 continue;
1673 }
1674 }
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001675 /* An element node. */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001676 s = xmlStreamCompAddStep(stream, step.value, step.value2,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001677 XML_ELEMENT_NODE, flags);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001678 if (s < 0)
1679 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001680 prevs = s;
1681 flags = 0;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001682 break;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001683 case XML_OP_CHILD:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001684 /* An element node child. */
1685 s = xmlStreamCompAddStep(stream, step.value, step.value2,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001686 XML_ELEMENT_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001687 if (s < 0)
1688 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001689 prevs = s;
1690 flags = 0;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001691 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001692 case XML_OP_ALL:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001693 s = xmlStreamCompAddStep(stream, NULL, NULL,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001694 XML_ELEMENT_NODE, flags);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001695 if (s < 0)
1696 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001697 prevs = s;
1698 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001699 break;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001700 case XML_OP_PARENT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001701 break;
1702 case XML_OP_ANCESTOR:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001703 /* Skip redundant continuations. */
1704 if (flags & XML_STREAM_STEP_DESC)
1705 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001706 flags |= XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001707 /*
1708 * Mark the expression as having "//".
1709 */
1710 if ((stream->flags & XML_STREAM_DESC) == 0)
1711 stream->flags |= XML_STREAM_DESC;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001712 break;
1713 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001714 }
1715 if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1716 /*
1717 * If this should behave like a real pattern, we will mark
1718 * the first step as having "//", to be reentrant on every
1719 * tree level.
1720 */
1721 if ((stream->flags & XML_STREAM_DESC) == 0)
1722 stream->flags |= XML_STREAM_DESC;
1723
1724 if (stream->nbStep > 0) {
1725 if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0)
1726 stream->steps[0].flags |= XML_STREAM_STEP_DESC;
1727 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001728 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001729 if (stream->nbStep <= s)
1730 goto error;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001731 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1732 if (root)
1733 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1734#ifdef DEBUG_STREAMING
1735 xmlDebugStreamComp(stream);
1736#endif
1737 comp->stream = stream;
1738 return(0);
1739error:
1740 xmlFreeStreamComp(stream);
1741 return(0);
1742}
1743
1744/**
1745 * xmlNewStreamCtxt:
1746 * @size: the number of expected states
1747 *
1748 * build a new stream context
1749 *
1750 * Returns the new structure or NULL in case of error.
1751 */
1752static xmlStreamCtxtPtr
1753xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1754 xmlStreamCtxtPtr cur;
1755
1756 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1757 if (cur == NULL) {
1758 ERROR(NULL, NULL, NULL,
1759 "xmlNewStreamCtxt: malloc failed\n");
1760 return(NULL);
1761 }
1762 memset(cur, 0, sizeof(xmlStreamCtxt));
1763 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1764 if (cur->states == NULL) {
1765 xmlFree(cur);
1766 ERROR(NULL, NULL, NULL,
1767 "xmlNewStreamCtxt: malloc failed\n");
1768 return(NULL);
1769 }
1770 cur->nbState = 0;
1771 cur->maxState = 4;
1772 cur->level = 0;
1773 cur->comp = stream;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001774 cur->blockLevel = -1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001775 return(cur);
1776}
1777
1778/**
1779 * xmlFreeStreamCtxt:
1780 * @stream: the stream context
1781 *
1782 * Free the stream context
1783 */
1784void
1785xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001786 xmlStreamCtxtPtr next;
1787
1788 while (stream != NULL) {
1789 next = stream->next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001790 if (stream->states != NULL)
1791 xmlFree(stream->states);
1792 xmlFree(stream);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001793 stream = next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001794 }
1795}
1796
1797/**
1798 * xmlStreamCtxtAddState:
1799 * @comp: the stream context
1800 * @idx: the step index for that streaming state
1801 *
1802 * Add a new state to the stream context
1803 *
1804 * Returns -1 in case of error or the state index if successful
1805 */
1806static int
1807xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1808 int i;
1809 for (i = 0;i < comp->nbState;i++) {
1810 if (comp->states[2 * i] < 0) {
1811 comp->states[2 * i] = idx;
1812 comp->states[2 * i + 1] = level;
1813 return(i);
1814 }
1815 }
1816 if (comp->nbState >= comp->maxState) {
1817 int *cur;
1818
1819 cur = (int *) xmlRealloc(comp->states,
1820 comp->maxState * 4 * sizeof(int));
1821 if (cur == NULL) {
1822 ERROR(NULL, NULL, NULL,
1823 "xmlNewStreamCtxt: malloc failed\n");
1824 return(-1);
1825 }
1826 comp->states = cur;
1827 comp->maxState *= 2;
1828 }
1829 comp->states[2 * comp->nbState] = idx;
1830 comp->states[2 * comp->nbState++ + 1] = level;
1831 return(comp->nbState - 1);
1832}
1833
1834/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001835 * xmlStreamPushInternal:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001836 * @stream: the stream context
1837 * @name: the current name
1838 * @ns: the namespace name
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001839 * @nodeType: the type of the node
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001840 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001841 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1842 * indicated a dictionary, then strings for name and ns will be expected
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001843 * to come from the dictionary.
1844 * Both @name and @ns being NULL means the / i.e. the root of the document.
1845 * This can also act as a reset.
1846 *
1847 * Returns: -1 in case of error, 1 if the current state in the stream is a
1848 * match and 0 otherwise.
1849 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001850static int
1851xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1852 const xmlChar *name, const xmlChar *ns,
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001853 int nodeType) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001854 int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001855 xmlStreamCompPtr comp;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001856 xmlStreamStep step;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001857#ifdef DEBUG_STREAMING
1858 xmlStreamCtxtPtr orig = stream;
1859#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001860
1861 if ((stream == NULL) || (stream->nbState < 0))
1862 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001863
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001864 while (stream != NULL) {
1865 comp = stream->comp;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001866
1867 if ((nodeType == XML_ELEMENT_NODE) &&
1868 (name == NULL) && (ns == NULL)) {
1869 /* We have a document node here (or a reset). */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001870 stream->nbState = 0;
1871 stream->level = 0;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001872 stream->blockLevel = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001873 if (comp->flags & XML_STREAM_FROM_ROOT) {
1874 if (comp->nbStep == 0) {
1875 /* TODO: We have a "/." here? */
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001876 ret = 1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001877 } else {
1878 if ((comp->nbStep == 1) &&
1879 (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
1880 (comp->steps[0].flags & XML_STREAM_STEP_DESC))
1881 {
1882 /*
1883 * In the case of "//." the document node will match
1884 * as well.
1885 */
1886 ret = 1;
1887 } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1888 /* TODO: Do we need this ? */
1889 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1890 if (tmp < 0)
1891 err++;
1892 }
1893 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001894 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001895 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001896 continue; /* while */
1897 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001898
1899 /*
1900 * Fast check for ".".
1901 */
1902 if (comp->nbStep == 0) {
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001903 /*
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00001904 * / and . are handled at the XPath node set creation
1905 * level by checking min depth
1906 */
1907 if (stream->flags & XML_PATTERN_XPATH) {
1908 stream = stream->next;
1909 continue; /* while */
1910 }
1911 /*
William M. Brackea152c02005-06-09 18:12:28 +00001912 * For non-pattern like evaluation like XML Schema IDCs
1913 * or traditional XPath expressions, this will match if
1914 * we are at the first level only, otherwise on every level.
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001915 */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001916 if ((nodeType != XML_ATTRIBUTE_NODE) &&
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001917 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1918 (stream->level == 0))) {
1919 ret = 1;
1920 }
1921 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001922 goto stream_next;
1923 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001924 if (stream->blockLevel != -1) {
1925 /*
1926 * Skip blocked expressions.
1927 */
1928 stream->level++;
1929 goto stream_next;
William M. Brackea152c02005-06-09 18:12:28 +00001930 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001931
1932 if ((nodeType != XML_ELEMENT_NODE) &&
1933 (nodeType != XML_ATTRIBUTE_NODE) &&
1934 ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) {
1935 /*
1936 * No need to process nodes of other types if we don't
1937 * resolve to those types.
1938 * TODO: Do we need to block the context here?
1939 */
1940 stream->level++;
1941 goto stream_next;
1942 }
1943
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001944 /*
1945 * Check evolution of existing states
1946 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001947 i = 0;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001948 m = stream->nbState;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001949 while (i < m) {
1950 if ((comp->flags & XML_STREAM_DESC) == 0) {
1951 /*
1952 * If there is no "//", then only the last
1953 * added state is of interest.
1954 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001955 stepNr = stream->states[2 * (stream->nbState -1)];
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001956 /*
1957 * TODO: Security check, should not happen, remove it.
1958 */
1959 if (stream->states[(2 * (stream->nbState -1)) + 1] <
1960 stream->level) {
1961 return (-1);
1962 }
1963 desc = 0;
1964 /* loop-stopper */
1965 i = m;
1966 } else {
1967 /*
1968 * If there are "//", then we need to process every "//"
1969 * occuring in the states, plus any other state for this
1970 * level.
1971 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001972 stepNr = stream->states[2 * i];
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001973
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001974 /* TODO: should not happen anymore: dead states */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001975 if (stepNr < 0)
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001976 goto next_state;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001977
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001978 tmp = stream->states[(2 * i) + 1];
1979
1980 /* skip new states just added */
1981 if (tmp > stream->level)
1982 goto next_state;
1983
1984 /* skip states at ancestor levels, except if "//" */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001985 desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001986 if ((tmp < stream->level) && (!desc))
1987 goto next_state;
1988 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001989 /*
1990 * Check for correct node-type.
1991 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001992 step = comp->steps[stepNr];
1993 if (step.nodeType != nodeType) {
1994 if (step.nodeType == XML_ATTRIBUTE_NODE) {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001995 /*
1996 * Block this expression for deeper evaluation.
1997 */
1998 if ((comp->flags & XML_STREAM_DESC) == 0)
1999 stream->blockLevel = stream->level +1;
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002000 goto next_state;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002001 } else if (step.nodeType != XML_STREAM_ANY_NODE)
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002002 goto next_state;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002003 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002004 /*
2005 * Compare local/namespace-name.
2006 */
2007 match = 0;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002008 if (step.nodeType == XML_STREAM_ANY_NODE) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002009 match = 1;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002010 } else if (step.name == NULL) {
2011 if (step.ns == NULL) {
2012 /*
2013 * This lets through all elements/attributes.
2014 */
2015 match = 1;
2016 } else if (ns != NULL)
2017 match = xmlStrEqual(step.ns, ns);
2018 } else if (((step.ns != NULL) == (ns != NULL)) &&
2019 (name != NULL) &&
2020 (step.name[0] == name[0]) &&
2021 xmlStrEqual(step.name, name) &&
2022 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2023 {
2024 match = 1;
2025 }
2026#if 0
2027/*
2028* TODO: Pointer comparison won't work, since not guaranteed that the given
2029* values are in the same dict; especially if it's the namespace name,
2030* normally coming from ns->href. We need a namespace dict mechanism !
2031*/
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002032 } else if (comp->dict) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002033 if (step.name == NULL) {
2034 if (step.ns == NULL)
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002035 match = 1;
2036 else
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002037 match = (step.ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002038 } else {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002039 match = ((step.name == name) && (step.ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002040 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002041#endif /* if 0 ------------------------------------------------------- */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002042 if (match) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002043 final = step.flags & XML_STREAM_STEP_FINAL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002044 if (desc) {
2045 if (final) {
2046 ret = 1;
2047 } else {
2048 /* descending match create a new state */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002049 xmlStreamCtxtAddState(stream, stepNr + 1,
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002050 stream->level + 1);
2051 }
2052 } else {
2053 if (final) {
2054 ret = 1;
2055 } else {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002056 xmlStreamCtxtAddState(stream, stepNr + 1,
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002057 stream->level + 1);
2058 }
2059 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002060 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00002061 /*
2062 * Check if we have a special case like "foo/bar//.", where
2063 * "foo" is selected as well.
2064 */
2065 ret = 1;
2066 }
2067 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002068 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2069 ((! match) || final)) {
2070 /*
2071 * Mark this expression as blocked for any evaluation at
2072 * deeper levels. Note that this includes "/foo"
2073 * expressions if the *pattern* behaviour is used.
2074 */
2075 stream->blockLevel = stream->level +1;
2076 }
2077next_state:
2078 i++;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002079 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002080
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002081 stream->level++;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002082
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002083 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002084 * Re/enter the expression.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002085 * Don't reenter if it's an absolute expression like "/foo",
2086 * except "//foo".
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002087 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002088 step = comp->steps[0];
2089 if (step.flags & XML_STREAM_STEP_ROOT)
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002090 goto stream_next;
2091
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002092 desc = step.flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002093 if (stream->flags & XML_PATTERN_NOTPATTERN) {
2094 /*
2095 * Re/enter the expression if it is a "descendant" one,
2096 * or if we are at the 1st level of evaluation.
2097 */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002098
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002099 if (stream->level == 1) {
2100 if (XML_STREAM_XS_IDC(stream)) {
William M. Brackea152c02005-06-09 18:12:28 +00002101 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002102 * XS-IDC: The missing "self::node()" will always
2103 * match the first given node.
2104 */
William M. Brackea152c02005-06-09 18:12:28 +00002105 goto stream_next;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002106 } else
2107 goto compare;
2108 }
2109 /*
2110 * A "//" is always reentrant.
2111 */
2112 if (desc)
2113 goto compare;
2114
2115 /*
2116 * XS-IDC: Process the 2nd level, since the missing
2117 * "self::node()" is responsible for the 2nd level being
2118 * the real start level.
2119 */
2120 if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
2121 goto compare;
2122
2123 goto stream_next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002124 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002125
2126compare:
2127 /*
2128 * Check expected node-type.
2129 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002130 if (step.nodeType != nodeType) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002131 if (nodeType == XML_ATTRIBUTE_NODE)
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002132 goto stream_next;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002133 else if (step.nodeType != XML_STREAM_ANY_NODE)
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002134 goto stream_next;
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002135 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002136 /*
2137 * Compare local/namespace-name.
2138 */
2139 match = 0;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002140 if (step.nodeType == XML_STREAM_ANY_NODE) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002141 match = 1;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002142 } else if (step.name == NULL) {
2143 if (step.ns == NULL) {
2144 /*
2145 * This lets through all elements/attributes.
2146 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002147 match = 1;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002148 } else if (ns != NULL)
2149 match = xmlStrEqual(step.ns, ns);
2150 } else if (((step.ns != NULL) == (ns != NULL)) &&
2151 (name != NULL) &&
2152 (step.name[0] == name[0]) &&
2153 xmlStrEqual(step.name, name) &&
2154 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2155 {
2156 match = 1;
2157 }
2158 final = step.flags & XML_STREAM_STEP_FINAL;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002159 if (match) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002160 if (final)
2161 ret = 1;
2162 else
2163 xmlStreamCtxtAddState(stream, 1, stream->level);
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002164 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00002165 /*
2166 * Check if we have a special case like "foo//.", where
2167 * "foo" is selected as well.
2168 */
2169 ret = 1;
2170 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002171 }
2172 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2173 ((! match) || final)) {
2174 /*
2175 * Mark this expression as blocked for any evaluation at
2176 * deeper levels.
2177 */
2178 stream->blockLevel = stream->level;
2179 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002180
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002181stream_next:
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002182 stream = stream->next;
2183 } /* while stream != NULL */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002184
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002185 if (err > 0)
2186 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002187#ifdef DEBUG_STREAMING
2188 xmlDebugStreamCtxt(orig, ret);
2189#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002190 return(ret);
2191}
2192
2193/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002194 * xmlStreamPush:
2195 * @stream: the stream context
2196 * @name: the current name
2197 * @ns: the namespace name
2198 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002199 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2200 * indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002201 * to come from the dictionary.
2202 * Both @name and @ns being NULL means the / i.e. the root of the document.
2203 * This can also act as a reset.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002204 * Otherwise the function will act as if it has been given an element-node.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002205 *
2206 * Returns: -1 in case of error, 1 if the current state in the stream is a
2207 * match and 0 otherwise.
2208 */
2209int
2210xmlStreamPush(xmlStreamCtxtPtr stream,
2211 const xmlChar *name, const xmlChar *ns) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002212 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE));
2213}
2214
2215/**
Daniel Veillard67952602006-01-05 15:29:44 +00002216 * xmlStreamPushNode:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002217 * @stream: the stream context
2218 * @name: the current name
2219 * @ns: the namespace name
2220 * @nodeType: the type of the node being pushed
2221 *
2222 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2223 * indicated a dictionary, then strings for name and ns will be expected
2224 * to come from the dictionary.
2225 * Both @name and @ns being NULL means the / i.e. the root of the document.
2226 * This can also act as a reset.
2227 * Different from xmlStreamPush() this function can be fed with nodes of type:
2228 * element-, attribute-, text-, cdata-section-, comment- and
2229 * processing-instruction-node.
2230 *
2231 * Returns: -1 in case of error, 1 if the current state in the stream is a
2232 * match and 0 otherwise.
2233 */
2234int
2235xmlStreamPushNode(xmlStreamCtxtPtr stream,
2236 const xmlChar *name, const xmlChar *ns,
2237 int nodeType)
2238{
2239 return (xmlStreamPushInternal(stream, name, ns,
2240 nodeType));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002241}
2242
2243/**
2244* xmlStreamPushAttr:
2245* @stream: the stream context
2246* @name: the current name
2247* @ns: the namespace name
2248*
William M. Brackfbb619f2005-06-06 13:49:18 +00002249* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2250* indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002251* to come from the dictionary.
2252* Both @name and @ns being NULL means the / i.e. the root of the document.
2253* This can also act as a reset.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002254* Otherwise the function will act as if it has been given an attribute-node.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002255*
2256* Returns: -1 in case of error, 1 if the current state in the stream is a
2257* match and 0 otherwise.
2258*/
2259int
2260xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2261 const xmlChar *name, const xmlChar *ns) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002262 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002263}
2264
2265/**
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002266 * xmlStreamPop:
2267 * @stream: the stream context
2268 *
2269 * push one level from the stream.
2270 *
2271 * Returns: -1 in case of error, 0 otherwise.
2272 */
2273int
2274xmlStreamPop(xmlStreamCtxtPtr stream) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002275 int i, lev;
Kasimier T. Buchcik65c2f1d2005-10-17 12:39:58 +00002276
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002277 if (stream == NULL)
2278 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002279 while (stream != NULL) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002280 /*
2281 * Reset block-level.
2282 */
2283 if (stream->blockLevel == stream->level)
2284 stream->blockLevel = -1;
2285
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002286 stream->level--;
2287 if (stream->level < 0)
Kasimier T. Buchcik65c2f1d2005-10-17 12:39:58 +00002288 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002289 /*
2290 * Check evolution of existing states
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002291 */
2292 for (i = stream->nbState -1; i >= 0; i--) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002293 /* discard obsoleted states */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002294 lev = stream->states[(2 * i) + 1];
2295 if (lev > stream->level)
2296 stream->nbState--;
2297 if (lev <= stream->level)
2298 break;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002299 }
2300 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00002301 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002302 return(0);
2303}
2304
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002305/**
2306 * xmlStreamWantsAnyNode:
Daniel Veillard67952602006-01-05 15:29:44 +00002307 * @streamCtxt: the stream context
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002308 *
2309 * Query if the streaming pattern additionally needs to be fed with
2310 * text-, cdata-section-, comment- and processing-instruction-nodes.
2311 * If the result is 0 then only element-nodes and attribute-nodes
2312 * need to be pushed.
2313 *
2314 * Returns: 1 in case of need of nodes of the above described types,
2315 * 0 otherwise. -1 on API errors.
2316 */
2317int
2318xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
2319{
2320 if (streamCtxt == NULL)
2321 return(-1);
2322 while (streamCtxt != NULL) {
2323 if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
2324 return(1);
2325 streamCtxt = streamCtxt->next;
2326 }
2327 return(0);
2328}
2329
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002330/************************************************************************
2331 * *
2332 * The public interfaces *
2333 * *
2334 ************************************************************************/
2335
2336/**
2337 * xmlPatterncompile:
2338 * @pattern: the pattern to compile
William M. Brackfbb619f2005-06-06 13:49:18 +00002339 * @dict: an optional dictionary for interned strings
Daniel Veillarded6c5492005-07-23 15:00:22 +00002340 * @flags: compilation flags, see xmlPatternFlags
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002341 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002342 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002343 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002344 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002345 * Returns the compiled form of the pattern or NULL in case of error
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002346 */
2347xmlPatternPtr
Daniel Veillarded6c5492005-07-23 15:00:22 +00002348xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002349 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002350 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002351 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002352 const xmlChar *or, *start;
2353 xmlChar *tmp = NULL;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002354 int type = 0;
2355 int streamable = 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002356
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002357 if (pattern == NULL)
2358 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002359
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002360 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002361 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002362 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002363 tmp = NULL;
2364 while ((*or != 0) && (*or != '|')) or++;
2365 if (*or == 0)
2366 ctxt = xmlNewPatParserContext(start, dict, namespaces);
2367 else {
2368 tmp = xmlStrndup(start, or - start);
2369 if (tmp != NULL) {
2370 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2371 }
2372 or++;
2373 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002374 if (ctxt == NULL) goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002375 cur = xmlNewPattern();
2376 if (cur == NULL) goto error;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002377 /*
2378 * Assign string dict.
2379 */
2380 if (dict) {
2381 cur->dict = dict;
2382 xmlDictReference(dict);
2383 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002384 if (ret == NULL)
2385 ret = cur;
2386 else {
2387 cur->next = ret->next;
2388 ret->next = cur;
2389 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002390 cur->flags = flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002391 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002392
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002393 if (XML_STREAM_XS_IDC(cur))
2394 xmlCompileIDCXPathPath(ctxt);
2395 else
2396 xmlCompilePathPattern(ctxt);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002397 if (ctxt->error != 0)
2398 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002399 xmlFreePatParserContext(ctxt);
William M. Brackfbb619f2005-06-06 13:49:18 +00002400 ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002401
2402
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002403 if (streamable) {
2404 if (type == 0) {
2405 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2406 } else if (type == PAT_FROM_ROOT) {
2407 if (cur->flags & PAT_FROM_CUR)
2408 streamable = 0;
2409 } else if (type == PAT_FROM_CUR) {
2410 if (cur->flags & PAT_FROM_ROOT)
2411 streamable = 0;
2412 }
2413 }
2414 if (streamable)
2415 xmlStreamCompile(cur);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002416 if (xmlReversePattern(cur) < 0)
2417 goto error;
William M. Brackea152c02005-06-09 18:12:28 +00002418 if (tmp != NULL) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002419 xmlFree(tmp);
William M. Brackea152c02005-06-09 18:12:28 +00002420 tmp = NULL;
2421 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002422 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002423 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002424 if (streamable == 0) {
2425 cur = ret;
2426 while (cur != NULL) {
2427 if (cur->stream != NULL) {
2428 xmlFreeStreamComp(cur->stream);
2429 cur->stream = NULL;
2430 }
2431 cur = cur->next;
2432 }
2433 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002434
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002435 return(ret);
2436error:
2437 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2438 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002439 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002440 return(NULL);
2441}
2442
2443/**
2444 * xmlPatternMatch:
2445 * @comp: the precompiled pattern
2446 * @node: a node
2447 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002448 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002449 *
2450 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2451 */
2452int
2453xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2454{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002455 int ret = 0;
2456
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002457 if ((comp == NULL) || (node == NULL))
2458 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002459
2460 while (comp != NULL) {
2461 ret = xmlPatMatch(comp, node);
2462 if (ret != 0)
2463 return(ret);
2464 comp = comp->next;
2465 }
2466 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002467}
2468
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002469/**
2470 * xmlPatternGetStreamCtxt:
2471 * @comp: the precompiled pattern
2472 *
2473 * Get a streaming context for that pattern
2474 * Use xmlFreeStreamCtxt to free the context.
2475 *
2476 * Returns a pointer to the context or NULL in case of failure
2477 */
2478xmlStreamCtxtPtr
2479xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2480{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002481 xmlStreamCtxtPtr ret = NULL, cur;
2482
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002483 if ((comp == NULL) || (comp->stream == NULL))
2484 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002485
2486 while (comp != NULL) {
2487 if (comp->stream == NULL)
2488 goto failed;
2489 cur = xmlNewStreamCtxt(comp->stream);
2490 if (cur == NULL)
2491 goto failed;
2492 if (ret == NULL)
2493 ret = cur;
2494 else {
2495 cur->next = ret->next;
2496 ret->next = cur;
2497 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002498 cur->flags = comp->flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002499 comp = comp->next;
2500 }
2501 return(ret);
2502failed:
2503 xmlFreeStreamCtxt(ret);
2504 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002505}
2506
Daniel Veillard56de87e2005-02-16 00:22:29 +00002507/**
2508 * xmlPatternStreamable:
2509 * @comp: the precompiled pattern
2510 *
2511 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2512 * should work.
2513 *
2514 * Returns 1 if streamable, 0 if not and -1 in case of error.
2515 */
2516int
2517xmlPatternStreamable(xmlPatternPtr comp) {
2518 if (comp == NULL)
2519 return(-1);
2520 while (comp != NULL) {
2521 if (comp->stream == NULL)
2522 return(0);
2523 comp = comp->next;
2524 }
2525 return(1);
2526}
2527
2528/**
2529 * xmlPatternMaxDepth:
2530 * @comp: the precompiled pattern
2531 *
2532 * Check the maximum depth reachable by a pattern
2533 *
2534 * Returns -2 if no limit (using //), otherwise the depth,
2535 * and -1 in case of error
2536 */
2537int
2538xmlPatternMaxDepth(xmlPatternPtr comp) {
2539 int ret = 0, i;
2540 if (comp == NULL)
2541 return(-1);
2542 while (comp != NULL) {
2543 if (comp->stream == NULL)
2544 return(-1);
2545 for (i = 0;i < comp->stream->nbStep;i++)
2546 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2547 return(-2);
2548 if (comp->stream->nbStep > ret)
2549 ret = comp->stream->nbStep;
2550 comp = comp->next;
2551 }
2552 return(ret);
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002553}
Daniel Veillard56de87e2005-02-16 00:22:29 +00002554
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002555/**
2556 * xmlPatternMinDepth:
2557 * @comp: the precompiled pattern
2558 *
2559 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2560 * part of the set.
2561 *
2562 * Returns -1 in case of error otherwise the depth,
2563 *
2564 */
2565int
2566xmlPatternMinDepth(xmlPatternPtr comp) {
2567 int ret = 12345678;
2568 if (comp == NULL)
2569 return(-1);
2570 while (comp != NULL) {
2571 if (comp->stream == NULL)
2572 return(-1);
2573 if (comp->stream->nbStep < ret)
2574 ret = comp->stream->nbStep;
2575 if (ret == 0)
2576 return(0);
2577 comp = comp->next;
2578 }
2579 return(ret);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002580}
2581
2582/**
2583 * xmlPatternFromRoot:
2584 * @comp: the precompiled pattern
2585 *
2586 * Check if the pattern must be looked at from the root.
2587 *
2588 * Returns 1 if true, 0 if false and -1 in case of error
2589 */
2590int
2591xmlPatternFromRoot(xmlPatternPtr comp) {
2592 if (comp == NULL)
2593 return(-1);
2594 while (comp != NULL) {
2595 if (comp->stream == NULL)
2596 return(-1);
2597 if (comp->flags & PAT_FROM_ROOT)
2598 return(1);
2599 comp = comp->next;
2600 }
2601 return(0);
2602
2603}
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002604
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002605#define bottom_pattern
2606#include "elfgcchack.h"
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002607#endif /* LIBXML_PATTERN_ENABLED */