blob: 7b3e50820e7b5881c4f03e8df2b8758652be57f5 [file] [log] [blame]
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001/*
2 * pattern.c: Implemetation of selectors for nodes
3 *
4 * Reference:
5 * http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
6 * to some extent
7 * http://www.w3.org/TR/1999/REC-xml-19991116
8 *
9 * See Copyright for the status of this software.
10 *
11 * daniel@veillard.com
12 */
13
Daniel Veillardf9d16912005-01-30 22:36:30 +000014/*
15 * TODO:
16 * - compilation flags to check for specific syntaxes
17 * using flags of xmlPatterncompile()
18 * - making clear how pattern starting with / or . need to be handled,
19 * currently push(NULL, NULL) means a reset of the streaming context
20 * and indicating we are on / (the document node), probably need
21 * something similar for .
Daniel Veillardd4301ab2005-02-03 22:24:10 +000022 * - get rid of the "compile" starting with lowercase
23 * - get rid of the Strdup/Strndup in case of dictionary
Daniel Veillardf9d16912005-01-30 22:36:30 +000024 */
25
Daniel Veillardb3de70c2003-12-02 22:32:15 +000026#define IN_LIBXML
27#include "libxml.h"
28
29#include <string.h>
30#include <libxml/xmlmemory.h>
31#include <libxml/tree.h>
32#include <libxml/hash.h>
33#include <libxml/dict.h>
34#include <libxml/xmlerror.h>
35#include <libxml/parserInternals.h>
36#include <libxml/pattern.h>
37
Daniel Veillardd4301ab2005-02-03 22:24:10 +000038#ifdef LIBXML_PATTERN_ENABLED
Daniel Veillardb3de70c2003-12-02 22:32:15 +000039
Daniel Veillardd4301ab2005-02-03 22:24:10 +000040/* #define DEBUG_STREAMING */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000041
Daniel Veillardb3de70c2003-12-02 22:32:15 +000042#define ERROR(a, b, c, d)
43#define ERROR5(a, b, c, d, e)
44
Daniel Veillard2fc6df92005-01-30 18:42:55 +000045#define XML_STREAM_STEP_DESC 1
46#define XML_STREAM_STEP_FINAL 2
47#define XML_STREAM_STEP_ROOT 4
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +000048#define XML_STREAM_STEP_ATTR 8
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
Daniel Veillard2fc6df92005-01-30 18:42:55 +000079typedef struct _xmlStreamStep xmlStreamStep;
80typedef xmlStreamStep *xmlStreamStepPtr;
81struct _xmlStreamStep {
82 int flags; /* properties of that step */
83 const xmlChar *name; /* first string value if NULL accept all */
84 const xmlChar *ns; /* second string value */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +000085 int nodeType; /* type of node */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000086};
87
88typedef struct _xmlStreamComp xmlStreamComp;
89typedef xmlStreamComp *xmlStreamCompPtr;
90struct _xmlStreamComp {
William M. Brackfbb619f2005-06-06 13:49:18 +000091 xmlDict *dict; /* the dictionary if any */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000092 int nbStep; /* number of steps in the automata */
93 int maxStep; /* allocated number of steps */
94 xmlStreamStepPtr steps; /* the array of steps */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000095 int flags;
Daniel Veillard2fc6df92005-01-30 18:42:55 +000096};
97
98struct _xmlStreamCtxt {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +000099 struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000100 xmlStreamCompPtr comp; /* the compiled stream */
William M. Brackfbb619f2005-06-06 13:49:18 +0000101 int nbState; /* number of states in the automata */
102 int maxState; /* allocated number of states */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000103 int level; /* how deep are we ? */
104 int *states; /* the array of step indexes */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +0000105 int flags; /* validation options */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +0000106 int blockLevel;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000107};
108
109static void xmlFreeStreamComp(xmlStreamCompPtr comp);
110
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000111/*
112 * Types are private:
113 */
114
115typedef enum {
116 XML_OP_END=0,
117 XML_OP_ROOT,
118 XML_OP_ELEM,
119 XML_OP_CHILD,
120 XML_OP_ATTR,
121 XML_OP_PARENT,
122 XML_OP_ANCESTOR,
123 XML_OP_NS,
124 XML_OP_ALL
125} xmlPatOp;
126
127
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000128typedef struct _xmlStepState xmlStepState;
129typedef xmlStepState *xmlStepStatePtr;
130struct _xmlStepState {
131 int step;
132 xmlNodePtr node;
133};
134
135typedef struct _xmlStepStates xmlStepStates;
136typedef xmlStepStates *xmlStepStatesPtr;
137struct _xmlStepStates {
138 int nbstates;
139 int maxstates;
140 xmlStepStatePtr states;
141};
142
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000143typedef struct _xmlStepOp xmlStepOp;
144typedef xmlStepOp *xmlStepOpPtr;
145struct _xmlStepOp {
146 xmlPatOp op;
147 const xmlChar *value;
148 const xmlChar *value2;
149};
150
William M. Brackea152c02005-06-09 18:12:28 +0000151#define PAT_FROM_ROOT (1<<8)
152#define PAT_FROM_CUR (1<<9)
Daniel Veillard56de87e2005-02-16 00:22:29 +0000153
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000154struct _xmlPattern {
155 void *data; /* the associated template */
William M. Brackfbb619f2005-06-06 13:49:18 +0000156 xmlDictPtr dict; /* the optional dictionary */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000157 struct _xmlPattern *next; /* next pattern if | is used */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000158 const xmlChar *pattern; /* the pattern */
Daniel Veillardf5812c32005-09-03 13:43:20 +0000159 int flags; /* flags */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000160 int nbStep;
161 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000162 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000163 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000164};
165
166typedef struct _xmlPatParserContext xmlPatParserContext;
167typedef xmlPatParserContext *xmlPatParserContextPtr;
168struct _xmlPatParserContext {
169 const xmlChar *cur; /* the current char being parsed */
170 const xmlChar *base; /* the full expression */
171 int error; /* error code */
William M. Brackfbb619f2005-06-06 13:49:18 +0000172 xmlDictPtr dict; /* the dictionary if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000173 xmlPatternPtr comp; /* the result */
174 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000175 const xmlChar **namespaces; /* the namespaces definitions */
176 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000177};
178
179/************************************************************************
180 * *
181 * Type functions *
182 * *
183 ************************************************************************/
184
185/**
186 * xmlNewPattern:
187 *
188 * Create a new XSLT Pattern
189 *
190 * Returns the newly allocated xmlPatternPtr or NULL in case of error
191 */
192static xmlPatternPtr
193xmlNewPattern(void) {
194 xmlPatternPtr cur;
195
196 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
197 if (cur == NULL) {
198 ERROR(NULL, NULL, NULL,
199 "xmlNewPattern : malloc failed\n");
200 return(NULL);
201 }
202 memset(cur, 0, sizeof(xmlPattern));
203 cur->maxStep = 10;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000204 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
205 if (cur->steps == NULL) {
206 xmlFree(cur);
207 ERROR(NULL, NULL, NULL,
208 "xmlNewPattern : malloc failed\n");
209 return(NULL);
210 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000211 return(cur);
212}
213
214/**
215 * xmlFreePattern:
216 * @comp: an XSLT comp
217 *
218 * Free up the memory allocated by @comp
219 */
220void
221xmlFreePattern(xmlPatternPtr comp) {
222 xmlStepOpPtr op;
223 int i;
224
225 if (comp == NULL)
226 return;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000227 if (comp->next != NULL)
228 xmlFreePattern(comp->next);
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000229 if (comp->stream != NULL)
230 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000231 if (comp->pattern != NULL)
232 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000233 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000234 if (comp->dict == NULL) {
235 for (i = 0;i < comp->nbStep;i++) {
236 op = &comp->steps[i];
237 if (op->value != NULL)
238 xmlFree((xmlChar *) op->value);
239 if (op->value2 != NULL)
240 xmlFree((xmlChar *) op->value2);
241 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000242 }
243 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000244 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000245 if (comp->dict != NULL)
246 xmlDictFree(comp->dict);
247
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000248 memset(comp, -1, sizeof(xmlPattern));
249 xmlFree(comp);
250}
251
252/**
253 * xmlFreePatternList:
254 * @comp: an XSLT comp list
255 *
256 * Free up the memory allocated by all the elements of @comp
257 */
258void
259xmlFreePatternList(xmlPatternPtr comp) {
260 xmlPatternPtr cur;
261
262 while (comp != NULL) {
263 cur = comp;
264 comp = comp->next;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +0000265 cur->next = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000266 xmlFreePattern(cur);
267 }
268}
269
270/**
271 * xmlNewPatParserContext:
272 * @pattern: the pattern context
William M. Brackfbb619f2005-06-06 13:49:18 +0000273 * @dict: the inherited dictionary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000274 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
275 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000276 *
277 * Create a new XML pattern parser context
278 *
279 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
280 */
281static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000282xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
283 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000284 xmlPatParserContextPtr cur;
285
286 if (pattern == NULL)
287 return(NULL);
288
289 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
290 if (cur == NULL) {
291 ERROR(NULL, NULL, NULL,
292 "xmlNewPatParserContext : malloc failed\n");
293 return(NULL);
294 }
295 memset(cur, 0, sizeof(xmlPatParserContext));
296 cur->dict = dict;
297 cur->cur = pattern;
298 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000299 if (namespaces != NULL) {
300 int i;
301 for (i = 0;namespaces[2 * i] != NULL;i++);
302 cur->nb_namespaces = i;
303 } else {
304 cur->nb_namespaces = 0;
305 }
306 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000307 return(cur);
308}
309
310/**
311 * xmlFreePatParserContext:
312 * @ctxt: an XSLT parser context
313 *
314 * Free up the memory allocated by @ctxt
315 */
316static void
317xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
318 if (ctxt == NULL)
319 return;
320 memset(ctxt, -1, sizeof(xmlPatParserContext));
321 xmlFree(ctxt);
322}
323
324/**
325 * xmlPatternAdd:
326 * @comp: the compiled match expression
327 * @op: an op
328 * @value: the first value
329 * @value2: the second value
330 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000331 * Add a step to an XSLT Compiled Match
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000332 *
333 * Returns -1 in case of failure, 0 otherwise.
334 */
335static int
336xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
337 xmlPatternPtr comp,
338 xmlPatOp op, xmlChar * value, xmlChar * value2)
339{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000340 if (comp->nbStep >= comp->maxStep) {
341 xmlStepOpPtr temp;
342 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
343 sizeof(xmlStepOp));
344 if (temp == NULL) {
345 ERROR(ctxt, NULL, NULL,
346 "xmlPatternAdd: realloc failed\n");
347 return (-1);
348 }
349 comp->steps = temp;
350 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000351 }
352 comp->steps[comp->nbStep].op = op;
353 comp->steps[comp->nbStep].value = value;
354 comp->steps[comp->nbStep].value2 = value2;
355 comp->nbStep++;
356 return (0);
357}
358
359#if 0
360/**
361 * xsltSwapTopPattern:
362 * @comp: the compiled match expression
363 *
364 * reverse the two top steps.
365 */
366static void
367xsltSwapTopPattern(xmlPatternPtr comp) {
368 int i;
369 int j = comp->nbStep - 1;
370
371 if (j > 0) {
372 register const xmlChar *tmp;
373 register xmlPatOp op;
374 i = j - 1;
375 tmp = comp->steps[i].value;
376 comp->steps[i].value = comp->steps[j].value;
377 comp->steps[j].value = tmp;
378 tmp = comp->steps[i].value2;
379 comp->steps[i].value2 = comp->steps[j].value2;
380 comp->steps[j].value2 = tmp;
381 op = comp->steps[i].op;
382 comp->steps[i].op = comp->steps[j].op;
383 comp->steps[j].op = op;
384 }
385}
386#endif
387
388/**
389 * xmlReversePattern:
390 * @comp: the compiled match expression
391 *
392 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000393 *
394 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000395 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000396static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000397xmlReversePattern(xmlPatternPtr comp) {
Daniel Veillard56de87e2005-02-16 00:22:29 +0000398 int i, j;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000399
Daniel Veillard56de87e2005-02-16 00:22:29 +0000400 /*
401 * remove the leading // for //a or .//a
402 */
403 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
404 for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
405 comp->steps[i].value = comp->steps[j].value;
406 comp->steps[i].value2 = comp->steps[j].value2;
407 comp->steps[i].op = comp->steps[j].op;
408 }
409 comp->nbStep--;
410 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000411 if (comp->nbStep >= comp->maxStep) {
412 xmlStepOpPtr temp;
413 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
414 sizeof(xmlStepOp));
415 if (temp == NULL) {
416 ERROR(ctxt, NULL, NULL,
417 "xmlReversePattern: realloc failed\n");
418 return (-1);
419 }
420 comp->steps = temp;
421 comp->maxStep *= 2;
422 }
Daniel Veillard56de87e2005-02-16 00:22:29 +0000423 i = 0;
424 j = comp->nbStep - 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000425 while (j > i) {
426 register const xmlChar *tmp;
427 register xmlPatOp op;
428 tmp = comp->steps[i].value;
429 comp->steps[i].value = comp->steps[j].value;
430 comp->steps[j].value = tmp;
431 tmp = comp->steps[i].value2;
432 comp->steps[i].value2 = comp->steps[j].value2;
433 comp->steps[j].value2 = tmp;
434 op = comp->steps[i].op;
435 comp->steps[i].op = comp->steps[j].op;
436 comp->steps[j].op = op;
437 j--;
438 i++;
439 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000440 comp->steps[comp->nbStep].value = NULL;
441 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000442 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000443 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000444}
445
446/************************************************************************
447 * *
448 * The interpreter for the precompiled patterns *
449 * *
450 ************************************************************************/
451
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000452static int
453xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
454 if ((states->states == NULL) || (states->maxstates <= 0)) {
455 states->maxstates = 4;
456 states->nbstates = 0;
457 states->states = xmlMalloc(4 * sizeof(xmlStepState));
458 }
459 else if (states->maxstates <= states->nbstates) {
460 xmlStepState *tmp;
461
462 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
463 2 * states->maxstates * sizeof(xmlStepState));
464 if (tmp == NULL)
465 return(-1);
466 states->states = tmp;
467 states->maxstates *= 2;
468 }
469 states->states[states->nbstates].step = step;
470 states->states[states->nbstates++].node = node;
471#if 0
472 fprintf(stderr, "Push: %d, %s\n", step, node->name);
473#endif
474 return(0);
475}
476
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000477/**
478 * xmlPatMatch:
479 * @comp: the precompiled pattern
480 * @node: a node
481 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000482 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000483 *
484 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
485 */
486static int
487xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
488 int i;
489 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000490 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000491
492 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000493 i = 0;
494restart:
495 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000496 step = &comp->steps[i];
497 switch (step->op) {
498 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000499 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000500 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000501 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000502 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000503 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000504 if ((node->type == XML_DOCUMENT_NODE) ||
505#ifdef LIBXML_DOCB_ENABLED
506 (node->type == XML_DOCB_DOCUMENT_NODE) ||
507#endif
508 (node->type == XML_HTML_DOCUMENT_NODE))
509 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000510 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000511 case XML_OP_ELEM:
512 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000513 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000514 if (step->value == NULL)
515 continue;
516 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000517 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000518 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000519 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000520
521 /* Namespace test */
522 if (node->ns == NULL) {
523 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000524 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000525 } else if (node->ns->href != NULL) {
526 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000527 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000528 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000529 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000530 }
531 continue;
532 case XML_OP_CHILD: {
533 xmlNodePtr lst;
534
535 if ((node->type != XML_ELEMENT_NODE) &&
536 (node->type != XML_DOCUMENT_NODE) &&
537#ifdef LIBXML_DOCB_ENABLED
538 (node->type != XML_DOCB_DOCUMENT_NODE) &&
539#endif
540 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000541 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000542
543 lst = node->children;
544
545 if (step->value != NULL) {
546 while (lst != NULL) {
547 if ((lst->type == XML_ELEMENT_NODE) &&
548 (step->value[0] == lst->name[0]) &&
549 (xmlStrEqual(step->value, lst->name)))
550 break;
551 lst = lst->next;
552 }
553 if (lst != NULL)
554 continue;
555 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000556 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000557 }
558 case XML_OP_ATTR:
559 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000560 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000561 if (step->value != NULL) {
562 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000563 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000564 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000565 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000566 }
567 /* Namespace test */
568 if (node->ns == NULL) {
569 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000570 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000571 } else if (step->value2 != NULL) {
572 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000573 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000574 }
575 continue;
576 case XML_OP_PARENT:
577 if ((node->type == XML_DOCUMENT_NODE) ||
578 (node->type == XML_HTML_DOCUMENT_NODE) ||
579#ifdef LIBXML_DOCB_ENABLED
580 (node->type == XML_DOCB_DOCUMENT_NODE) ||
581#endif
582 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000583 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000584 node = node->parent;
585 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000586 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000587 if (step->value == NULL)
588 continue;
589 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000590 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000591 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000592 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000593 /* Namespace test */
594 if (node->ns == NULL) {
595 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000596 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000597 } else if (node->ns->href != NULL) {
598 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000599 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000600 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000601 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000602 }
603 continue;
604 case XML_OP_ANCESTOR:
605 /* TODO: implement coalescing of ANCESTOR/NODE ops */
606 if (step->value == NULL) {
607 i++;
608 step = &comp->steps[i];
609 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000610 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000611 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000612 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000613 if (step->value == NULL)
614 return(-1);
615 }
616 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000617 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000618 if ((node->type == XML_DOCUMENT_NODE) ||
619 (node->type == XML_HTML_DOCUMENT_NODE) ||
620#ifdef LIBXML_DOCB_ENABLED
621 (node->type == XML_DOCB_DOCUMENT_NODE) ||
622#endif
623 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000624 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000625 node = node->parent;
626 while (node != NULL) {
627 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000628 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000629 if ((node->type == XML_ELEMENT_NODE) &&
630 (step->value[0] == node->name[0]) &&
631 (xmlStrEqual(step->value, node->name))) {
632 /* Namespace test */
633 if (node->ns == NULL) {
634 if (step->value2 == NULL)
635 break;
636 } else if (node->ns->href != NULL) {
637 if ((step->value2 != NULL) &&
638 (xmlStrEqual(step->value2, node->ns->href)))
639 break;
640 }
641 }
642 node = node->parent;
643 }
644 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000645 goto rollback;
646 /*
647 * prepare a potential rollback from here
648 * for ancestors of that node.
649 */
650 if (step->op == XML_OP_ANCESTOR)
651 xmlPatPushState(&states, i, node);
652 else
653 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000654 continue;
655 case XML_OP_NS:
656 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000657 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000658 if (node->ns == NULL) {
659 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000660 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000661 } else if (node->ns->href != NULL) {
662 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000663 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000664 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000665 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000666 }
667 break;
668 case XML_OP_ALL:
669 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000670 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000671 break;
672 }
673 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000674found:
675 if (states.states != NULL) {
676 /* Free the rollback states */
677 xmlFree(states.states);
678 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000679 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000680rollback:
681 /* got an error try to rollback */
682 if (states.states == NULL)
683 return(0);
684 if (states.nbstates <= 0) {
685 xmlFree(states.states);
686 return(0);
687 }
688 states.nbstates--;
689 i = states.states[states.nbstates].step;
690 node = states.states[states.nbstates].node;
691#if 0
692 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
693#endif
694 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000695}
696
697/************************************************************************
698 * *
699 * Dedicated parser for templates *
700 * *
701 ************************************************************************/
702
703#define TODO \
704 xmlGenericError(xmlGenericErrorContext, \
705 "Unimplemented block at %s:%d\n", \
706 __FILE__, __LINE__);
707#define CUR (*ctxt->cur)
708#define SKIP(val) ctxt->cur += (val)
709#define NXT(val) ctxt->cur[(val)]
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000710#define PEEKPREV(val) ctxt->cur[-(val)]
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000711#define CUR_PTR ctxt->cur
712
713#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000714 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000715
716#define CURRENT (*ctxt->cur)
717#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
718
719
720#define PUSH(op, val, val2) \
721 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
722
723#define XSLT_ERROR(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000724 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000725 ctxt->error = (X); return; }
726
727#define XSLT_ERROR0(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000728 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000729 ctxt->error = (X); return(0); }
730
731#if 0
732/**
733 * xmlPatScanLiteral:
734 * @ctxt: the XPath Parser context
735 *
736 * Parse an XPath Litteral:
737 *
738 * [29] Literal ::= '"' [^"]* '"'
739 * | "'" [^']* "'"
740 *
741 * Returns the Literal parsed or NULL
742 */
743
744static xmlChar *
745xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
746 const xmlChar *q, *cur;
747 xmlChar *ret = NULL;
748 int val, len;
749
750 SKIP_BLANKS;
751 if (CUR == '"') {
752 NEXT;
753 cur = q = CUR_PTR;
754 val = xmlStringCurrentChar(NULL, cur, &len);
755 while ((IS_CHAR(val)) && (val != '"')) {
756 cur += len;
757 val = xmlStringCurrentChar(NULL, cur, &len);
758 }
759 if (!IS_CHAR(val)) {
760 ctxt->error = 1;
761 return(NULL);
762 } else {
763 ret = xmlStrndup(q, cur - q);
764 }
765 cur += len;
766 CUR_PTR = cur;
767 } else if (CUR == '\'') {
768 NEXT;
769 cur = q = CUR_PTR;
770 val = xmlStringCurrentChar(NULL, cur, &len);
771 while ((IS_CHAR(val)) && (val != '\'')) {
772 cur += len;
773 val = xmlStringCurrentChar(NULL, cur, &len);
774 }
775 if (!IS_CHAR(val)) {
776 ctxt->error = 1;
777 return(NULL);
778 } else {
779 ret = xmlStrndup(q, cur - q);
780 }
781 cur += len;
782 CUR_PTR = cur;
783 } else {
784 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
785 ctxt->error = 1;
786 return(NULL);
787 }
788 return(ret);
789}
790#endif
791
792/**
793 * xmlPatScanName:
794 * @ctxt: the XPath Parser context
795 *
796 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
797 * CombiningChar | Extender
798 *
799 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
800 *
801 * [6] Names ::= Name (S Name)*
802 *
803 * Returns the Name parsed or NULL
804 */
805
806static xmlChar *
807xmlPatScanName(xmlPatParserContextPtr ctxt) {
808 const xmlChar *q, *cur;
809 xmlChar *ret = NULL;
810 int val, len;
811
812 SKIP_BLANKS;
813
814 cur = q = CUR_PTR;
815 val = xmlStringCurrentChar(NULL, cur, &len);
816 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
817 return(NULL);
818
819 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
820 (val == '.') || (val == '-') ||
821 (val == '_') ||
822 (IS_COMBINING(val)) ||
823 (IS_EXTENDER(val))) {
824 cur += len;
825 val = xmlStringCurrentChar(NULL, cur, &len);
826 }
827 ret = xmlStrndup(q, cur - q);
828 CUR_PTR = cur;
829 return(ret);
830}
831
832/**
833 * xmlPatScanNCName:
834 * @ctxt: the XPath Parser context
835 *
836 * Parses a non qualified name
837 *
838 * Returns the Name parsed or NULL
839 */
840
841static xmlChar *
842xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
843 const xmlChar *q, *cur;
844 xmlChar *ret = NULL;
845 int val, len;
846
847 SKIP_BLANKS;
848
849 cur = q = CUR_PTR;
850 val = xmlStringCurrentChar(NULL, cur, &len);
851 if (!IS_LETTER(val) && (val != '_'))
852 return(NULL);
853
854 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
855 (val == '.') || (val == '-') ||
856 (val == '_') ||
857 (IS_COMBINING(val)) ||
858 (IS_EXTENDER(val))) {
859 cur += len;
860 val = xmlStringCurrentChar(NULL, cur, &len);
861 }
862 ret = xmlStrndup(q, cur - q);
863 CUR_PTR = cur;
864 return(ret);
865}
866
867#if 0
868/**
869 * xmlPatScanQName:
870 * @ctxt: the XPath Parser context
871 * @prefix: the place to store the prefix
872 *
873 * Parse a qualified name
874 *
875 * Returns the Name parsed or NULL
876 */
877
878static xmlChar *
879xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
880 xmlChar *ret = NULL;
881
882 *prefix = NULL;
883 ret = xmlPatScanNCName(ctxt);
884 if (CUR == ':') {
885 *prefix = ret;
886 NEXT;
887 ret = xmlPatScanNCName(ctxt);
888 }
889 return(ret);
890}
891#endif
892
893/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000894 * xmlCompileAttributeTest:
895 * @ctxt: the compilation context
896 *
897 * Compile an attribute test.
898 */
899static void
900xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
901 xmlChar *token = NULL;
902 xmlChar *name = NULL;
903 xmlChar *URL = NULL;
904
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000905 SKIP_BLANKS;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000906 name = xmlPatScanNCName(ctxt);
907 if (name == NULL) {
908 if (CUR == '*') {
909 PUSH(XML_OP_ATTR, NULL, NULL);
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +0000910 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000911 } else {
912 ERROR(NULL, NULL, NULL,
913 "xmlCompileAttributeTest : Name expected\n");
914 ctxt->error = 1;
915 }
916 return;
917 }
918 if (CUR == ':') {
919 int i;
920 xmlChar *prefix = name;
921
922 NEXT;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000923
924 if (IS_BLANK_CH(CUR)) {
925 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
926 xmlFree(prefix);
927 ctxt->error = 1;
928 goto error;
929 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000930 /*
931 * This is a namespace match
932 */
933 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000934 if ((prefix[0] == 'x') &&
935 (prefix[1] == 'm') &&
936 (prefix[2] == 'l') &&
937 (prefix[3] == 0)) {
938 URL = xmlStrdup(XML_XML_NAMESPACE);
939 } else {
940 for (i = 0;i < ctxt->nb_namespaces;i++) {
941 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
942 URL = xmlStrdup(ctxt->namespaces[2 * i]);
943 break;
944 }
945 }
946 if (i >= ctxt->nb_namespaces) {
947 ERROR5(NULL, NULL, NULL,
948 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
949 prefix);
950 ctxt->error = 1;
951 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000952 }
953 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000954 xmlFree(prefix);
955 if (token == NULL) {
956 if (CUR == '*') {
957 NEXT;
958 PUSH(XML_OP_ATTR, NULL, URL);
959 } else {
960 ERROR(NULL, NULL, NULL,
961 "xmlCompileAttributeTest : Name expected\n");
962 ctxt->error = 1;
963 goto error;
964 }
965 } else {
966 PUSH(XML_OP_ATTR, token, URL);
967 }
968 } else {
969 PUSH(XML_OP_ATTR, name, NULL);
970 }
971 return;
972error:
973 if (URL != NULL)
974 xmlFree(URL);
975 if (token != NULL)
976 xmlFree(token);
977}
978
979
980/**
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000981 * xmlCompileStepPattern:
982 * @ctxt: the compilation context
983 *
984 * Compile the Step Pattern and generates a precompiled
985 * form suitable for fast matching.
986 *
987 * [3] Step ::= '.' | NameTest
988 * [4] NameTest ::= QName | '*' | NCName ':' '*'
989 */
990
991static void
992xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
993 xmlChar *token = NULL;
994 xmlChar *name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000995 xmlChar *URL = NULL;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000996 int hasBlanks = 0;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000997
998 SKIP_BLANKS;
999 if (CUR == '.') {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001000 /*
1001 * Context node.
1002 */
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001003 NEXT;
1004 PUSH(XML_OP_ELEM, NULL, NULL);
1005 return;
1006 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001007 if (CUR == '@') {
1008 /*
1009 * Attribute test.
1010 */
1011 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1012 ERROR5(NULL, NULL, NULL,
1013 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1014 ctxt->error = 1;
1015 return;
1016 }
1017 NEXT;
1018 xmlCompileAttributeTest(ctxt);
1019 if (ctxt->error != 0)
1020 goto error;
1021 return;
1022 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001023 name = xmlPatScanNCName(ctxt);
1024 if (name == NULL) {
1025 if (CUR == '*') {
1026 NEXT;
1027 PUSH(XML_OP_ALL, NULL, NULL);
1028 return;
1029 } else {
1030 ERROR(NULL, NULL, NULL,
1031 "xmlCompileStepPattern : Name expected\n");
1032 ctxt->error = 1;
1033 return;
1034 }
1035 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001036 if (IS_BLANK_CH(CUR)) {
1037 hasBlanks = 1;
1038 SKIP_BLANKS;
1039 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001040 if (CUR == ':') {
1041 NEXT;
1042 if (CUR != ':') {
1043 xmlChar *prefix = name;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001044 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001045
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001046 if (hasBlanks || IS_BLANK_CH(CUR)) {
1047 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1048 ctxt->error = 1;
1049 goto error;
1050 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001051 /*
1052 * This is a namespace match
1053 */
1054 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001055 if ((prefix[0] == 'x') &&
1056 (prefix[1] == 'm') &&
1057 (prefix[2] == 'l') &&
1058 (prefix[3] == 0)) {
1059 URL = xmlStrdup(XML_XML_NAMESPACE);
1060 } else {
1061 for (i = 0;i < ctxt->nb_namespaces;i++) {
1062 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1063 URL = xmlStrdup(ctxt->namespaces[2 * i]);
1064 break;
1065 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +00001066 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001067 if (i >= ctxt->nb_namespaces) {
1068 ERROR5(NULL, NULL, NULL,
1069 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1070 prefix);
1071 ctxt->error = 1;
1072 goto error;
1073 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001074 }
1075 xmlFree(prefix);
1076 if (token == NULL) {
1077 if (CUR == '*') {
1078 NEXT;
1079 PUSH(XML_OP_NS, URL, NULL);
1080 } else {
1081 ERROR(NULL, NULL, NULL,
1082 "xmlCompileStepPattern : Name expected\n");
1083 ctxt->error = 1;
1084 goto error;
1085 }
1086 } else {
1087 PUSH(XML_OP_ELEM, token, URL);
1088 }
1089 } else {
1090 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001091 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1092 xmlFree(name);
1093 name = xmlPatScanName(ctxt);
1094 if (name == NULL) {
1095 if (CUR == '*') {
1096 NEXT;
1097 PUSH(XML_OP_ALL, NULL, NULL);
1098 return;
1099 } else {
1100 ERROR(NULL, NULL, NULL,
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001101 "xmlCompileStepPattern : QName expected\n");
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001102 ctxt->error = 1;
1103 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001104 }
1105 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001106 if (CUR == ':') {
1107 xmlChar *prefix = name;
1108 int i;
1109
1110 NEXT;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001111 if (IS_BLANK_CH(CUR)) {
1112 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1113 ctxt->error = 1;
1114 goto error;
1115 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001116 /*
1117 * This is a namespace match
1118 */
1119 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001120 if ((prefix[0] == 'x') &&
1121 (prefix[1] == 'm') &&
1122 (prefix[2] == 'l') &&
1123 (prefix[3] == 0)) {
1124 URL = xmlStrdup(XML_XML_NAMESPACE);
1125 } else {
1126 for (i = 0;i < ctxt->nb_namespaces;i++) {
1127 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1128 URL = xmlStrdup(ctxt->namespaces[2 * i]);
1129 break;
1130 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001131 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001132 if (i >= ctxt->nb_namespaces) {
1133 ERROR5(NULL, NULL, NULL,
1134 "xmlCompileStepPattern : no namespace bound "
1135 "to prefix %s\n", prefix);
1136 ctxt->error = 1;
1137 goto error;
1138 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001139 }
1140 xmlFree(prefix);
1141 if (token == NULL) {
1142 if (CUR == '*') {
1143 NEXT;
1144 PUSH(XML_OP_NS, URL, NULL);
1145 } else {
1146 ERROR(NULL, NULL, NULL,
1147 "xmlCompileStepPattern : Name expected\n");
1148 ctxt->error = 1;
1149 goto error;
1150 }
1151 } else {
1152 PUSH(XML_OP_CHILD, token, URL);
1153 }
1154 } else
1155 PUSH(XML_OP_CHILD, name, NULL);
1156 return;
1157 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1158 xmlFree(name);
1159 name = NULL;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001160 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1161 ERROR5(NULL, NULL, NULL,
1162 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1163 ctxt->error = 1;
1164 goto error;
1165 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001166 xmlCompileAttributeTest(ctxt);
1167 if (ctxt->error != 0)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001168 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001169 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001170 } else {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001171 ERROR5(NULL, NULL, NULL,
1172 "The 'element' or 'attribute' axis is expected.\n", NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001173 ctxt->error = 1;
1174 goto error;
1175 }
Daniel Veillard0e460da2005-03-30 22:47:10 +00001176 /* NOT REACHED xmlFree(name); */
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001177 }
1178 } else if (CUR == '*') {
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001179 if (name != NULL) {
1180 ctxt->error = 1;
1181 goto error;
1182 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001183 NEXT;
1184 PUSH(XML_OP_ALL, token, NULL);
1185 } else {
1186 if (name == NULL) {
1187 ctxt->error = 1;
1188 goto error;
1189 }
1190 PUSH(XML_OP_ELEM, name, NULL);
1191 }
1192 return;
1193error:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001194 if (URL != NULL)
1195 xmlFree(URL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001196 if (token != NULL)
1197 xmlFree(token);
1198 if (name != NULL)
1199 xmlFree(name);
1200}
1201
1202/**
1203 * xmlCompilePathPattern:
1204 * @ctxt: the compilation context
1205 *
1206 * Compile the Path Pattern and generates a precompiled
1207 * form suitable for fast matching.
1208 *
1209 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1210 */
1211static void
1212xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1213 SKIP_BLANKS;
William M. Brack537f1172005-06-14 22:02:59 +00001214 if (CUR == '/') {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001215 ctxt->comp->flags |= PAT_FROM_ROOT;
William M. Brackea152c02005-06-09 18:12:28 +00001216 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001217 ctxt->comp->flags |= PAT_FROM_CUR;
1218 }
William M. Brackea152c02005-06-09 18:12:28 +00001219
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001220 if ((CUR == '/') && (NXT(1) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001221 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001222 NEXT;
1223 NEXT;
1224 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001225 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001226 NEXT;
1227 NEXT;
1228 NEXT;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001229 /* Check for incompleteness. */
1230 SKIP_BLANKS;
1231 if (CUR == 0) {
1232 ERROR5(NULL, NULL, NULL,
1233 "Incomplete expression '%s'.\n", ctxt->base);
1234 ctxt->error = 1;
1235 goto error;
1236 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001237 }
1238 if (CUR == '@') {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001239 NEXT;
1240 xmlCompileAttributeTest(ctxt);
1241 SKIP_BLANKS;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001242 /* TODO: check for incompleteness */
William M. Brackfbb619f2005-06-06 13:49:18 +00001243 if (CUR != 0) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001244 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001245 if (ctxt->error != 0)
1246 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001247 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001248 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001249 if (CUR == '/') {
1250 PUSH(XML_OP_ROOT, NULL, NULL);
1251 NEXT;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001252 /* Check for incompleteness. */
1253 SKIP_BLANKS;
1254 if (CUR == 0) {
1255 ERROR5(NULL, NULL, NULL,
1256 "Incomplete expression '%s'.\n", ctxt->base);
1257 ctxt->error = 1;
1258 goto error;
1259 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001260 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001261 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001262 if (ctxt->error != 0)
1263 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001264 SKIP_BLANKS;
1265 while (CUR == '/') {
William M. Brackfbb619f2005-06-06 13:49:18 +00001266 if (NXT(1) == '/') {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001267 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1268 NEXT;
1269 NEXT;
1270 SKIP_BLANKS;
1271 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001272 if (ctxt->error != 0)
1273 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001274 } else {
1275 PUSH(XML_OP_PARENT, NULL, NULL);
1276 NEXT;
1277 SKIP_BLANKS;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001278 if (CUR == 0) {
1279 ERROR5(NULL, NULL, NULL,
1280 "Incomplete expression '%s'.\n", ctxt->base);
1281 ctxt->error = 1;
1282 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001283 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001284 xmlCompileStepPattern(ctxt);
1285 if (ctxt->error != 0)
1286 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001287 }
1288 }
1289 }
Daniel Veillard56de87e2005-02-16 00:22:29 +00001290 if (CUR != 0) {
1291 ERROR5(NULL, NULL, NULL,
1292 "Failed to compile pattern %s\n", ctxt->base);
1293 ctxt->error = 1;
1294 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001295error:
1296 return;
1297}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001298
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001299/**
1300 * xmlCompileIDCXPathPath:
1301 * @ctxt: the compilation context
1302 *
1303 * Compile the Path Pattern and generates a precompiled
1304 * form suitable for fast matching.
1305 *
1306 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1307 */
1308static void
1309xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
1310 SKIP_BLANKS;
1311 if (CUR == '/') {
1312 ERROR5(NULL, NULL, NULL,
1313 "Unexpected selection of the document root in '%s'.\n",
1314 ctxt->base);
1315 goto error;
1316 }
1317 ctxt->comp->flags |= PAT_FROM_CUR;
1318
1319 if (CUR == '.') {
1320 /* "." - "self::node()" */
1321 NEXT;
1322 SKIP_BLANKS;
1323 if (CUR == 0) {
1324 /*
1325 * Selection of the context node.
1326 */
1327 PUSH(XML_OP_ELEM, NULL, NULL);
1328 return;
1329 }
1330 if (CUR != '/') {
1331 /* TODO: A more meaningful error message. */
1332 ERROR5(NULL, NULL, NULL,
1333 "Unexpected token after '.' in '%s'.\n", ctxt->base);
1334 goto error;
1335 }
1336 /* "./" - "self::node()/" */
1337 NEXT;
1338 SKIP_BLANKS;
1339 if (CUR == '/') {
1340 if (IS_BLANK_CH(PEEKPREV(1))) {
1341 /*
1342 * Disallow "./ /"
1343 */
1344 ERROR5(NULL, NULL, NULL,
1345 "Unexpected '/' token in '%s'.\n", ctxt->base);
1346 goto error;
1347 }
1348 /* ".//" - "self:node()/descendant-or-self::node()/" */
1349 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1350 NEXT;
1351 SKIP_BLANKS;
1352 }
1353 if (CUR == 0)
1354 goto error_unfinished;
1355 }
1356 /*
1357 * Process steps.
1358 */
1359 do {
1360 xmlCompileStepPattern(ctxt);
1361 if (ctxt->error != 0)
1362 goto error;
1363 SKIP_BLANKS;
1364 if (CUR != '/')
1365 break;
1366 PUSH(XML_OP_PARENT, NULL, NULL);
1367 NEXT;
1368 SKIP_BLANKS;
1369 if (CUR == '/') {
1370 /*
1371 * Disallow subsequent '//'.
1372 */
1373 ERROR5(NULL, NULL, NULL,
1374 "Unexpected subsequent '//' in '%s'.\n",
1375 ctxt->base);
1376 goto error;
1377 }
1378 if (CUR == 0)
1379 goto error_unfinished;
1380
1381 } while (CUR != 0);
1382
1383 if (CUR != 0) {
1384 ERROR5(NULL, NULL, NULL,
1385 "Failed to compile expression '%s'.\n", ctxt->base);
1386 ctxt->error = 1;
1387 }
1388 return;
1389error:
1390 ctxt->error = 1;
1391 return;
1392
1393error_unfinished:
1394 ctxt->error = 1;
1395 ERROR5(NULL, NULL, NULL,
1396 "Unfinished expression '%s'.\n", ctxt->base);
1397 return;
1398}
1399
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001400/************************************************************************
1401 * *
1402 * The streaming code *
1403 * *
1404 ************************************************************************/
1405
1406#ifdef DEBUG_STREAMING
1407static void
1408xmlDebugStreamComp(xmlStreamCompPtr stream) {
1409 int i;
1410
1411 if (stream == NULL) {
1412 printf("Stream: NULL\n");
1413 return;
1414 }
1415 printf("Stream: %d steps\n", stream->nbStep);
1416 for (i = 0;i < stream->nbStep;i++) {
1417 if (stream->steps[i].ns != NULL) {
1418 printf("{%s}", stream->steps[i].ns);
1419 }
1420 if (stream->steps[i].name == NULL) {
1421 printf("* ");
1422 } else {
1423 printf("%s ", stream->steps[i].name);
1424 }
1425 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1426 printf("root ");
1427 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1428 printf("// ");
1429 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1430 printf("final ");
1431 printf("\n");
1432 }
1433}
1434static void
1435xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1436 int i;
1437
1438 if (ctxt == NULL) {
1439 printf("Stream: NULL\n");
1440 return;
1441 }
1442 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1443 if (match)
1444 printf("matches\n");
1445 else
1446 printf("\n");
1447 for (i = 0;i < ctxt->nbState;i++) {
1448 if (ctxt->states[2 * i] < 0)
1449 printf(" %d: free\n", i);
1450 else {
1451 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1452 ctxt->states[(2 * i) + 1]);
1453 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1454 XML_STREAM_STEP_DESC)
1455 printf(" //\n");
1456 else
1457 printf("\n");
1458 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001459 }
1460}
1461#endif
1462/**
1463 * xmlNewStreamComp:
1464 * @size: the number of expected steps
1465 *
1466 * build a new compiled pattern for streaming
1467 *
1468 * Returns the new structure or NULL in case of error.
1469 */
1470static xmlStreamCompPtr
1471xmlNewStreamComp(int size) {
1472 xmlStreamCompPtr cur;
1473
1474 if (size < 4)
1475 size = 4;
1476
1477 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1478 if (cur == NULL) {
1479 ERROR(NULL, NULL, NULL,
1480 "xmlNewStreamComp: malloc failed\n");
1481 return(NULL);
1482 }
1483 memset(cur, 0, sizeof(xmlStreamComp));
1484 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1485 if (cur->steps == NULL) {
1486 xmlFree(cur);
1487 ERROR(NULL, NULL, NULL,
1488 "xmlNewStreamComp: malloc failed\n");
1489 return(NULL);
1490 }
1491 cur->nbStep = 0;
1492 cur->maxStep = size;
1493 return(cur);
1494}
1495
1496/**
1497 * xmlFreeStreamComp:
1498 * @comp: the compiled pattern for streaming
1499 *
1500 * Free the compiled pattern for streaming
1501 */
1502static void
1503xmlFreeStreamComp(xmlStreamCompPtr comp) {
1504 if (comp != NULL) {
1505 if (comp->steps != NULL)
1506 xmlFree(comp->steps);
1507 if (comp->dict != NULL)
1508 xmlDictFree(comp->dict);
1509 xmlFree(comp);
1510 }
1511}
1512
1513/**
1514 * xmlStreamCompAddStep:
1515 * @comp: the compiled pattern for streaming
1516 * @name: the first string, the name, or NULL for *
1517 * @ns: the second step, the namespace name
1518 * @flags: the flags for that step
1519 *
1520 * Add a new step to the compiled pattern
1521 *
1522 * Returns -1 in case of error or the step index if successful
1523 */
1524static int
1525xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001526 const xmlChar *ns, int nodeType, int flags) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001527 xmlStreamStepPtr cur;
1528
1529 if (comp->nbStep >= comp->maxStep) {
1530 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1531 comp->maxStep * 2 * sizeof(xmlStreamStep));
1532 if (cur == NULL) {
1533 ERROR(NULL, NULL, NULL,
1534 "xmlNewStreamComp: malloc failed\n");
1535 return(-1);
1536 }
1537 comp->steps = cur;
1538 comp->maxStep *= 2;
1539 }
1540 cur = &comp->steps[comp->nbStep++];
1541 cur->flags = flags;
1542 cur->name = name;
1543 cur->ns = ns;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001544 cur->nodeType = nodeType;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001545 return(comp->nbStep - 1);
1546}
1547
1548/**
1549 * xmlStreamCompile:
1550 * @comp: the precompiled pattern
1551 *
1552 * Tries to stream compile a pattern
1553 *
1554 * Returns -1 in case of failure and 0 in case of success.
1555 */
1556static int
1557xmlStreamCompile(xmlPatternPtr comp) {
1558 xmlStreamCompPtr stream;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001559 int i, s = 0, root = 0, flags = 0, prevs = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001560 xmlStepOp step;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001561
1562 if ((comp == NULL) || (comp->steps == NULL))
1563 return(-1);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001564 /*
1565 * special case for .
1566 */
1567 if ((comp->nbStep == 1) &&
1568 (comp->steps[0].op == XML_OP_ELEM) &&
1569 (comp->steps[0].value == NULL) &&
1570 (comp->steps[0].value2 == NULL)) {
1571 stream = xmlNewStreamComp(0);
1572 if (stream == NULL)
1573 return(-1);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001574 /* Note that the stream will have no steps in this case. */
1575 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001576 comp->stream = stream;
1577 return(0);
1578 }
1579
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001580 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1581 if (stream == NULL)
1582 return(-1);
1583 if (comp->dict != NULL) {
1584 stream->dict = comp->dict;
1585 xmlDictReference(stream->dict);
1586 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001587
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001588 i = 0;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001589 if (comp->flags & PAT_FROM_ROOT)
1590 stream->flags |= XML_STREAM_FROM_ROOT;
1591
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001592 for (;i < comp->nbStep;i++) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001593 step = comp->steps[i];
1594 switch (step.op) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001595 case XML_OP_END:
1596 break;
1597 case XML_OP_ROOT:
1598 if (i != 0)
1599 goto error;
1600 root = 1;
1601 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001602 case XML_OP_NS:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001603 s = xmlStreamCompAddStep(stream, NULL, step.value,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001604 XML_ELEMENT_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001605 if (s < 0)
1606 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001607 prevs = s;
1608 flags = 0;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001609 break;
1610 case XML_OP_ATTR:
1611 flags |= XML_STREAM_STEP_ATTR;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001612 prevs = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001613 s = xmlStreamCompAddStep(stream,
1614 step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001615 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001616 if (s < 0)
1617 goto error;
1618 break;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001619 case XML_OP_ELEM:
1620 if ((step.value == NULL) && (step.value2 == NULL)) {
1621 /*
1622 * We have a "." or "self::node()" here.
1623 * Eliminate redundant self::node() tests like in "/./."
1624 * or "//./"
1625 * The only case we won't eliminate is "//.", i.e. if
1626 * self::node() is the last node test and we had
1627 * continuation somewhere beforehand.
1628 */
1629 if ((comp->nbStep == i + 1) &&
1630 (flags & XML_STREAM_STEP_DESC)) {
1631 /*
1632 * Mark the special case where the expression resolves
1633 * to any type of node.
1634 */
1635 if (comp->nbStep == i + 1) {
1636 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1637 }
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001638 flags |= XML_STREAM_STEP_NODE;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001639 s = xmlStreamCompAddStep(stream, NULL, NULL,
1640 XML_STREAM_ANY_NODE, flags);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001641 if (s < 0)
1642 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001643 flags = 0;
1644 /*
1645 * If there was a previous step, mark it to be added to
1646 * the result node-set; this is needed since only
1647 * the last step will be marked as "final" and only
1648 * "final" nodes are added to the resulting set.
1649 */
1650 if (prevs != -1) {
1651 stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1652 prevs = -1;
1653 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001654 break;
1655
1656 } else {
1657 /* Just skip this one. */
1658 continue;
1659 }
1660 }
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001661 /* An element node. */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001662 s = xmlStreamCompAddStep(stream, step.value, step.value2,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001663 XML_ELEMENT_NODE, flags);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001664 if (s < 0)
1665 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001666 prevs = s;
1667 flags = 0;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001668 break;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001669 case XML_OP_CHILD:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001670 /* An element node child. */
1671 s = xmlStreamCompAddStep(stream, step.value, step.value2,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001672 XML_ELEMENT_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001673 if (s < 0)
1674 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001675 prevs = s;
1676 flags = 0;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001677 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001678 case XML_OP_ALL:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001679 s = xmlStreamCompAddStep(stream, NULL, NULL,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001680 XML_ELEMENT_NODE, flags);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001681 if (s < 0)
1682 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001683 prevs = s;
1684 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001685 break;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001686 case XML_OP_PARENT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001687 break;
1688 case XML_OP_ANCESTOR:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001689 /* Skip redundant continuations. */
1690 if (flags & XML_STREAM_STEP_DESC)
1691 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001692 flags |= XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001693 /*
1694 * Mark the expression as having "//".
1695 */
1696 if ((stream->flags & XML_STREAM_DESC) == 0)
1697 stream->flags |= XML_STREAM_DESC;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001698 break;
1699 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001700 }
1701 if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1702 /*
1703 * If this should behave like a real pattern, we will mark
1704 * the first step as having "//", to be reentrant on every
1705 * tree level.
1706 */
1707 if ((stream->flags & XML_STREAM_DESC) == 0)
1708 stream->flags |= XML_STREAM_DESC;
1709
1710 if (stream->nbStep > 0) {
1711 if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0)
1712 stream->steps[0].flags |= XML_STREAM_STEP_DESC;
1713 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001714 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001715 if (stream->nbStep <= s)
1716 goto error;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001717 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1718 if (root)
1719 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1720#ifdef DEBUG_STREAMING
1721 xmlDebugStreamComp(stream);
1722#endif
1723 comp->stream = stream;
1724 return(0);
1725error:
1726 xmlFreeStreamComp(stream);
1727 return(0);
1728}
1729
1730/**
1731 * xmlNewStreamCtxt:
1732 * @size: the number of expected states
1733 *
1734 * build a new stream context
1735 *
1736 * Returns the new structure or NULL in case of error.
1737 */
1738static xmlStreamCtxtPtr
1739xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1740 xmlStreamCtxtPtr cur;
1741
1742 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1743 if (cur == NULL) {
1744 ERROR(NULL, NULL, NULL,
1745 "xmlNewStreamCtxt: malloc failed\n");
1746 return(NULL);
1747 }
1748 memset(cur, 0, sizeof(xmlStreamCtxt));
1749 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1750 if (cur->states == NULL) {
1751 xmlFree(cur);
1752 ERROR(NULL, NULL, NULL,
1753 "xmlNewStreamCtxt: malloc failed\n");
1754 return(NULL);
1755 }
1756 cur->nbState = 0;
1757 cur->maxState = 4;
1758 cur->level = 0;
1759 cur->comp = stream;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001760 cur->blockLevel = -1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001761 return(cur);
1762}
1763
1764/**
1765 * xmlFreeStreamCtxt:
1766 * @stream: the stream context
1767 *
1768 * Free the stream context
1769 */
1770void
1771xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001772 xmlStreamCtxtPtr next;
1773
1774 while (stream != NULL) {
1775 next = stream->next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001776 if (stream->states != NULL)
1777 xmlFree(stream->states);
1778 xmlFree(stream);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001779 stream = next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001780 }
1781}
1782
1783/**
1784 * xmlStreamCtxtAddState:
1785 * @comp: the stream context
1786 * @idx: the step index for that streaming state
1787 *
1788 * Add a new state to the stream context
1789 *
1790 * Returns -1 in case of error or the state index if successful
1791 */
1792static int
1793xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1794 int i;
1795 for (i = 0;i < comp->nbState;i++) {
1796 if (comp->states[2 * i] < 0) {
1797 comp->states[2 * i] = idx;
1798 comp->states[2 * i + 1] = level;
1799 return(i);
1800 }
1801 }
1802 if (comp->nbState >= comp->maxState) {
1803 int *cur;
1804
1805 cur = (int *) xmlRealloc(comp->states,
1806 comp->maxState * 4 * sizeof(int));
1807 if (cur == NULL) {
1808 ERROR(NULL, NULL, NULL,
1809 "xmlNewStreamCtxt: malloc failed\n");
1810 return(-1);
1811 }
1812 comp->states = cur;
1813 comp->maxState *= 2;
1814 }
1815 comp->states[2 * comp->nbState] = idx;
1816 comp->states[2 * comp->nbState++ + 1] = level;
1817 return(comp->nbState - 1);
1818}
1819
1820/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001821 * xmlStreamPushInternal:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001822 * @stream: the stream context
1823 * @name: the current name
1824 * @ns: the namespace name
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001825 * @nodeType: the type of the node
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001826 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001827 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1828 * indicated a dictionary, then strings for name and ns will be expected
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001829 * to come from the dictionary.
1830 * Both @name and @ns being NULL means the / i.e. the root of the document.
1831 * This can also act as a reset.
1832 *
1833 * Returns: -1 in case of error, 1 if the current state in the stream is a
1834 * match and 0 otherwise.
1835 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001836static int
1837xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1838 const xmlChar *name, const xmlChar *ns,
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001839 int nodeType) {
William M. Brack537f1172005-06-14 22:02:59 +00001840 int ret = 0, err = 0, final = 0, tmp, i, m, match, step, desc;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001841 xmlStreamCompPtr comp;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001842#ifdef DEBUG_STREAMING
1843 xmlStreamCtxtPtr orig = stream;
1844#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001845
1846 if ((stream == NULL) || (stream->nbState < 0))
1847 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001848
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001849 while (stream != NULL) {
1850 comp = stream->comp;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001851
1852 if ((nodeType == XML_ELEMENT_NODE) &&
1853 (name == NULL) && (ns == NULL)) {
1854 /* We have a document node here (or a reset). */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001855 stream->nbState = 0;
1856 stream->level = 0;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001857 stream->blockLevel = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001858 if (comp->flags & XML_STREAM_FROM_ROOT) {
1859 if (comp->nbStep == 0) {
1860 /* TODO: We have a "/." here? */
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001861 ret = 1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001862 } else {
1863 if ((comp->nbStep == 1) &&
1864 (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
1865 (comp->steps[0].flags & XML_STREAM_STEP_DESC))
1866 {
1867 /*
1868 * In the case of "//." the document node will match
1869 * as well.
1870 */
1871 ret = 1;
1872 } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1873 /* TODO: Do we need this ? */
1874 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1875 if (tmp < 0)
1876 err++;
1877 }
1878 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001879 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001880 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001881 continue; /* while */
1882 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001883
1884 /*
1885 * Fast check for ".".
1886 */
1887 if (comp->nbStep == 0) {
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001888 /*
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00001889 * / and . are handled at the XPath node set creation
1890 * level by checking min depth
1891 */
1892 if (stream->flags & XML_PATTERN_XPATH) {
1893 stream = stream->next;
1894 continue; /* while */
1895 }
1896 /*
William M. Brackea152c02005-06-09 18:12:28 +00001897 * For non-pattern like evaluation like XML Schema IDCs
1898 * or traditional XPath expressions, this will match if
1899 * we are at the first level only, otherwise on every level.
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001900 */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001901 if ((nodeType != XML_ATTRIBUTE_NODE) &&
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001902 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1903 (stream->level == 0))) {
1904 ret = 1;
1905 }
1906 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001907 goto stream_next;
1908 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001909 if (stream->blockLevel != -1) {
1910 /*
1911 * Skip blocked expressions.
1912 */
1913 stream->level++;
1914 goto stream_next;
William M. Brackea152c02005-06-09 18:12:28 +00001915 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001916
1917 if ((nodeType != XML_ELEMENT_NODE) &&
1918 (nodeType != XML_ATTRIBUTE_NODE) &&
1919 ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) {
1920 /*
1921 * No need to process nodes of other types if we don't
1922 * resolve to those types.
1923 * TODO: Do we need to block the context here?
1924 */
1925 stream->level++;
1926 goto stream_next;
1927 }
1928
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001929 /*
1930 * Check evolution of existing states
1931 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001932 i = 0;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001933 m = stream->nbState;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001934 while (i < m) {
1935 if ((comp->flags & XML_STREAM_DESC) == 0) {
1936 /*
1937 * If there is no "//", then only the last
1938 * added state is of interest.
1939 */
1940 step = stream->states[2 * (stream->nbState -1)];
1941 /*
1942 * TODO: Security check, should not happen, remove it.
1943 */
1944 if (stream->states[(2 * (stream->nbState -1)) + 1] <
1945 stream->level) {
1946 return (-1);
1947 }
1948 desc = 0;
1949 /* loop-stopper */
1950 i = m;
1951 } else {
1952 /*
1953 * If there are "//", then we need to process every "//"
1954 * occuring in the states, plus any other state for this
1955 * level.
1956 */
1957 step = stream->states[2 * i];
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001958
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001959 /* TODO: should not happen anymore: dead states */
1960 if (step < 0)
1961 goto next_state;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001962
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001963 tmp = stream->states[(2 * i) + 1];
1964
1965 /* skip new states just added */
1966 if (tmp > stream->level)
1967 goto next_state;
1968
1969 /* skip states at ancestor levels, except if "//" */
1970 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1971 if ((tmp < stream->level) && (!desc))
1972 goto next_state;
1973 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001974 /*
1975 * Check for correct node-type.
1976 */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001977 if (comp->steps[step].nodeType != nodeType) {
1978 if (comp->steps[step].nodeType == XML_ATTRIBUTE_NODE) {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001979 /*
1980 * Block this expression for deeper evaluation.
1981 */
1982 if ((comp->flags & XML_STREAM_DESC) == 0)
1983 stream->blockLevel = stream->level +1;
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00001984 goto next_state;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001985 } else if (comp->steps[step].nodeType != XML_STREAM_ANY_NODE)
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00001986 goto next_state;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001987 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001988 /*
1989 * Compare local/namespace-name.
1990 */
1991 match = 0;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001992 if (comp->steps[step].nodeType == XML_STREAM_ANY_NODE) {
1993 match = 1;
1994 } else if (comp->dict) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001995 if (comp->steps[step].name == NULL) {
1996 if (comp->steps[step].ns == NULL)
1997 match = 1;
1998 else
1999 match = (comp->steps[step].ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002000 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002001 match = ((comp->steps[step].name == name) &&
2002 (comp->steps[step].ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002003 }
2004 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002005 if (comp->steps[step].name == NULL) {
2006 if (comp->steps[step].ns == NULL)
2007 match = 1;
2008 else
2009 match = xmlStrEqual(comp->steps[step].ns, ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002010 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002011 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
2012 (xmlStrEqual(comp->steps[step].ns, ns)));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002013 }
2014 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002015 if (match) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002016 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
2017 if (desc) {
2018 if (final) {
2019 ret = 1;
2020 } else {
2021 /* descending match create a new state */
2022 xmlStreamCtxtAddState(stream, step + 1,
2023 stream->level + 1);
2024 }
2025 } else {
2026 if (final) {
2027 ret = 1;
2028 } else {
2029 xmlStreamCtxtAddState(stream, step + 1,
2030 stream->level + 1);
2031 }
2032 }
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00002033 if ((ret != 1) &&
2034 (comp->steps[step].flags & XML_STREAM_STEP_IN_SET)) {
2035 /*
2036 * Check if we have a special case like "foo/bar//.", where
2037 * "foo" is selected as well.
2038 */
2039 ret = 1;
2040 }
2041 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002042 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2043 ((! match) || final)) {
2044 /*
2045 * Mark this expression as blocked for any evaluation at
2046 * deeper levels. Note that this includes "/foo"
2047 * expressions if the *pattern* behaviour is used.
2048 */
2049 stream->blockLevel = stream->level +1;
2050 }
2051next_state:
2052 i++;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002053 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002054
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002055 stream->level++;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002056
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002057 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002058 * Re/enter the expression.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002059 * Don't reenter if it's an absolute expression like "/foo",
2060 * except "//foo".
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002061 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002062 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT)
2063 goto stream_next;
2064
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002065 desc = comp->steps[0].flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002066 if (stream->flags & XML_PATTERN_NOTPATTERN) {
2067 /*
2068 * Re/enter the expression if it is a "descendant" one,
2069 * or if we are at the 1st level of evaluation.
2070 */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002071
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002072 if (stream->level == 1) {
2073 if (XML_STREAM_XS_IDC(stream)) {
William M. Brackea152c02005-06-09 18:12:28 +00002074 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002075 * XS-IDC: The missing "self::node()" will always
2076 * match the first given node.
2077 */
William M. Brackea152c02005-06-09 18:12:28 +00002078 goto stream_next;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002079 } else
2080 goto compare;
2081 }
2082 /*
2083 * A "//" is always reentrant.
2084 */
2085 if (desc)
2086 goto compare;
2087
2088 /*
2089 * XS-IDC: Process the 2nd level, since the missing
2090 * "self::node()" is responsible for the 2nd level being
2091 * the real start level.
2092 */
2093 if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
2094 goto compare;
2095
2096 goto stream_next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002097 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002098
2099compare:
2100 /*
2101 * Check expected node-type.
2102 */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002103 if (comp->steps[0].nodeType != nodeType) {
2104 if (nodeType == XML_ATTRIBUTE_NODE)
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002105 goto stream_next;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002106 else if (comp->steps[0].nodeType != XML_STREAM_ANY_NODE)
2107 goto stream_next;
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002108 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002109 /*
2110 * Compare local/namespace-name.
2111 */
2112 match = 0;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002113 if (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) {
2114 match = 1;
2115 } else if (comp->steps[0].name == NULL) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002116 if (comp->steps[0].ns == NULL)
2117 match = 1;
2118 else {
2119 if (comp->dict)
2120 match = (comp->steps[0].ns == ns);
2121 else
2122 match = xmlStrEqual(comp->steps[0].ns, ns);
2123 }
2124 } else {
2125 if (comp->dict)
2126 match = ((comp->steps[0].name == name) &&
2127 (comp->steps[0].ns == ns));
2128 else
2129 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
2130 (xmlStrEqual(comp->steps[0].ns, ns)));
2131 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002132 final = comp->steps[0].flags & XML_STREAM_STEP_FINAL;
2133 if (match) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002134 if (final)
2135 ret = 1;
2136 else
2137 xmlStreamCtxtAddState(stream, 1, stream->level);
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00002138 if ((ret != 1) &&
2139 (comp->steps[0].flags & XML_STREAM_STEP_IN_SET)) {
2140 /*
2141 * Check if we have a special case like "foo//.", where
2142 * "foo" is selected as well.
2143 */
2144 ret = 1;
2145 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002146 }
2147 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2148 ((! match) || final)) {
2149 /*
2150 * Mark this expression as blocked for any evaluation at
2151 * deeper levels.
2152 */
2153 stream->blockLevel = stream->level;
2154 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002155
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002156stream_next:
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002157 stream = stream->next;
2158 } /* while stream != NULL */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002159
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002160 if (err > 0)
2161 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002162#ifdef DEBUG_STREAMING
2163 xmlDebugStreamCtxt(orig, ret);
2164#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002165 return(ret);
2166}
2167
2168/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002169 * xmlStreamPush:
2170 * @stream: the stream context
2171 * @name: the current name
2172 * @ns: the namespace name
2173 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002174 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2175 * indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002176 * to come from the dictionary.
2177 * Both @name and @ns being NULL means the / i.e. the root of the document.
2178 * This can also act as a reset.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002179 * Otherwise the function will act as if it has been given an element-node.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002180 *
2181 * Returns: -1 in case of error, 1 if the current state in the stream is a
2182 * match and 0 otherwise.
2183 */
2184int
2185xmlStreamPush(xmlStreamCtxtPtr stream,
2186 const xmlChar *name, const xmlChar *ns) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002187 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE));
2188}
2189
2190/**
Daniel Veillard67952602006-01-05 15:29:44 +00002191 * xmlStreamPushNode:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002192 * @stream: the stream context
2193 * @name: the current name
2194 * @ns: the namespace name
2195 * @nodeType: the type of the node being pushed
2196 *
2197 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2198 * indicated a dictionary, then strings for name and ns will be expected
2199 * to come from the dictionary.
2200 * Both @name and @ns being NULL means the / i.e. the root of the document.
2201 * This can also act as a reset.
2202 * Different from xmlStreamPush() this function can be fed with nodes of type:
2203 * element-, attribute-, text-, cdata-section-, comment- and
2204 * processing-instruction-node.
2205 *
2206 * Returns: -1 in case of error, 1 if the current state in the stream is a
2207 * match and 0 otherwise.
2208 */
2209int
2210xmlStreamPushNode(xmlStreamCtxtPtr stream,
2211 const xmlChar *name, const xmlChar *ns,
2212 int nodeType)
2213{
2214 return (xmlStreamPushInternal(stream, name, ns,
2215 nodeType));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002216}
2217
2218/**
2219* xmlStreamPushAttr:
2220* @stream: the stream context
2221* @name: the current name
2222* @ns: the namespace name
2223*
William M. Brackfbb619f2005-06-06 13:49:18 +00002224* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2225* indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002226* to come from the dictionary.
2227* Both @name and @ns being NULL means the / i.e. the root of the document.
2228* This can also act as a reset.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002229* Otherwise the function will act as if it has been given an attribute-node.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002230*
2231* Returns: -1 in case of error, 1 if the current state in the stream is a
2232* match and 0 otherwise.
2233*/
2234int
2235xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2236 const xmlChar *name, const xmlChar *ns) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002237 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002238}
2239
2240/**
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002241 * xmlStreamPop:
2242 * @stream: the stream context
2243 *
2244 * push one level from the stream.
2245 *
2246 * Returns: -1 in case of error, 0 otherwise.
2247 */
2248int
2249xmlStreamPop(xmlStreamCtxtPtr stream) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002250 int i, lev;
Kasimier T. Buchcik65c2f1d2005-10-17 12:39:58 +00002251
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002252 if (stream == NULL)
2253 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002254 while (stream != NULL) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002255 /*
2256 * Reset block-level.
2257 */
2258 if (stream->blockLevel == stream->level)
2259 stream->blockLevel = -1;
2260
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002261 stream->level--;
2262 if (stream->level < 0)
Kasimier T. Buchcik65c2f1d2005-10-17 12:39:58 +00002263 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002264 /*
2265 * Check evolution of existing states
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002266 */
2267 for (i = stream->nbState -1; i >= 0; i--) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002268 /* discard obsoleted states */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002269 lev = stream->states[(2 * i) + 1];
2270 if (lev > stream->level)
2271 stream->nbState--;
2272 if (lev <= stream->level)
2273 break;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002274 }
2275 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00002276 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002277 return(0);
2278}
2279
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002280/**
2281 * xmlStreamWantsAnyNode:
Daniel Veillard67952602006-01-05 15:29:44 +00002282 * @streamCtxt: the stream context
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002283 *
2284 * Query if the streaming pattern additionally needs to be fed with
2285 * text-, cdata-section-, comment- and processing-instruction-nodes.
2286 * If the result is 0 then only element-nodes and attribute-nodes
2287 * need to be pushed.
2288 *
2289 * Returns: 1 in case of need of nodes of the above described types,
2290 * 0 otherwise. -1 on API errors.
2291 */
2292int
2293xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
2294{
2295 if (streamCtxt == NULL)
2296 return(-1);
2297 while (streamCtxt != NULL) {
2298 if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
2299 return(1);
2300 streamCtxt = streamCtxt->next;
2301 }
2302 return(0);
2303}
2304
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002305/************************************************************************
2306 * *
2307 * The public interfaces *
2308 * *
2309 ************************************************************************/
2310
2311/**
2312 * xmlPatterncompile:
2313 * @pattern: the pattern to compile
William M. Brackfbb619f2005-06-06 13:49:18 +00002314 * @dict: an optional dictionary for interned strings
Daniel Veillarded6c5492005-07-23 15:00:22 +00002315 * @flags: compilation flags, see xmlPatternFlags
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002316 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002317 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002318 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002319 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002320 * Returns the compiled form of the pattern or NULL in case of error
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002321 */
2322xmlPatternPtr
Daniel Veillarded6c5492005-07-23 15:00:22 +00002323xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002324 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002325 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002326 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002327 const xmlChar *or, *start;
2328 xmlChar *tmp = NULL;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002329 int type = 0;
2330 int streamable = 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002331
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002332 if (pattern == NULL)
2333 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002334
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002335 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002336 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002337 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002338 tmp = NULL;
2339 while ((*or != 0) && (*or != '|')) or++;
2340 if (*or == 0)
2341 ctxt = xmlNewPatParserContext(start, dict, namespaces);
2342 else {
2343 tmp = xmlStrndup(start, or - start);
2344 if (tmp != NULL) {
2345 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2346 }
2347 or++;
2348 }
2349 if (ctxt == NULL) goto error;
2350 cur = xmlNewPattern();
2351 if (cur == NULL) goto error;
2352 if (ret == NULL)
2353 ret = cur;
2354 else {
2355 cur->next = ret->next;
2356 ret->next = cur;
2357 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002358 cur->flags = flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002359 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002360
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002361 if (XML_STREAM_XS_IDC(cur))
2362 xmlCompileIDCXPathPath(ctxt);
2363 else
2364 xmlCompilePathPattern(ctxt);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002365 if (ctxt->error != 0)
2366 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002367 xmlFreePatParserContext(ctxt);
William M. Brackfbb619f2005-06-06 13:49:18 +00002368 ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002369
2370
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002371 if (streamable) {
2372 if (type == 0) {
2373 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2374 } else if (type == PAT_FROM_ROOT) {
2375 if (cur->flags & PAT_FROM_CUR)
2376 streamable = 0;
2377 } else if (type == PAT_FROM_CUR) {
2378 if (cur->flags & PAT_FROM_ROOT)
2379 streamable = 0;
2380 }
2381 }
2382 if (streamable)
2383 xmlStreamCompile(cur);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002384 if (xmlReversePattern(cur) < 0)
2385 goto error;
William M. Brackea152c02005-06-09 18:12:28 +00002386 if (tmp != NULL) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002387 xmlFree(tmp);
William M. Brackea152c02005-06-09 18:12:28 +00002388 tmp = NULL;
2389 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002390 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002391 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002392 if (streamable == 0) {
2393 cur = ret;
2394 while (cur != NULL) {
2395 if (cur->stream != NULL) {
2396 xmlFreeStreamComp(cur->stream);
2397 cur->stream = NULL;
2398 }
2399 cur = cur->next;
2400 }
2401 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002402
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002403 return(ret);
2404error:
2405 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2406 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002407 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002408 return(NULL);
2409}
2410
2411/**
2412 * xmlPatternMatch:
2413 * @comp: the precompiled pattern
2414 * @node: a node
2415 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002416 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002417 *
2418 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2419 */
2420int
2421xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2422{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002423 int ret = 0;
2424
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002425 if ((comp == NULL) || (node == NULL))
2426 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002427
2428 while (comp != NULL) {
2429 ret = xmlPatMatch(comp, node);
2430 if (ret != 0)
2431 return(ret);
2432 comp = comp->next;
2433 }
2434 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002435}
2436
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002437/**
2438 * xmlPatternGetStreamCtxt:
2439 * @comp: the precompiled pattern
2440 *
2441 * Get a streaming context for that pattern
2442 * Use xmlFreeStreamCtxt to free the context.
2443 *
2444 * Returns a pointer to the context or NULL in case of failure
2445 */
2446xmlStreamCtxtPtr
2447xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2448{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002449 xmlStreamCtxtPtr ret = NULL, cur;
2450
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002451 if ((comp == NULL) || (comp->stream == NULL))
2452 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002453
2454 while (comp != NULL) {
2455 if (comp->stream == NULL)
2456 goto failed;
2457 cur = xmlNewStreamCtxt(comp->stream);
2458 if (cur == NULL)
2459 goto failed;
2460 if (ret == NULL)
2461 ret = cur;
2462 else {
2463 cur->next = ret->next;
2464 ret->next = cur;
2465 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002466 cur->flags = comp->flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002467 comp = comp->next;
2468 }
2469 return(ret);
2470failed:
2471 xmlFreeStreamCtxt(ret);
2472 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002473}
2474
Daniel Veillard56de87e2005-02-16 00:22:29 +00002475/**
2476 * xmlPatternStreamable:
2477 * @comp: the precompiled pattern
2478 *
2479 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2480 * should work.
2481 *
2482 * Returns 1 if streamable, 0 if not and -1 in case of error.
2483 */
2484int
2485xmlPatternStreamable(xmlPatternPtr comp) {
2486 if (comp == NULL)
2487 return(-1);
2488 while (comp != NULL) {
2489 if (comp->stream == NULL)
2490 return(0);
2491 comp = comp->next;
2492 }
2493 return(1);
2494}
2495
2496/**
2497 * xmlPatternMaxDepth:
2498 * @comp: the precompiled pattern
2499 *
2500 * Check the maximum depth reachable by a pattern
2501 *
2502 * Returns -2 if no limit (using //), otherwise the depth,
2503 * and -1 in case of error
2504 */
2505int
2506xmlPatternMaxDepth(xmlPatternPtr comp) {
2507 int ret = 0, i;
2508 if (comp == NULL)
2509 return(-1);
2510 while (comp != NULL) {
2511 if (comp->stream == NULL)
2512 return(-1);
2513 for (i = 0;i < comp->stream->nbStep;i++)
2514 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2515 return(-2);
2516 if (comp->stream->nbStep > ret)
2517 ret = comp->stream->nbStep;
2518 comp = comp->next;
2519 }
2520 return(ret);
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002521}
Daniel Veillard56de87e2005-02-16 00:22:29 +00002522
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002523/**
2524 * xmlPatternMinDepth:
2525 * @comp: the precompiled pattern
2526 *
2527 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2528 * part of the set.
2529 *
2530 * Returns -1 in case of error otherwise the depth,
2531 *
2532 */
2533int
2534xmlPatternMinDepth(xmlPatternPtr comp) {
2535 int ret = 12345678;
2536 if (comp == NULL)
2537 return(-1);
2538 while (comp != NULL) {
2539 if (comp->stream == NULL)
2540 return(-1);
2541 if (comp->stream->nbStep < ret)
2542 ret = comp->stream->nbStep;
2543 if (ret == 0)
2544 return(0);
2545 comp = comp->next;
2546 }
2547 return(ret);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002548}
2549
2550/**
2551 * xmlPatternFromRoot:
2552 * @comp: the precompiled pattern
2553 *
2554 * Check if the pattern must be looked at from the root.
2555 *
2556 * Returns 1 if true, 0 if false and -1 in case of error
2557 */
2558int
2559xmlPatternFromRoot(xmlPatternPtr comp) {
2560 if (comp == NULL)
2561 return(-1);
2562 while (comp != NULL) {
2563 if (comp->stream == NULL)
2564 return(-1);
2565 if (comp->flags & PAT_FROM_ROOT)
2566 return(1);
2567 comp = comp->next;
2568 }
2569 return(0);
2570
2571}
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002572
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002573#define bottom_pattern
2574#include "elfgcchack.h"
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002575#endif /* LIBXML_PATTERN_ENABLED */