blob: 75935f0f4a093bd9d04db8368f86852d204acf54 [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) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000627 if ((node->type == XML_ELEMENT_NODE) &&
628 (step->value[0] == node->name[0]) &&
629 (xmlStrEqual(step->value, node->name))) {
630 /* Namespace test */
631 if (node->ns == NULL) {
632 if (step->value2 == NULL)
633 break;
634 } else if (node->ns->href != NULL) {
635 if ((step->value2 != NULL) &&
636 (xmlStrEqual(step->value2, node->ns->href)))
637 break;
638 }
639 }
640 node = node->parent;
641 }
642 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000643 goto rollback;
644 /*
645 * prepare a potential rollback from here
646 * for ancestors of that node.
647 */
648 if (step->op == XML_OP_ANCESTOR)
649 xmlPatPushState(&states, i, node);
650 else
651 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000652 continue;
653 case XML_OP_NS:
654 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000655 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000656 if (node->ns == NULL) {
657 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000658 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000659 } else if (node->ns->href != NULL) {
660 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000661 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000662 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000663 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000664 }
665 break;
666 case XML_OP_ALL:
667 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000668 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000669 break;
670 }
671 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000672found:
673 if (states.states != NULL) {
674 /* Free the rollback states */
675 xmlFree(states.states);
676 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000677 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000678rollback:
679 /* got an error try to rollback */
680 if (states.states == NULL)
681 return(0);
682 if (states.nbstates <= 0) {
683 xmlFree(states.states);
684 return(0);
685 }
686 states.nbstates--;
687 i = states.states[states.nbstates].step;
688 node = states.states[states.nbstates].node;
689#if 0
690 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
691#endif
692 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000693}
694
695/************************************************************************
696 * *
697 * Dedicated parser for templates *
698 * *
699 ************************************************************************/
700
701#define TODO \
702 xmlGenericError(xmlGenericErrorContext, \
703 "Unimplemented block at %s:%d\n", \
704 __FILE__, __LINE__);
705#define CUR (*ctxt->cur)
706#define SKIP(val) ctxt->cur += (val)
707#define NXT(val) ctxt->cur[(val)]
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000708#define PEEKPREV(val) ctxt->cur[-(val)]
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000709#define CUR_PTR ctxt->cur
710
711#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000712 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000713
714#define CURRENT (*ctxt->cur)
715#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
716
717
718#define PUSH(op, val, val2) \
719 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
720
721#define XSLT_ERROR(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000722 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000723 ctxt->error = (X); return; }
724
725#define XSLT_ERROR0(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000726 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000727 ctxt->error = (X); return(0); }
728
729#if 0
730/**
731 * xmlPatScanLiteral:
732 * @ctxt: the XPath Parser context
733 *
734 * Parse an XPath Litteral:
735 *
736 * [29] Literal ::= '"' [^"]* '"'
737 * | "'" [^']* "'"
738 *
739 * Returns the Literal parsed or NULL
740 */
741
742static xmlChar *
743xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
744 const xmlChar *q, *cur;
745 xmlChar *ret = NULL;
746 int val, len;
747
748 SKIP_BLANKS;
749 if (CUR == '"') {
750 NEXT;
751 cur = q = CUR_PTR;
752 val = xmlStringCurrentChar(NULL, cur, &len);
753 while ((IS_CHAR(val)) && (val != '"')) {
754 cur += len;
755 val = xmlStringCurrentChar(NULL, cur, &len);
756 }
757 if (!IS_CHAR(val)) {
758 ctxt->error = 1;
759 return(NULL);
760 } else {
761 ret = xmlStrndup(q, cur - q);
762 }
763 cur += len;
764 CUR_PTR = cur;
765 } else if (CUR == '\'') {
766 NEXT;
767 cur = q = CUR_PTR;
768 val = xmlStringCurrentChar(NULL, cur, &len);
769 while ((IS_CHAR(val)) && (val != '\'')) {
770 cur += len;
771 val = xmlStringCurrentChar(NULL, cur, &len);
772 }
773 if (!IS_CHAR(val)) {
774 ctxt->error = 1;
775 return(NULL);
776 } else {
777 ret = xmlStrndup(q, cur - q);
778 }
779 cur += len;
780 CUR_PTR = cur;
781 } else {
782 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
783 ctxt->error = 1;
784 return(NULL);
785 }
786 return(ret);
787}
788#endif
789
790/**
791 * xmlPatScanName:
792 * @ctxt: the XPath Parser context
793 *
794 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
795 * CombiningChar | Extender
796 *
797 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
798 *
799 * [6] Names ::= Name (S Name)*
800 *
801 * Returns the Name parsed or NULL
802 */
803
804static xmlChar *
805xmlPatScanName(xmlPatParserContextPtr ctxt) {
806 const xmlChar *q, *cur;
807 xmlChar *ret = NULL;
808 int val, len;
809
810 SKIP_BLANKS;
811
812 cur = q = CUR_PTR;
813 val = xmlStringCurrentChar(NULL, cur, &len);
814 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
815 return(NULL);
816
817 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
818 (val == '.') || (val == '-') ||
819 (val == '_') ||
820 (IS_COMBINING(val)) ||
821 (IS_EXTENDER(val))) {
822 cur += len;
823 val = xmlStringCurrentChar(NULL, cur, &len);
824 }
825 ret = xmlStrndup(q, cur - q);
826 CUR_PTR = cur;
827 return(ret);
828}
829
830/**
831 * xmlPatScanNCName:
832 * @ctxt: the XPath Parser context
833 *
834 * Parses a non qualified name
835 *
836 * Returns the Name parsed or NULL
837 */
838
839static xmlChar *
840xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
841 const xmlChar *q, *cur;
842 xmlChar *ret = NULL;
843 int val, len;
844
845 SKIP_BLANKS;
846
847 cur = q = CUR_PTR;
848 val = xmlStringCurrentChar(NULL, cur, &len);
849 if (!IS_LETTER(val) && (val != '_'))
850 return(NULL);
851
852 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
853 (val == '.') || (val == '-') ||
854 (val == '_') ||
855 (IS_COMBINING(val)) ||
856 (IS_EXTENDER(val))) {
857 cur += len;
858 val = xmlStringCurrentChar(NULL, cur, &len);
859 }
860 ret = xmlStrndup(q, cur - q);
861 CUR_PTR = cur;
862 return(ret);
863}
864
865#if 0
866/**
867 * xmlPatScanQName:
868 * @ctxt: the XPath Parser context
869 * @prefix: the place to store the prefix
870 *
871 * Parse a qualified name
872 *
873 * Returns the Name parsed or NULL
874 */
875
876static xmlChar *
877xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
878 xmlChar *ret = NULL;
879
880 *prefix = NULL;
881 ret = xmlPatScanNCName(ctxt);
882 if (CUR == ':') {
883 *prefix = ret;
884 NEXT;
885 ret = xmlPatScanNCName(ctxt);
886 }
887 return(ret);
888}
889#endif
890
891/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000892 * xmlCompileAttributeTest:
893 * @ctxt: the compilation context
894 *
895 * Compile an attribute test.
896 */
897static void
898xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
899 xmlChar *token = NULL;
900 xmlChar *name = NULL;
901 xmlChar *URL = NULL;
902
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000903 SKIP_BLANKS;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000904 name = xmlPatScanNCName(ctxt);
905 if (name == NULL) {
906 if (CUR == '*') {
907 PUSH(XML_OP_ATTR, NULL, NULL);
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +0000908 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000909 } else {
910 ERROR(NULL, NULL, NULL,
911 "xmlCompileAttributeTest : Name expected\n");
912 ctxt->error = 1;
913 }
914 return;
915 }
916 if (CUR == ':') {
917 int i;
918 xmlChar *prefix = name;
919
920 NEXT;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000921
922 if (IS_BLANK_CH(CUR)) {
923 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
924 xmlFree(prefix);
925 ctxt->error = 1;
926 goto error;
927 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000928 /*
929 * This is a namespace match
930 */
931 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000932 if ((prefix[0] == 'x') &&
933 (prefix[1] == 'm') &&
934 (prefix[2] == 'l') &&
935 (prefix[3] == 0)) {
936 URL = xmlStrdup(XML_XML_NAMESPACE);
937 } else {
938 for (i = 0;i < ctxt->nb_namespaces;i++) {
939 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
940 URL = xmlStrdup(ctxt->namespaces[2 * i]);
941 break;
942 }
943 }
944 if (i >= ctxt->nb_namespaces) {
945 ERROR5(NULL, NULL, NULL,
946 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
947 prefix);
948 ctxt->error = 1;
949 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000950 }
951 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000952 xmlFree(prefix);
953 if (token == NULL) {
954 if (CUR == '*') {
955 NEXT;
956 PUSH(XML_OP_ATTR, NULL, URL);
957 } else {
958 ERROR(NULL, NULL, NULL,
959 "xmlCompileAttributeTest : Name expected\n");
960 ctxt->error = 1;
961 goto error;
962 }
963 } else {
964 PUSH(XML_OP_ATTR, token, URL);
965 }
966 } else {
967 PUSH(XML_OP_ATTR, name, NULL);
968 }
969 return;
970error:
971 if (URL != NULL)
972 xmlFree(URL);
973 if (token != NULL)
974 xmlFree(token);
975}
976
977
978/**
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000979 * xmlCompileStepPattern:
980 * @ctxt: the compilation context
981 *
982 * Compile the Step Pattern and generates a precompiled
983 * form suitable for fast matching.
984 *
985 * [3] Step ::= '.' | NameTest
986 * [4] NameTest ::= QName | '*' | NCName ':' '*'
987 */
988
989static void
990xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
991 xmlChar *token = NULL;
992 xmlChar *name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000993 xmlChar *URL = NULL;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000994 int hasBlanks = 0;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000995
996 SKIP_BLANKS;
997 if (CUR == '.') {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000998 /*
999 * Context node.
1000 */
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001001 NEXT;
1002 PUSH(XML_OP_ELEM, NULL, NULL);
1003 return;
1004 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001005 if (CUR == '@') {
1006 /*
1007 * Attribute test.
1008 */
1009 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1010 ERROR5(NULL, NULL, NULL,
1011 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1012 ctxt->error = 1;
1013 return;
1014 }
1015 NEXT;
1016 xmlCompileAttributeTest(ctxt);
1017 if (ctxt->error != 0)
1018 goto error;
1019 return;
1020 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001021 name = xmlPatScanNCName(ctxt);
1022 if (name == NULL) {
1023 if (CUR == '*') {
1024 NEXT;
1025 PUSH(XML_OP_ALL, NULL, NULL);
1026 return;
1027 } else {
1028 ERROR(NULL, NULL, NULL,
1029 "xmlCompileStepPattern : Name expected\n");
1030 ctxt->error = 1;
1031 return;
1032 }
1033 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001034 if (IS_BLANK_CH(CUR)) {
1035 hasBlanks = 1;
1036 SKIP_BLANKS;
1037 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001038 if (CUR == ':') {
1039 NEXT;
1040 if (CUR != ':') {
1041 xmlChar *prefix = name;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001042 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001043
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001044 if (hasBlanks || IS_BLANK_CH(CUR)) {
1045 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1046 ctxt->error = 1;
1047 goto error;
1048 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001049 /*
1050 * This is a namespace match
1051 */
1052 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001053 if ((prefix[0] == 'x') &&
1054 (prefix[1] == 'm') &&
1055 (prefix[2] == 'l') &&
1056 (prefix[3] == 0)) {
1057 URL = xmlStrdup(XML_XML_NAMESPACE);
1058 } else {
1059 for (i = 0;i < ctxt->nb_namespaces;i++) {
1060 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1061 URL = xmlStrdup(ctxt->namespaces[2 * i]);
1062 break;
1063 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +00001064 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001065 if (i >= ctxt->nb_namespaces) {
1066 ERROR5(NULL, NULL, NULL,
1067 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1068 prefix);
1069 ctxt->error = 1;
1070 goto error;
1071 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001072 }
1073 xmlFree(prefix);
1074 if (token == NULL) {
1075 if (CUR == '*') {
1076 NEXT;
1077 PUSH(XML_OP_NS, URL, NULL);
1078 } else {
1079 ERROR(NULL, NULL, NULL,
1080 "xmlCompileStepPattern : Name expected\n");
1081 ctxt->error = 1;
1082 goto error;
1083 }
1084 } else {
1085 PUSH(XML_OP_ELEM, token, URL);
1086 }
1087 } else {
1088 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001089 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1090 xmlFree(name);
1091 name = xmlPatScanName(ctxt);
1092 if (name == NULL) {
1093 if (CUR == '*') {
1094 NEXT;
1095 PUSH(XML_OP_ALL, NULL, NULL);
1096 return;
1097 } else {
1098 ERROR(NULL, NULL, NULL,
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001099 "xmlCompileStepPattern : QName expected\n");
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001100 ctxt->error = 1;
1101 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001102 }
1103 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001104 if (CUR == ':') {
1105 xmlChar *prefix = name;
1106 int i;
1107
1108 NEXT;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001109 if (IS_BLANK_CH(CUR)) {
1110 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1111 ctxt->error = 1;
1112 goto error;
1113 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001114 /*
1115 * This is a namespace match
1116 */
1117 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001118 if ((prefix[0] == 'x') &&
1119 (prefix[1] == 'm') &&
1120 (prefix[2] == 'l') &&
1121 (prefix[3] == 0)) {
1122 URL = xmlStrdup(XML_XML_NAMESPACE);
1123 } else {
1124 for (i = 0;i < ctxt->nb_namespaces;i++) {
1125 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1126 URL = xmlStrdup(ctxt->namespaces[2 * i]);
1127 break;
1128 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001129 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001130 if (i >= ctxt->nb_namespaces) {
1131 ERROR5(NULL, NULL, NULL,
1132 "xmlCompileStepPattern : no namespace bound "
1133 "to prefix %s\n", prefix);
1134 ctxt->error = 1;
1135 goto error;
1136 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001137 }
1138 xmlFree(prefix);
1139 if (token == NULL) {
1140 if (CUR == '*') {
1141 NEXT;
1142 PUSH(XML_OP_NS, URL, NULL);
1143 } else {
1144 ERROR(NULL, NULL, NULL,
1145 "xmlCompileStepPattern : Name expected\n");
1146 ctxt->error = 1;
1147 goto error;
1148 }
1149 } else {
1150 PUSH(XML_OP_CHILD, token, URL);
1151 }
1152 } else
1153 PUSH(XML_OP_CHILD, name, NULL);
1154 return;
1155 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1156 xmlFree(name);
1157 name = NULL;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001158 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1159 ERROR5(NULL, NULL, NULL,
1160 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1161 ctxt->error = 1;
1162 goto error;
1163 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001164 xmlCompileAttributeTest(ctxt);
1165 if (ctxt->error != 0)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001166 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001167 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001168 } else {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001169 ERROR5(NULL, NULL, NULL,
1170 "The 'element' or 'attribute' axis is expected.\n", NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001171 ctxt->error = 1;
1172 goto error;
1173 }
Daniel Veillard0e460da2005-03-30 22:47:10 +00001174 /* NOT REACHED xmlFree(name); */
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001175 }
1176 } else if (CUR == '*') {
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001177 if (name != NULL) {
1178 ctxt->error = 1;
1179 goto error;
1180 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001181 NEXT;
1182 PUSH(XML_OP_ALL, token, NULL);
1183 } else {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001184 PUSH(XML_OP_ELEM, name, NULL);
1185 }
1186 return;
1187error:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001188 if (URL != NULL)
1189 xmlFree(URL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001190 if (token != NULL)
1191 xmlFree(token);
1192 if (name != NULL)
1193 xmlFree(name);
1194}
1195
1196/**
1197 * xmlCompilePathPattern:
1198 * @ctxt: the compilation context
1199 *
1200 * Compile the Path Pattern and generates a precompiled
1201 * form suitable for fast matching.
1202 *
1203 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1204 */
1205static void
1206xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1207 SKIP_BLANKS;
William M. Brack537f1172005-06-14 22:02:59 +00001208 if (CUR == '/') {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001209 ctxt->comp->flags |= PAT_FROM_ROOT;
William M. Brackea152c02005-06-09 18:12:28 +00001210 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001211 ctxt->comp->flags |= PAT_FROM_CUR;
1212 }
William M. Brackea152c02005-06-09 18:12:28 +00001213
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001214 if ((CUR == '/') && (NXT(1) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001215 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001216 NEXT;
1217 NEXT;
1218 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001219 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001220 NEXT;
1221 NEXT;
1222 NEXT;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001223 /* Check for incompleteness. */
1224 SKIP_BLANKS;
1225 if (CUR == 0) {
1226 ERROR5(NULL, NULL, NULL,
1227 "Incomplete expression '%s'.\n", ctxt->base);
1228 ctxt->error = 1;
1229 goto error;
1230 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001231 }
1232 if (CUR == '@') {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001233 NEXT;
1234 xmlCompileAttributeTest(ctxt);
1235 SKIP_BLANKS;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001236 /* TODO: check for incompleteness */
William M. Brackfbb619f2005-06-06 13:49:18 +00001237 if (CUR != 0) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001238 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001239 if (ctxt->error != 0)
1240 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001241 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001242 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001243 if (CUR == '/') {
1244 PUSH(XML_OP_ROOT, NULL, NULL);
1245 NEXT;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001246 /* Check for incompleteness. */
1247 SKIP_BLANKS;
1248 if (CUR == 0) {
1249 ERROR5(NULL, NULL, NULL,
1250 "Incomplete expression '%s'.\n", ctxt->base);
1251 ctxt->error = 1;
1252 goto error;
1253 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001254 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001255 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001256 if (ctxt->error != 0)
1257 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001258 SKIP_BLANKS;
1259 while (CUR == '/') {
William M. Brackfbb619f2005-06-06 13:49:18 +00001260 if (NXT(1) == '/') {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001261 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1262 NEXT;
1263 NEXT;
1264 SKIP_BLANKS;
1265 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001266 if (ctxt->error != 0)
1267 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001268 } else {
1269 PUSH(XML_OP_PARENT, NULL, NULL);
1270 NEXT;
1271 SKIP_BLANKS;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001272 if (CUR == 0) {
1273 ERROR5(NULL, NULL, NULL,
1274 "Incomplete expression '%s'.\n", ctxt->base);
1275 ctxt->error = 1;
1276 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001277 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001278 xmlCompileStepPattern(ctxt);
1279 if (ctxt->error != 0)
1280 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001281 }
1282 }
1283 }
Daniel Veillard56de87e2005-02-16 00:22:29 +00001284 if (CUR != 0) {
1285 ERROR5(NULL, NULL, NULL,
1286 "Failed to compile pattern %s\n", ctxt->base);
1287 ctxt->error = 1;
1288 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001289error:
1290 return;
1291}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001292
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001293/**
1294 * xmlCompileIDCXPathPath:
1295 * @ctxt: the compilation context
1296 *
1297 * Compile the Path Pattern and generates a precompiled
1298 * form suitable for fast matching.
1299 *
1300 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1301 */
1302static void
1303xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
1304 SKIP_BLANKS;
1305 if (CUR == '/') {
1306 ERROR5(NULL, NULL, NULL,
1307 "Unexpected selection of the document root in '%s'.\n",
1308 ctxt->base);
1309 goto error;
1310 }
1311 ctxt->comp->flags |= PAT_FROM_CUR;
1312
1313 if (CUR == '.') {
1314 /* "." - "self::node()" */
1315 NEXT;
1316 SKIP_BLANKS;
1317 if (CUR == 0) {
1318 /*
1319 * Selection of the context node.
1320 */
1321 PUSH(XML_OP_ELEM, NULL, NULL);
1322 return;
1323 }
1324 if (CUR != '/') {
1325 /* TODO: A more meaningful error message. */
1326 ERROR5(NULL, NULL, NULL,
1327 "Unexpected token after '.' in '%s'.\n", ctxt->base);
1328 goto error;
1329 }
1330 /* "./" - "self::node()/" */
1331 NEXT;
1332 SKIP_BLANKS;
1333 if (CUR == '/') {
1334 if (IS_BLANK_CH(PEEKPREV(1))) {
1335 /*
1336 * Disallow "./ /"
1337 */
1338 ERROR5(NULL, NULL, NULL,
1339 "Unexpected '/' token in '%s'.\n", ctxt->base);
1340 goto error;
1341 }
1342 /* ".//" - "self:node()/descendant-or-self::node()/" */
1343 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1344 NEXT;
1345 SKIP_BLANKS;
1346 }
1347 if (CUR == 0)
1348 goto error_unfinished;
1349 }
1350 /*
1351 * Process steps.
1352 */
1353 do {
1354 xmlCompileStepPattern(ctxt);
1355 if (ctxt->error != 0)
1356 goto error;
1357 SKIP_BLANKS;
1358 if (CUR != '/')
1359 break;
1360 PUSH(XML_OP_PARENT, NULL, NULL);
1361 NEXT;
1362 SKIP_BLANKS;
1363 if (CUR == '/') {
1364 /*
1365 * Disallow subsequent '//'.
1366 */
1367 ERROR5(NULL, NULL, NULL,
1368 "Unexpected subsequent '//' in '%s'.\n",
1369 ctxt->base);
1370 goto error;
1371 }
1372 if (CUR == 0)
1373 goto error_unfinished;
1374
1375 } while (CUR != 0);
1376
1377 if (CUR != 0) {
1378 ERROR5(NULL, NULL, NULL,
1379 "Failed to compile expression '%s'.\n", ctxt->base);
1380 ctxt->error = 1;
1381 }
1382 return;
1383error:
1384 ctxt->error = 1;
1385 return;
1386
1387error_unfinished:
1388 ctxt->error = 1;
1389 ERROR5(NULL, NULL, NULL,
1390 "Unfinished expression '%s'.\n", ctxt->base);
1391 return;
1392}
1393
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001394/************************************************************************
1395 * *
1396 * The streaming code *
1397 * *
1398 ************************************************************************/
1399
1400#ifdef DEBUG_STREAMING
1401static void
1402xmlDebugStreamComp(xmlStreamCompPtr stream) {
1403 int i;
1404
1405 if (stream == NULL) {
1406 printf("Stream: NULL\n");
1407 return;
1408 }
1409 printf("Stream: %d steps\n", stream->nbStep);
1410 for (i = 0;i < stream->nbStep;i++) {
1411 if (stream->steps[i].ns != NULL) {
1412 printf("{%s}", stream->steps[i].ns);
1413 }
1414 if (stream->steps[i].name == NULL) {
1415 printf("* ");
1416 } else {
1417 printf("%s ", stream->steps[i].name);
1418 }
1419 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1420 printf("root ");
1421 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1422 printf("// ");
1423 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1424 printf("final ");
1425 printf("\n");
1426 }
1427}
1428static void
1429xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1430 int i;
1431
1432 if (ctxt == NULL) {
1433 printf("Stream: NULL\n");
1434 return;
1435 }
1436 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1437 if (match)
1438 printf("matches\n");
1439 else
1440 printf("\n");
1441 for (i = 0;i < ctxt->nbState;i++) {
1442 if (ctxt->states[2 * i] < 0)
1443 printf(" %d: free\n", i);
1444 else {
1445 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1446 ctxt->states[(2 * i) + 1]);
1447 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1448 XML_STREAM_STEP_DESC)
1449 printf(" //\n");
1450 else
1451 printf("\n");
1452 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001453 }
1454}
1455#endif
1456/**
1457 * xmlNewStreamComp:
1458 * @size: the number of expected steps
1459 *
1460 * build a new compiled pattern for streaming
1461 *
1462 * Returns the new structure or NULL in case of error.
1463 */
1464static xmlStreamCompPtr
1465xmlNewStreamComp(int size) {
1466 xmlStreamCompPtr cur;
1467
1468 if (size < 4)
1469 size = 4;
1470
1471 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1472 if (cur == NULL) {
1473 ERROR(NULL, NULL, NULL,
1474 "xmlNewStreamComp: malloc failed\n");
1475 return(NULL);
1476 }
1477 memset(cur, 0, sizeof(xmlStreamComp));
1478 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1479 if (cur->steps == NULL) {
1480 xmlFree(cur);
1481 ERROR(NULL, NULL, NULL,
1482 "xmlNewStreamComp: malloc failed\n");
1483 return(NULL);
1484 }
1485 cur->nbStep = 0;
1486 cur->maxStep = size;
1487 return(cur);
1488}
1489
1490/**
1491 * xmlFreeStreamComp:
1492 * @comp: the compiled pattern for streaming
1493 *
1494 * Free the compiled pattern for streaming
1495 */
1496static void
1497xmlFreeStreamComp(xmlStreamCompPtr comp) {
1498 if (comp != NULL) {
1499 if (comp->steps != NULL)
1500 xmlFree(comp->steps);
1501 if (comp->dict != NULL)
1502 xmlDictFree(comp->dict);
1503 xmlFree(comp);
1504 }
1505}
1506
1507/**
1508 * xmlStreamCompAddStep:
1509 * @comp: the compiled pattern for streaming
1510 * @name: the first string, the name, or NULL for *
1511 * @ns: the second step, the namespace name
1512 * @flags: the flags for that step
1513 *
1514 * Add a new step to the compiled pattern
1515 *
1516 * Returns -1 in case of error or the step index if successful
1517 */
1518static int
1519xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001520 const xmlChar *ns, int nodeType, int flags) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001521 xmlStreamStepPtr cur;
1522
1523 if (comp->nbStep >= comp->maxStep) {
1524 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1525 comp->maxStep * 2 * sizeof(xmlStreamStep));
1526 if (cur == NULL) {
1527 ERROR(NULL, NULL, NULL,
1528 "xmlNewStreamComp: malloc failed\n");
1529 return(-1);
1530 }
1531 comp->steps = cur;
1532 comp->maxStep *= 2;
1533 }
1534 cur = &comp->steps[comp->nbStep++];
1535 cur->flags = flags;
1536 cur->name = name;
1537 cur->ns = ns;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001538 cur->nodeType = nodeType;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001539 return(comp->nbStep - 1);
1540}
1541
1542/**
1543 * xmlStreamCompile:
1544 * @comp: the precompiled pattern
1545 *
1546 * Tries to stream compile a pattern
1547 *
1548 * Returns -1 in case of failure and 0 in case of success.
1549 */
1550static int
1551xmlStreamCompile(xmlPatternPtr comp) {
1552 xmlStreamCompPtr stream;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001553 int i, s = 0, root = 0, flags = 0, prevs = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001554 xmlStepOp step;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001555
1556 if ((comp == NULL) || (comp->steps == NULL))
1557 return(-1);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001558 /*
1559 * special case for .
1560 */
1561 if ((comp->nbStep == 1) &&
1562 (comp->steps[0].op == XML_OP_ELEM) &&
1563 (comp->steps[0].value == NULL) &&
1564 (comp->steps[0].value2 == NULL)) {
1565 stream = xmlNewStreamComp(0);
1566 if (stream == NULL)
1567 return(-1);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001568 /* Note that the stream will have no steps in this case. */
1569 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001570 comp->stream = stream;
1571 return(0);
1572 }
1573
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001574 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1575 if (stream == NULL)
1576 return(-1);
1577 if (comp->dict != NULL) {
1578 stream->dict = comp->dict;
1579 xmlDictReference(stream->dict);
1580 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001581
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001582 i = 0;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001583 if (comp->flags & PAT_FROM_ROOT)
1584 stream->flags |= XML_STREAM_FROM_ROOT;
1585
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001586 for (;i < comp->nbStep;i++) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001587 step = comp->steps[i];
1588 switch (step.op) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001589 case XML_OP_END:
1590 break;
1591 case XML_OP_ROOT:
1592 if (i != 0)
1593 goto error;
1594 root = 1;
1595 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001596 case XML_OP_NS:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001597 s = xmlStreamCompAddStep(stream, NULL, step.value,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001598 XML_ELEMENT_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001599 if (s < 0)
1600 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001601 prevs = s;
1602 flags = 0;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001603 break;
1604 case XML_OP_ATTR:
1605 flags |= XML_STREAM_STEP_ATTR;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001606 prevs = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001607 s = xmlStreamCompAddStep(stream,
1608 step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001609 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001610 if (s < 0)
1611 goto error;
1612 break;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001613 case XML_OP_ELEM:
1614 if ((step.value == NULL) && (step.value2 == NULL)) {
1615 /*
1616 * We have a "." or "self::node()" here.
1617 * Eliminate redundant self::node() tests like in "/./."
1618 * or "//./"
1619 * The only case we won't eliminate is "//.", i.e. if
1620 * self::node() is the last node test and we had
1621 * continuation somewhere beforehand.
1622 */
1623 if ((comp->nbStep == i + 1) &&
1624 (flags & XML_STREAM_STEP_DESC)) {
1625 /*
1626 * Mark the special case where the expression resolves
1627 * to any type of node.
1628 */
1629 if (comp->nbStep == i + 1) {
1630 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1631 }
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001632 flags |= XML_STREAM_STEP_NODE;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001633 s = xmlStreamCompAddStep(stream, NULL, NULL,
1634 XML_STREAM_ANY_NODE, flags);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001635 if (s < 0)
1636 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001637 flags = 0;
1638 /*
1639 * If there was a previous step, mark it to be added to
1640 * the result node-set; this is needed since only
1641 * the last step will be marked as "final" and only
1642 * "final" nodes are added to the resulting set.
1643 */
1644 if (prevs != -1) {
1645 stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1646 prevs = -1;
1647 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001648 break;
1649
1650 } else {
1651 /* Just skip this one. */
1652 continue;
1653 }
1654 }
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001655 /* An element node. */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001656 s = xmlStreamCompAddStep(stream, step.value, step.value2,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001657 XML_ELEMENT_NODE, flags);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001658 if (s < 0)
1659 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001660 prevs = s;
1661 flags = 0;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001662 break;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001663 case XML_OP_CHILD:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001664 /* An element node child. */
1665 s = xmlStreamCompAddStep(stream, step.value, step.value2,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001666 XML_ELEMENT_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001667 if (s < 0)
1668 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001669 prevs = s;
1670 flags = 0;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001671 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001672 case XML_OP_ALL:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001673 s = xmlStreamCompAddStep(stream, NULL, NULL,
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001674 XML_ELEMENT_NODE, flags);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001675 if (s < 0)
1676 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001677 prevs = s;
1678 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001679 break;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001680 case XML_OP_PARENT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001681 break;
1682 case XML_OP_ANCESTOR:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001683 /* Skip redundant continuations. */
1684 if (flags & XML_STREAM_STEP_DESC)
1685 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001686 flags |= XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001687 /*
1688 * Mark the expression as having "//".
1689 */
1690 if ((stream->flags & XML_STREAM_DESC) == 0)
1691 stream->flags |= XML_STREAM_DESC;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001692 break;
1693 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001694 }
1695 if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1696 /*
1697 * If this should behave like a real pattern, we will mark
1698 * the first step as having "//", to be reentrant on every
1699 * tree level.
1700 */
1701 if ((stream->flags & XML_STREAM_DESC) == 0)
1702 stream->flags |= XML_STREAM_DESC;
1703
1704 if (stream->nbStep > 0) {
1705 if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0)
1706 stream->steps[0].flags |= XML_STREAM_STEP_DESC;
1707 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001708 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001709 if (stream->nbStep <= s)
1710 goto error;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001711 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1712 if (root)
1713 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1714#ifdef DEBUG_STREAMING
1715 xmlDebugStreamComp(stream);
1716#endif
1717 comp->stream = stream;
1718 return(0);
1719error:
1720 xmlFreeStreamComp(stream);
1721 return(0);
1722}
1723
1724/**
1725 * xmlNewStreamCtxt:
1726 * @size: the number of expected states
1727 *
1728 * build a new stream context
1729 *
1730 * Returns the new structure or NULL in case of error.
1731 */
1732static xmlStreamCtxtPtr
1733xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1734 xmlStreamCtxtPtr cur;
1735
1736 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1737 if (cur == NULL) {
1738 ERROR(NULL, NULL, NULL,
1739 "xmlNewStreamCtxt: malloc failed\n");
1740 return(NULL);
1741 }
1742 memset(cur, 0, sizeof(xmlStreamCtxt));
1743 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1744 if (cur->states == NULL) {
1745 xmlFree(cur);
1746 ERROR(NULL, NULL, NULL,
1747 "xmlNewStreamCtxt: malloc failed\n");
1748 return(NULL);
1749 }
1750 cur->nbState = 0;
1751 cur->maxState = 4;
1752 cur->level = 0;
1753 cur->comp = stream;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001754 cur->blockLevel = -1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001755 return(cur);
1756}
1757
1758/**
1759 * xmlFreeStreamCtxt:
1760 * @stream: the stream context
1761 *
1762 * Free the stream context
1763 */
1764void
1765xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001766 xmlStreamCtxtPtr next;
1767
1768 while (stream != NULL) {
1769 next = stream->next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001770 if (stream->states != NULL)
1771 xmlFree(stream->states);
1772 xmlFree(stream);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001773 stream = next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001774 }
1775}
1776
1777/**
1778 * xmlStreamCtxtAddState:
1779 * @comp: the stream context
1780 * @idx: the step index for that streaming state
1781 *
1782 * Add a new state to the stream context
1783 *
1784 * Returns -1 in case of error or the state index if successful
1785 */
1786static int
1787xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1788 int i;
1789 for (i = 0;i < comp->nbState;i++) {
1790 if (comp->states[2 * i] < 0) {
1791 comp->states[2 * i] = idx;
1792 comp->states[2 * i + 1] = level;
1793 return(i);
1794 }
1795 }
1796 if (comp->nbState >= comp->maxState) {
1797 int *cur;
1798
1799 cur = (int *) xmlRealloc(comp->states,
1800 comp->maxState * 4 * sizeof(int));
1801 if (cur == NULL) {
1802 ERROR(NULL, NULL, NULL,
1803 "xmlNewStreamCtxt: malloc failed\n");
1804 return(-1);
1805 }
1806 comp->states = cur;
1807 comp->maxState *= 2;
1808 }
1809 comp->states[2 * comp->nbState] = idx;
1810 comp->states[2 * comp->nbState++ + 1] = level;
1811 return(comp->nbState - 1);
1812}
1813
1814/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001815 * xmlStreamPushInternal:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001816 * @stream: the stream context
1817 * @name: the current name
1818 * @ns: the namespace name
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001819 * @nodeType: the type of the node
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001820 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001821 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1822 * indicated a dictionary, then strings for name and ns will be expected
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001823 * to come from the dictionary.
1824 * Both @name and @ns being NULL means the / i.e. the root of the document.
1825 * This can also act as a reset.
1826 *
1827 * Returns: -1 in case of error, 1 if the current state in the stream is a
1828 * match and 0 otherwise.
1829 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001830static int
1831xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1832 const xmlChar *name, const xmlChar *ns,
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001833 int nodeType) {
William M. Brack537f1172005-06-14 22:02:59 +00001834 int ret = 0, err = 0, final = 0, tmp, i, m, match, step, desc;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001835 xmlStreamCompPtr comp;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001836#ifdef DEBUG_STREAMING
1837 xmlStreamCtxtPtr orig = stream;
1838#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001839
1840 if ((stream == NULL) || (stream->nbState < 0))
1841 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001842
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001843 while (stream != NULL) {
1844 comp = stream->comp;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001845
1846 if ((nodeType == XML_ELEMENT_NODE) &&
1847 (name == NULL) && (ns == NULL)) {
1848 /* We have a document node here (or a reset). */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001849 stream->nbState = 0;
1850 stream->level = 0;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001851 stream->blockLevel = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001852 if (comp->flags & XML_STREAM_FROM_ROOT) {
1853 if (comp->nbStep == 0) {
1854 /* TODO: We have a "/." here? */
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001855 ret = 1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001856 } else {
1857 if ((comp->nbStep == 1) &&
1858 (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
1859 (comp->steps[0].flags & XML_STREAM_STEP_DESC))
1860 {
1861 /*
1862 * In the case of "//." the document node will match
1863 * as well.
1864 */
1865 ret = 1;
1866 } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1867 /* TODO: Do we need this ? */
1868 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1869 if (tmp < 0)
1870 err++;
1871 }
1872 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001873 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001874 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001875 continue; /* while */
1876 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001877
1878 /*
1879 * Fast check for ".".
1880 */
1881 if (comp->nbStep == 0) {
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001882 /*
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00001883 * / and . are handled at the XPath node set creation
1884 * level by checking min depth
1885 */
1886 if (stream->flags & XML_PATTERN_XPATH) {
1887 stream = stream->next;
1888 continue; /* while */
1889 }
1890 /*
William M. Brackea152c02005-06-09 18:12:28 +00001891 * For non-pattern like evaluation like XML Schema IDCs
1892 * or traditional XPath expressions, this will match if
1893 * we are at the first level only, otherwise on every level.
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001894 */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001895 if ((nodeType != XML_ATTRIBUTE_NODE) &&
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001896 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1897 (stream->level == 0))) {
1898 ret = 1;
1899 }
1900 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001901 goto stream_next;
1902 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001903 if (stream->blockLevel != -1) {
1904 /*
1905 * Skip blocked expressions.
1906 */
1907 stream->level++;
1908 goto stream_next;
William M. Brackea152c02005-06-09 18:12:28 +00001909 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001910
1911 if ((nodeType != XML_ELEMENT_NODE) &&
1912 (nodeType != XML_ATTRIBUTE_NODE) &&
1913 ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) {
1914 /*
1915 * No need to process nodes of other types if we don't
1916 * resolve to those types.
1917 * TODO: Do we need to block the context here?
1918 */
1919 stream->level++;
1920 goto stream_next;
1921 }
1922
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001923 /*
1924 * Check evolution of existing states
1925 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001926 i = 0;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001927 m = stream->nbState;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001928 while (i < m) {
1929 if ((comp->flags & XML_STREAM_DESC) == 0) {
1930 /*
1931 * If there is no "//", then only the last
1932 * added state is of interest.
1933 */
1934 step = stream->states[2 * (stream->nbState -1)];
1935 /*
1936 * TODO: Security check, should not happen, remove it.
1937 */
1938 if (stream->states[(2 * (stream->nbState -1)) + 1] <
1939 stream->level) {
1940 return (-1);
1941 }
1942 desc = 0;
1943 /* loop-stopper */
1944 i = m;
1945 } else {
1946 /*
1947 * If there are "//", then we need to process every "//"
1948 * occuring in the states, plus any other state for this
1949 * level.
1950 */
1951 step = stream->states[2 * i];
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001952
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001953 /* TODO: should not happen anymore: dead states */
1954 if (step < 0)
1955 goto next_state;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001956
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001957 tmp = stream->states[(2 * i) + 1];
1958
1959 /* skip new states just added */
1960 if (tmp > stream->level)
1961 goto next_state;
1962
1963 /* skip states at ancestor levels, except if "//" */
1964 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1965 if ((tmp < stream->level) && (!desc))
1966 goto next_state;
1967 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001968 /*
1969 * Check for correct node-type.
1970 */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001971 if (comp->steps[step].nodeType != nodeType) {
1972 if (comp->steps[step].nodeType == XML_ATTRIBUTE_NODE) {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001973 /*
1974 * Block this expression for deeper evaluation.
1975 */
1976 if ((comp->flags & XML_STREAM_DESC) == 0)
1977 stream->blockLevel = stream->level +1;
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00001978 goto next_state;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001979 } else if (comp->steps[step].nodeType != XML_STREAM_ANY_NODE)
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00001980 goto next_state;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001981 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001982 /*
1983 * Compare local/namespace-name.
1984 */
1985 match = 0;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001986 if (comp->steps[step].nodeType == XML_STREAM_ANY_NODE) {
1987 match = 1;
1988 } else if (comp->dict) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001989 if (comp->steps[step].name == NULL) {
1990 if (comp->steps[step].ns == NULL)
1991 match = 1;
1992 else
1993 match = (comp->steps[step].ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001994 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001995 match = ((comp->steps[step].name == name) &&
1996 (comp->steps[step].ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001997 }
1998 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001999 if (comp->steps[step].name == NULL) {
2000 if (comp->steps[step].ns == NULL)
2001 match = 1;
2002 else
2003 match = xmlStrEqual(comp->steps[step].ns, ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002004 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002005 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
2006 (xmlStrEqual(comp->steps[step].ns, ns)));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002007 }
2008 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002009 if (match) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002010 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
2011 if (desc) {
2012 if (final) {
2013 ret = 1;
2014 } else {
2015 /* descending match create a new state */
2016 xmlStreamCtxtAddState(stream, step + 1,
2017 stream->level + 1);
2018 }
2019 } else {
2020 if (final) {
2021 ret = 1;
2022 } else {
2023 xmlStreamCtxtAddState(stream, step + 1,
2024 stream->level + 1);
2025 }
2026 }
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00002027 if ((ret != 1) &&
2028 (comp->steps[step].flags & XML_STREAM_STEP_IN_SET)) {
2029 /*
2030 * Check if we have a special case like "foo/bar//.", where
2031 * "foo" is selected as well.
2032 */
2033 ret = 1;
2034 }
2035 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002036 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2037 ((! match) || final)) {
2038 /*
2039 * Mark this expression as blocked for any evaluation at
2040 * deeper levels. Note that this includes "/foo"
2041 * expressions if the *pattern* behaviour is used.
2042 */
2043 stream->blockLevel = stream->level +1;
2044 }
2045next_state:
2046 i++;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002047 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002048
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002049 stream->level++;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002050
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002051 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002052 * Re/enter the expression.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002053 * Don't reenter if it's an absolute expression like "/foo",
2054 * except "//foo".
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002055 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002056 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT)
2057 goto stream_next;
2058
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002059 desc = comp->steps[0].flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002060 if (stream->flags & XML_PATTERN_NOTPATTERN) {
2061 /*
2062 * Re/enter the expression if it is a "descendant" one,
2063 * or if we are at the 1st level of evaluation.
2064 */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002065
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002066 if (stream->level == 1) {
2067 if (XML_STREAM_XS_IDC(stream)) {
William M. Brackea152c02005-06-09 18:12:28 +00002068 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002069 * XS-IDC: The missing "self::node()" will always
2070 * match the first given node.
2071 */
William M. Brackea152c02005-06-09 18:12:28 +00002072 goto stream_next;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002073 } else
2074 goto compare;
2075 }
2076 /*
2077 * A "//" is always reentrant.
2078 */
2079 if (desc)
2080 goto compare;
2081
2082 /*
2083 * XS-IDC: Process the 2nd level, since the missing
2084 * "self::node()" is responsible for the 2nd level being
2085 * the real start level.
2086 */
2087 if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
2088 goto compare;
2089
2090 goto stream_next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002091 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002092
2093compare:
2094 /*
2095 * Check expected node-type.
2096 */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002097 if (comp->steps[0].nodeType != nodeType) {
2098 if (nodeType == XML_ATTRIBUTE_NODE)
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002099 goto stream_next;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002100 else if (comp->steps[0].nodeType != XML_STREAM_ANY_NODE)
2101 goto stream_next;
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002102 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002103 /*
2104 * Compare local/namespace-name.
2105 */
2106 match = 0;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002107 if (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) {
2108 match = 1;
2109 } else if (comp->steps[0].name == NULL) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002110 if (comp->steps[0].ns == NULL)
2111 match = 1;
2112 else {
2113 if (comp->dict)
2114 match = (comp->steps[0].ns == ns);
2115 else
2116 match = xmlStrEqual(comp->steps[0].ns, ns);
2117 }
2118 } else {
2119 if (comp->dict)
2120 match = ((comp->steps[0].name == name) &&
2121 (comp->steps[0].ns == ns));
2122 else
2123 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
2124 (xmlStrEqual(comp->steps[0].ns, ns)));
2125 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002126 final = comp->steps[0].flags & XML_STREAM_STEP_FINAL;
2127 if (match) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002128 if (final)
2129 ret = 1;
2130 else
2131 xmlStreamCtxtAddState(stream, 1, stream->level);
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00002132 if ((ret != 1) &&
2133 (comp->steps[0].flags & XML_STREAM_STEP_IN_SET)) {
2134 /*
2135 * Check if we have a special case like "foo//.", where
2136 * "foo" is selected as well.
2137 */
2138 ret = 1;
2139 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002140 }
2141 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2142 ((! match) || final)) {
2143 /*
2144 * Mark this expression as blocked for any evaluation at
2145 * deeper levels.
2146 */
2147 stream->blockLevel = stream->level;
2148 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002149
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002150stream_next:
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002151 stream = stream->next;
2152 } /* while stream != NULL */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002153
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002154 if (err > 0)
2155 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002156#ifdef DEBUG_STREAMING
2157 xmlDebugStreamCtxt(orig, ret);
2158#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002159 return(ret);
2160}
2161
2162/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002163 * xmlStreamPush:
2164 * @stream: the stream context
2165 * @name: the current name
2166 * @ns: the namespace name
2167 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002168 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2169 * indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002170 * to come from the dictionary.
2171 * Both @name and @ns being NULL means the / i.e. the root of the document.
2172 * This can also act as a reset.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002173 * Otherwise the function will act as if it has been given an element-node.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002174 *
2175 * Returns: -1 in case of error, 1 if the current state in the stream is a
2176 * match and 0 otherwise.
2177 */
2178int
2179xmlStreamPush(xmlStreamCtxtPtr stream,
2180 const xmlChar *name, const xmlChar *ns) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002181 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE));
2182}
2183
2184/**
Daniel Veillard67952602006-01-05 15:29:44 +00002185 * xmlStreamPushNode:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002186 * @stream: the stream context
2187 * @name: the current name
2188 * @ns: the namespace name
2189 * @nodeType: the type of the node being pushed
2190 *
2191 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2192 * indicated a dictionary, then strings for name and ns will be expected
2193 * to come from the dictionary.
2194 * Both @name and @ns being NULL means the / i.e. the root of the document.
2195 * This can also act as a reset.
2196 * Different from xmlStreamPush() this function can be fed with nodes of type:
2197 * element-, attribute-, text-, cdata-section-, comment- and
2198 * processing-instruction-node.
2199 *
2200 * Returns: -1 in case of error, 1 if the current state in the stream is a
2201 * match and 0 otherwise.
2202 */
2203int
2204xmlStreamPushNode(xmlStreamCtxtPtr stream,
2205 const xmlChar *name, const xmlChar *ns,
2206 int nodeType)
2207{
2208 return (xmlStreamPushInternal(stream, name, ns,
2209 nodeType));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002210}
2211
2212/**
2213* xmlStreamPushAttr:
2214* @stream: the stream context
2215* @name: the current name
2216* @ns: the namespace name
2217*
William M. Brackfbb619f2005-06-06 13:49:18 +00002218* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2219* indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002220* to come from the dictionary.
2221* Both @name and @ns being NULL means the / i.e. the root of the document.
2222* This can also act as a reset.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002223* Otherwise the function will act as if it has been given an attribute-node.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002224*
2225* Returns: -1 in case of error, 1 if the current state in the stream is a
2226* match and 0 otherwise.
2227*/
2228int
2229xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2230 const xmlChar *name, const xmlChar *ns) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002231 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002232}
2233
2234/**
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002235 * xmlStreamPop:
2236 * @stream: the stream context
2237 *
2238 * push one level from the stream.
2239 *
2240 * Returns: -1 in case of error, 0 otherwise.
2241 */
2242int
2243xmlStreamPop(xmlStreamCtxtPtr stream) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002244 int i, lev;
Kasimier T. Buchcik65c2f1d2005-10-17 12:39:58 +00002245
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002246 if (stream == NULL)
2247 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002248 while (stream != NULL) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002249 /*
2250 * Reset block-level.
2251 */
2252 if (stream->blockLevel == stream->level)
2253 stream->blockLevel = -1;
2254
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002255 stream->level--;
2256 if (stream->level < 0)
Kasimier T. Buchcik65c2f1d2005-10-17 12:39:58 +00002257 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002258 /*
2259 * Check evolution of existing states
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002260 */
2261 for (i = stream->nbState -1; i >= 0; i--) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002262 /* discard obsoleted states */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002263 lev = stream->states[(2 * i) + 1];
2264 if (lev > stream->level)
2265 stream->nbState--;
2266 if (lev <= stream->level)
2267 break;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002268 }
2269 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00002270 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002271 return(0);
2272}
2273
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002274/**
2275 * xmlStreamWantsAnyNode:
Daniel Veillard67952602006-01-05 15:29:44 +00002276 * @streamCtxt: the stream context
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002277 *
2278 * Query if the streaming pattern additionally needs to be fed with
2279 * text-, cdata-section-, comment- and processing-instruction-nodes.
2280 * If the result is 0 then only element-nodes and attribute-nodes
2281 * need to be pushed.
2282 *
2283 * Returns: 1 in case of need of nodes of the above described types,
2284 * 0 otherwise. -1 on API errors.
2285 */
2286int
2287xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
2288{
2289 if (streamCtxt == NULL)
2290 return(-1);
2291 while (streamCtxt != NULL) {
2292 if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
2293 return(1);
2294 streamCtxt = streamCtxt->next;
2295 }
2296 return(0);
2297}
2298
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002299/************************************************************************
2300 * *
2301 * The public interfaces *
2302 * *
2303 ************************************************************************/
2304
2305/**
2306 * xmlPatterncompile:
2307 * @pattern: the pattern to compile
William M. Brackfbb619f2005-06-06 13:49:18 +00002308 * @dict: an optional dictionary for interned strings
Daniel Veillarded6c5492005-07-23 15:00:22 +00002309 * @flags: compilation flags, see xmlPatternFlags
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002310 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002311 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002312 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002313 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002314 * Returns the compiled form of the pattern or NULL in case of error
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002315 */
2316xmlPatternPtr
Daniel Veillarded6c5492005-07-23 15:00:22 +00002317xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002318 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002319 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002320 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002321 const xmlChar *or, *start;
2322 xmlChar *tmp = NULL;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002323 int type = 0;
2324 int streamable = 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002325
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002326 if (pattern == NULL)
2327 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002328
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002329 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002330 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002331 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002332 tmp = NULL;
2333 while ((*or != 0) && (*or != '|')) or++;
2334 if (*or == 0)
2335 ctxt = xmlNewPatParserContext(start, dict, namespaces);
2336 else {
2337 tmp = xmlStrndup(start, or - start);
2338 if (tmp != NULL) {
2339 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2340 }
2341 or++;
2342 }
2343 if (ctxt == NULL) goto error;
2344 cur = xmlNewPattern();
2345 if (cur == NULL) goto error;
2346 if (ret == NULL)
2347 ret = cur;
2348 else {
2349 cur->next = ret->next;
2350 ret->next = cur;
2351 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002352 cur->flags = flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002353 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002354
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002355 if (XML_STREAM_XS_IDC(cur))
2356 xmlCompileIDCXPathPath(ctxt);
2357 else
2358 xmlCompilePathPattern(ctxt);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002359 if (ctxt->error != 0)
2360 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002361 xmlFreePatParserContext(ctxt);
William M. Brackfbb619f2005-06-06 13:49:18 +00002362 ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002363
2364
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002365 if (streamable) {
2366 if (type == 0) {
2367 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2368 } else if (type == PAT_FROM_ROOT) {
2369 if (cur->flags & PAT_FROM_CUR)
2370 streamable = 0;
2371 } else if (type == PAT_FROM_CUR) {
2372 if (cur->flags & PAT_FROM_ROOT)
2373 streamable = 0;
2374 }
2375 }
2376 if (streamable)
2377 xmlStreamCompile(cur);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002378 if (xmlReversePattern(cur) < 0)
2379 goto error;
William M. Brackea152c02005-06-09 18:12:28 +00002380 if (tmp != NULL) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002381 xmlFree(tmp);
William M. Brackea152c02005-06-09 18:12:28 +00002382 tmp = NULL;
2383 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002384 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002385 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002386 if (streamable == 0) {
2387 cur = ret;
2388 while (cur != NULL) {
2389 if (cur->stream != NULL) {
2390 xmlFreeStreamComp(cur->stream);
2391 cur->stream = NULL;
2392 }
2393 cur = cur->next;
2394 }
2395 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002396
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002397 return(ret);
2398error:
2399 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2400 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002401 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002402 return(NULL);
2403}
2404
2405/**
2406 * xmlPatternMatch:
2407 * @comp: the precompiled pattern
2408 * @node: a node
2409 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002410 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002411 *
2412 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2413 */
2414int
2415xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2416{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002417 int ret = 0;
2418
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002419 if ((comp == NULL) || (node == NULL))
2420 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002421
2422 while (comp != NULL) {
2423 ret = xmlPatMatch(comp, node);
2424 if (ret != 0)
2425 return(ret);
2426 comp = comp->next;
2427 }
2428 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002429}
2430
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002431/**
2432 * xmlPatternGetStreamCtxt:
2433 * @comp: the precompiled pattern
2434 *
2435 * Get a streaming context for that pattern
2436 * Use xmlFreeStreamCtxt to free the context.
2437 *
2438 * Returns a pointer to the context or NULL in case of failure
2439 */
2440xmlStreamCtxtPtr
2441xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2442{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002443 xmlStreamCtxtPtr ret = NULL, cur;
2444
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002445 if ((comp == NULL) || (comp->stream == NULL))
2446 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002447
2448 while (comp != NULL) {
2449 if (comp->stream == NULL)
2450 goto failed;
2451 cur = xmlNewStreamCtxt(comp->stream);
2452 if (cur == NULL)
2453 goto failed;
2454 if (ret == NULL)
2455 ret = cur;
2456 else {
2457 cur->next = ret->next;
2458 ret->next = cur;
2459 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002460 cur->flags = comp->flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002461 comp = comp->next;
2462 }
2463 return(ret);
2464failed:
2465 xmlFreeStreamCtxt(ret);
2466 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002467}
2468
Daniel Veillard56de87e2005-02-16 00:22:29 +00002469/**
2470 * xmlPatternStreamable:
2471 * @comp: the precompiled pattern
2472 *
2473 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2474 * should work.
2475 *
2476 * Returns 1 if streamable, 0 if not and -1 in case of error.
2477 */
2478int
2479xmlPatternStreamable(xmlPatternPtr comp) {
2480 if (comp == NULL)
2481 return(-1);
2482 while (comp != NULL) {
2483 if (comp->stream == NULL)
2484 return(0);
2485 comp = comp->next;
2486 }
2487 return(1);
2488}
2489
2490/**
2491 * xmlPatternMaxDepth:
2492 * @comp: the precompiled pattern
2493 *
2494 * Check the maximum depth reachable by a pattern
2495 *
2496 * Returns -2 if no limit (using //), otherwise the depth,
2497 * and -1 in case of error
2498 */
2499int
2500xmlPatternMaxDepth(xmlPatternPtr comp) {
2501 int ret = 0, i;
2502 if (comp == NULL)
2503 return(-1);
2504 while (comp != NULL) {
2505 if (comp->stream == NULL)
2506 return(-1);
2507 for (i = 0;i < comp->stream->nbStep;i++)
2508 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2509 return(-2);
2510 if (comp->stream->nbStep > ret)
2511 ret = comp->stream->nbStep;
2512 comp = comp->next;
2513 }
2514 return(ret);
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002515}
Daniel Veillard56de87e2005-02-16 00:22:29 +00002516
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002517/**
2518 * xmlPatternMinDepth:
2519 * @comp: the precompiled pattern
2520 *
2521 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2522 * part of the set.
2523 *
2524 * Returns -1 in case of error otherwise the depth,
2525 *
2526 */
2527int
2528xmlPatternMinDepth(xmlPatternPtr comp) {
2529 int ret = 12345678;
2530 if (comp == NULL)
2531 return(-1);
2532 while (comp != NULL) {
2533 if (comp->stream == NULL)
2534 return(-1);
2535 if (comp->stream->nbStep < ret)
2536 ret = comp->stream->nbStep;
2537 if (ret == 0)
2538 return(0);
2539 comp = comp->next;
2540 }
2541 return(ret);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002542}
2543
2544/**
2545 * xmlPatternFromRoot:
2546 * @comp: the precompiled pattern
2547 *
2548 * Check if the pattern must be looked at from the root.
2549 *
2550 * Returns 1 if true, 0 if false and -1 in case of error
2551 */
2552int
2553xmlPatternFromRoot(xmlPatternPtr comp) {
2554 if (comp == NULL)
2555 return(-1);
2556 while (comp != NULL) {
2557 if (comp->stream == NULL)
2558 return(-1);
2559 if (comp->flags & PAT_FROM_ROOT)
2560 return(1);
2561 comp = comp->next;
2562 }
2563 return(0);
2564
2565}
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002566
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002567#define bottom_pattern
2568#include "elfgcchack.h"
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002569#endif /* LIBXML_PATTERN_ENABLED */