blob: 7bd8bb010ccd45837707a9797e2fb3eb97b80b20 [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 */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000041#define SUPPORT_IDC
Daniel Veillard2fc6df92005-01-30 18:42:55 +000042
Daniel Veillardb3de70c2003-12-02 22:32:15 +000043#define ERROR(a, b, c, d)
44#define ERROR5(a, b, c, d, e)
45
Daniel Veillard2fc6df92005-01-30 18:42:55 +000046#define XML_STREAM_STEP_DESC 1
47#define XML_STREAM_STEP_FINAL 2
48#define XML_STREAM_STEP_ROOT 4
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +000049#define XML_STREAM_STEP_ATTR 8
Daniel Veillard2fc6df92005-01-30 18:42:55 +000050
William M. Brackea152c02005-06-09 18:12:28 +000051#define XML_PATTERN_NOTPATTERN (XML_PATTERN_XPATH | \
52 XML_PATTERN_XSSEL | \
53 XML_PATTERN_XSFIELD)
54#define XML_PATTERN_XSD (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD)
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000055
Daniel Veillard2fc6df92005-01-30 18:42:55 +000056typedef struct _xmlStreamStep xmlStreamStep;
57typedef xmlStreamStep *xmlStreamStepPtr;
58struct _xmlStreamStep {
59 int flags; /* properties of that step */
60 const xmlChar *name; /* first string value if NULL accept all */
61 const xmlChar *ns; /* second string value */
62};
63
64typedef struct _xmlStreamComp xmlStreamComp;
65typedef xmlStreamComp *xmlStreamCompPtr;
66struct _xmlStreamComp {
William M. Brackfbb619f2005-06-06 13:49:18 +000067 xmlDict *dict; /* the dictionary if any */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000068 int nbStep; /* number of steps in the automata */
69 int maxStep; /* allocated number of steps */
70 xmlStreamStepPtr steps; /* the array of steps */
71};
72
73struct _xmlStreamCtxt {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +000074 struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000075 xmlStreamCompPtr comp; /* the compiled stream */
William M. Brackfbb619f2005-06-06 13:49:18 +000076 int nbState; /* number of states in the automata */
77 int maxState; /* allocated number of states */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000078 int level; /* how deep are we ? */
79 int *states; /* the array of step indexes */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000080 int flags; /* validation options */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000081};
82
83static void xmlFreeStreamComp(xmlStreamCompPtr comp);
84
Daniel Veillardb3de70c2003-12-02 22:32:15 +000085/*
86 * Types are private:
87 */
88
89typedef enum {
90 XML_OP_END=0,
91 XML_OP_ROOT,
92 XML_OP_ELEM,
93 XML_OP_CHILD,
94 XML_OP_ATTR,
95 XML_OP_PARENT,
96 XML_OP_ANCESTOR,
97 XML_OP_NS,
98 XML_OP_ALL
99} xmlPatOp;
100
101
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000102typedef struct _xmlStepState xmlStepState;
103typedef xmlStepState *xmlStepStatePtr;
104struct _xmlStepState {
105 int step;
106 xmlNodePtr node;
107};
108
109typedef struct _xmlStepStates xmlStepStates;
110typedef xmlStepStates *xmlStepStatesPtr;
111struct _xmlStepStates {
112 int nbstates;
113 int maxstates;
114 xmlStepStatePtr states;
115};
116
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000117typedef struct _xmlStepOp xmlStepOp;
118typedef xmlStepOp *xmlStepOpPtr;
119struct _xmlStepOp {
120 xmlPatOp op;
121 const xmlChar *value;
122 const xmlChar *value2;
123};
124
William M. Brackea152c02005-06-09 18:12:28 +0000125#define PAT_FROM_ROOT (1<<8)
126#define PAT_FROM_CUR (1<<9)
Daniel Veillard56de87e2005-02-16 00:22:29 +0000127
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000128struct _xmlPattern {
129 void *data; /* the associated template */
William M. Brackfbb619f2005-06-06 13:49:18 +0000130 xmlDictPtr dict; /* the optional dictionary */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000131 struct _xmlPattern *next; /* next pattern if | is used */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000132 const xmlChar *pattern; /* the pattern */
William M. Brackea152c02005-06-09 18:12:28 +0000133 xmlPatternFlags flags; /* flags */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000134 int nbStep;
135 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000136 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000137 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000138};
139
140typedef struct _xmlPatParserContext xmlPatParserContext;
141typedef xmlPatParserContext *xmlPatParserContextPtr;
142struct _xmlPatParserContext {
143 const xmlChar *cur; /* the current char being parsed */
144 const xmlChar *base; /* the full expression */
145 int error; /* error code */
William M. Brackfbb619f2005-06-06 13:49:18 +0000146 xmlDictPtr dict; /* the dictionary if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000147 xmlPatternPtr comp; /* the result */
148 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000149 const xmlChar **namespaces; /* the namespaces definitions */
150 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000151};
152
153/************************************************************************
154 * *
155 * Type functions *
156 * *
157 ************************************************************************/
158
159/**
160 * xmlNewPattern:
161 *
162 * Create a new XSLT Pattern
163 *
164 * Returns the newly allocated xmlPatternPtr or NULL in case of error
165 */
166static xmlPatternPtr
167xmlNewPattern(void) {
168 xmlPatternPtr cur;
169
170 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
171 if (cur == NULL) {
172 ERROR(NULL, NULL, NULL,
173 "xmlNewPattern : malloc failed\n");
174 return(NULL);
175 }
176 memset(cur, 0, sizeof(xmlPattern));
177 cur->maxStep = 10;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000178 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
179 if (cur->steps == NULL) {
180 xmlFree(cur);
181 ERROR(NULL, NULL, NULL,
182 "xmlNewPattern : malloc failed\n");
183 return(NULL);
184 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000185 return(cur);
186}
187
188/**
189 * xmlFreePattern:
190 * @comp: an XSLT comp
191 *
192 * Free up the memory allocated by @comp
193 */
194void
195xmlFreePattern(xmlPatternPtr comp) {
196 xmlStepOpPtr op;
197 int i;
198
199 if (comp == NULL)
200 return;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000201 if (comp->next != NULL)
202 xmlFreePattern(comp->next);
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000203 if (comp->stream != NULL)
204 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000205 if (comp->pattern != NULL)
206 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000207 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000208 if (comp->dict == NULL) {
209 for (i = 0;i < comp->nbStep;i++) {
210 op = &comp->steps[i];
211 if (op->value != NULL)
212 xmlFree((xmlChar *) op->value);
213 if (op->value2 != NULL)
214 xmlFree((xmlChar *) op->value2);
215 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000216 }
217 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000218 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000219 if (comp->dict != NULL)
220 xmlDictFree(comp->dict);
221
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000222 memset(comp, -1, sizeof(xmlPattern));
223 xmlFree(comp);
224}
225
226/**
227 * xmlFreePatternList:
228 * @comp: an XSLT comp list
229 *
230 * Free up the memory allocated by all the elements of @comp
231 */
232void
233xmlFreePatternList(xmlPatternPtr comp) {
234 xmlPatternPtr cur;
235
236 while (comp != NULL) {
237 cur = comp;
238 comp = comp->next;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +0000239 cur->next = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000240 xmlFreePattern(cur);
241 }
242}
243
244/**
245 * xmlNewPatParserContext:
246 * @pattern: the pattern context
William M. Brackfbb619f2005-06-06 13:49:18 +0000247 * @dict: the inherited dictionary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000248 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
249 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000250 *
251 * Create a new XML pattern parser context
252 *
253 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
254 */
255static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000256xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
257 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000258 xmlPatParserContextPtr cur;
259
260 if (pattern == NULL)
261 return(NULL);
262
263 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
264 if (cur == NULL) {
265 ERROR(NULL, NULL, NULL,
266 "xmlNewPatParserContext : malloc failed\n");
267 return(NULL);
268 }
269 memset(cur, 0, sizeof(xmlPatParserContext));
270 cur->dict = dict;
271 cur->cur = pattern;
272 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000273 if (namespaces != NULL) {
274 int i;
275 for (i = 0;namespaces[2 * i] != NULL;i++);
276 cur->nb_namespaces = i;
277 } else {
278 cur->nb_namespaces = 0;
279 }
280 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000281 return(cur);
282}
283
284/**
285 * xmlFreePatParserContext:
286 * @ctxt: an XSLT parser context
287 *
288 * Free up the memory allocated by @ctxt
289 */
290static void
291xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
292 if (ctxt == NULL)
293 return;
294 memset(ctxt, -1, sizeof(xmlPatParserContext));
295 xmlFree(ctxt);
296}
297
298/**
299 * xmlPatternAdd:
300 * @comp: the compiled match expression
301 * @op: an op
302 * @value: the first value
303 * @value2: the second value
304 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000305 * Add a step to an XSLT Compiled Match
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000306 *
307 * Returns -1 in case of failure, 0 otherwise.
308 */
309static int
310xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
311 xmlPatternPtr comp,
312 xmlPatOp op, xmlChar * value, xmlChar * value2)
313{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000314 if (comp->nbStep >= comp->maxStep) {
315 xmlStepOpPtr temp;
316 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
317 sizeof(xmlStepOp));
318 if (temp == NULL) {
319 ERROR(ctxt, NULL, NULL,
320 "xmlPatternAdd: realloc failed\n");
321 return (-1);
322 }
323 comp->steps = temp;
324 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000325 }
326 comp->steps[comp->nbStep].op = op;
327 comp->steps[comp->nbStep].value = value;
328 comp->steps[comp->nbStep].value2 = value2;
329 comp->nbStep++;
330 return (0);
331}
332
333#if 0
334/**
335 * xsltSwapTopPattern:
336 * @comp: the compiled match expression
337 *
338 * reverse the two top steps.
339 */
340static void
341xsltSwapTopPattern(xmlPatternPtr comp) {
342 int i;
343 int j = comp->nbStep - 1;
344
345 if (j > 0) {
346 register const xmlChar *tmp;
347 register xmlPatOp op;
348 i = j - 1;
349 tmp = comp->steps[i].value;
350 comp->steps[i].value = comp->steps[j].value;
351 comp->steps[j].value = tmp;
352 tmp = comp->steps[i].value2;
353 comp->steps[i].value2 = comp->steps[j].value2;
354 comp->steps[j].value2 = tmp;
355 op = comp->steps[i].op;
356 comp->steps[i].op = comp->steps[j].op;
357 comp->steps[j].op = op;
358 }
359}
360#endif
361
362/**
363 * xmlReversePattern:
364 * @comp: the compiled match expression
365 *
366 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000367 *
368 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000369 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000370static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000371xmlReversePattern(xmlPatternPtr comp) {
Daniel Veillard56de87e2005-02-16 00:22:29 +0000372 int i, j;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000373
Daniel Veillard56de87e2005-02-16 00:22:29 +0000374 /*
375 * remove the leading // for //a or .//a
376 */
377 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
378 for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
379 comp->steps[i].value = comp->steps[j].value;
380 comp->steps[i].value2 = comp->steps[j].value2;
381 comp->steps[i].op = comp->steps[j].op;
382 }
383 comp->nbStep--;
384 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000385 if (comp->nbStep >= comp->maxStep) {
386 xmlStepOpPtr temp;
387 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
388 sizeof(xmlStepOp));
389 if (temp == NULL) {
390 ERROR(ctxt, NULL, NULL,
391 "xmlReversePattern: realloc failed\n");
392 return (-1);
393 }
394 comp->steps = temp;
395 comp->maxStep *= 2;
396 }
Daniel Veillard56de87e2005-02-16 00:22:29 +0000397 i = 0;
398 j = comp->nbStep - 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000399 while (j > i) {
400 register const xmlChar *tmp;
401 register xmlPatOp op;
402 tmp = comp->steps[i].value;
403 comp->steps[i].value = comp->steps[j].value;
404 comp->steps[j].value = tmp;
405 tmp = comp->steps[i].value2;
406 comp->steps[i].value2 = comp->steps[j].value2;
407 comp->steps[j].value2 = tmp;
408 op = comp->steps[i].op;
409 comp->steps[i].op = comp->steps[j].op;
410 comp->steps[j].op = op;
411 j--;
412 i++;
413 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000414 comp->steps[comp->nbStep].value = NULL;
415 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000416 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000417 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000418}
419
420/************************************************************************
421 * *
422 * The interpreter for the precompiled patterns *
423 * *
424 ************************************************************************/
425
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000426static int
427xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
428 if ((states->states == NULL) || (states->maxstates <= 0)) {
429 states->maxstates = 4;
430 states->nbstates = 0;
431 states->states = xmlMalloc(4 * sizeof(xmlStepState));
432 }
433 else if (states->maxstates <= states->nbstates) {
434 xmlStepState *tmp;
435
436 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
437 2 * states->maxstates * sizeof(xmlStepState));
438 if (tmp == NULL)
439 return(-1);
440 states->states = tmp;
441 states->maxstates *= 2;
442 }
443 states->states[states->nbstates].step = step;
444 states->states[states->nbstates++].node = node;
445#if 0
446 fprintf(stderr, "Push: %d, %s\n", step, node->name);
447#endif
448 return(0);
449}
450
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000451/**
452 * xmlPatMatch:
453 * @comp: the precompiled pattern
454 * @node: a node
455 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000456 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000457 *
458 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
459 */
460static int
461xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
462 int i;
463 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000464 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000465
466 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000467 i = 0;
468restart:
469 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000470 step = &comp->steps[i];
471 switch (step->op) {
472 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000473 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000474 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000475 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000476 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000477 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000478 if ((node->type == XML_DOCUMENT_NODE) ||
479#ifdef LIBXML_DOCB_ENABLED
480 (node->type == XML_DOCB_DOCUMENT_NODE) ||
481#endif
482 (node->type == XML_HTML_DOCUMENT_NODE))
483 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000484 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000485 case XML_OP_ELEM:
486 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000487 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000488 if (step->value == NULL)
489 continue;
490 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000491 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000492 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000493 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000494
495 /* Namespace test */
496 if (node->ns == NULL) {
497 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000498 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000499 } else if (node->ns->href != NULL) {
500 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000501 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000502 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000503 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000504 }
505 continue;
506 case XML_OP_CHILD: {
507 xmlNodePtr lst;
508
509 if ((node->type != XML_ELEMENT_NODE) &&
510 (node->type != XML_DOCUMENT_NODE) &&
511#ifdef LIBXML_DOCB_ENABLED
512 (node->type != XML_DOCB_DOCUMENT_NODE) &&
513#endif
514 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000515 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000516
517 lst = node->children;
518
519 if (step->value != NULL) {
520 while (lst != NULL) {
521 if ((lst->type == XML_ELEMENT_NODE) &&
522 (step->value[0] == lst->name[0]) &&
523 (xmlStrEqual(step->value, lst->name)))
524 break;
525 lst = lst->next;
526 }
527 if (lst != NULL)
528 continue;
529 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000530 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000531 }
532 case XML_OP_ATTR:
533 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000534 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000535 if (step->value != NULL) {
536 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000537 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000538 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000539 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000540 }
541 /* Namespace test */
542 if (node->ns == NULL) {
543 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000544 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000545 } else if (step->value2 != NULL) {
546 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000547 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000548 }
549 continue;
550 case XML_OP_PARENT:
551 if ((node->type == XML_DOCUMENT_NODE) ||
552 (node->type == XML_HTML_DOCUMENT_NODE) ||
553#ifdef LIBXML_DOCB_ENABLED
554 (node->type == XML_DOCB_DOCUMENT_NODE) ||
555#endif
556 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000557 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000558 node = node->parent;
559 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000560 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000561 if (step->value == NULL)
562 continue;
563 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000564 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000565 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000566 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000567 /* 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 (node->ns->href != NULL) {
572 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000573 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000574 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000575 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000576 }
577 continue;
578 case XML_OP_ANCESTOR:
579 /* TODO: implement coalescing of ANCESTOR/NODE ops */
580 if (step->value == NULL) {
581 i++;
582 step = &comp->steps[i];
583 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000584 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000585 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000586 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000587 if (step->value == NULL)
588 return(-1);
589 }
590 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000591 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000592 if ((node->type == XML_DOCUMENT_NODE) ||
593 (node->type == XML_HTML_DOCUMENT_NODE) ||
594#ifdef LIBXML_DOCB_ENABLED
595 (node->type == XML_DOCB_DOCUMENT_NODE) ||
596#endif
597 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000598 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000599 node = node->parent;
600 while (node != NULL) {
601 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000602 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000603 if ((node->type == XML_ELEMENT_NODE) &&
604 (step->value[0] == node->name[0]) &&
605 (xmlStrEqual(step->value, node->name))) {
606 /* Namespace test */
607 if (node->ns == NULL) {
608 if (step->value2 == NULL)
609 break;
610 } else if (node->ns->href != NULL) {
611 if ((step->value2 != NULL) &&
612 (xmlStrEqual(step->value2, node->ns->href)))
613 break;
614 }
615 }
616 node = node->parent;
617 }
618 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000619 goto rollback;
620 /*
621 * prepare a potential rollback from here
622 * for ancestors of that node.
623 */
624 if (step->op == XML_OP_ANCESTOR)
625 xmlPatPushState(&states, i, node);
626 else
627 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000628 continue;
629 case XML_OP_NS:
630 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000631 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000632 if (node->ns == NULL) {
633 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000634 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000635 } else if (node->ns->href != NULL) {
636 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000637 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000638 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000639 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000640 }
641 break;
642 case XML_OP_ALL:
643 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000644 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000645 break;
646 }
647 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000648found:
649 if (states.states != NULL) {
650 /* Free the rollback states */
651 xmlFree(states.states);
652 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000653 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000654rollback:
655 /* got an error try to rollback */
656 if (states.states == NULL)
657 return(0);
658 if (states.nbstates <= 0) {
659 xmlFree(states.states);
660 return(0);
661 }
662 states.nbstates--;
663 i = states.states[states.nbstates].step;
664 node = states.states[states.nbstates].node;
665#if 0
666 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
667#endif
668 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000669}
670
671/************************************************************************
672 * *
673 * Dedicated parser for templates *
674 * *
675 ************************************************************************/
676
677#define TODO \
678 xmlGenericError(xmlGenericErrorContext, \
679 "Unimplemented block at %s:%d\n", \
680 __FILE__, __LINE__);
681#define CUR (*ctxt->cur)
682#define SKIP(val) ctxt->cur += (val)
683#define NXT(val) ctxt->cur[(val)]
684#define CUR_PTR ctxt->cur
685
686#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000687 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000688
689#define CURRENT (*ctxt->cur)
690#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
691
692
693#define PUSH(op, val, val2) \
694 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
695
696#define XSLT_ERROR(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000697 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000698 ctxt->error = (X); return; }
699
700#define XSLT_ERROR0(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000701 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000702 ctxt->error = (X); return(0); }
703
704#if 0
705/**
706 * xmlPatScanLiteral:
707 * @ctxt: the XPath Parser context
708 *
709 * Parse an XPath Litteral:
710 *
711 * [29] Literal ::= '"' [^"]* '"'
712 * | "'" [^']* "'"
713 *
714 * Returns the Literal parsed or NULL
715 */
716
717static xmlChar *
718xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
719 const xmlChar *q, *cur;
720 xmlChar *ret = NULL;
721 int val, len;
722
723 SKIP_BLANKS;
724 if (CUR == '"') {
725 NEXT;
726 cur = q = CUR_PTR;
727 val = xmlStringCurrentChar(NULL, cur, &len);
728 while ((IS_CHAR(val)) && (val != '"')) {
729 cur += len;
730 val = xmlStringCurrentChar(NULL, cur, &len);
731 }
732 if (!IS_CHAR(val)) {
733 ctxt->error = 1;
734 return(NULL);
735 } else {
736 ret = xmlStrndup(q, cur - q);
737 }
738 cur += len;
739 CUR_PTR = cur;
740 } else if (CUR == '\'') {
741 NEXT;
742 cur = q = CUR_PTR;
743 val = xmlStringCurrentChar(NULL, cur, &len);
744 while ((IS_CHAR(val)) && (val != '\'')) {
745 cur += len;
746 val = xmlStringCurrentChar(NULL, cur, &len);
747 }
748 if (!IS_CHAR(val)) {
749 ctxt->error = 1;
750 return(NULL);
751 } else {
752 ret = xmlStrndup(q, cur - q);
753 }
754 cur += len;
755 CUR_PTR = cur;
756 } else {
757 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
758 ctxt->error = 1;
759 return(NULL);
760 }
761 return(ret);
762}
763#endif
764
765/**
766 * xmlPatScanName:
767 * @ctxt: the XPath Parser context
768 *
769 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
770 * CombiningChar | Extender
771 *
772 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
773 *
774 * [6] Names ::= Name (S Name)*
775 *
776 * Returns the Name parsed or NULL
777 */
778
779static xmlChar *
780xmlPatScanName(xmlPatParserContextPtr ctxt) {
781 const xmlChar *q, *cur;
782 xmlChar *ret = NULL;
783 int val, len;
784
785 SKIP_BLANKS;
786
787 cur = q = CUR_PTR;
788 val = xmlStringCurrentChar(NULL, cur, &len);
789 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
790 return(NULL);
791
792 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
793 (val == '.') || (val == '-') ||
794 (val == '_') ||
795 (IS_COMBINING(val)) ||
796 (IS_EXTENDER(val))) {
797 cur += len;
798 val = xmlStringCurrentChar(NULL, cur, &len);
799 }
800 ret = xmlStrndup(q, cur - q);
801 CUR_PTR = cur;
802 return(ret);
803}
804
805/**
806 * xmlPatScanNCName:
807 * @ctxt: the XPath Parser context
808 *
809 * Parses a non qualified name
810 *
811 * Returns the Name parsed or NULL
812 */
813
814static xmlChar *
815xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
816 const xmlChar *q, *cur;
817 xmlChar *ret = NULL;
818 int val, len;
819
820 SKIP_BLANKS;
821
822 cur = q = CUR_PTR;
823 val = xmlStringCurrentChar(NULL, cur, &len);
824 if (!IS_LETTER(val) && (val != '_'))
825 return(NULL);
826
827 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
828 (val == '.') || (val == '-') ||
829 (val == '_') ||
830 (IS_COMBINING(val)) ||
831 (IS_EXTENDER(val))) {
832 cur += len;
833 val = xmlStringCurrentChar(NULL, cur, &len);
834 }
835 ret = xmlStrndup(q, cur - q);
836 CUR_PTR = cur;
837 return(ret);
838}
839
840#if 0
841/**
842 * xmlPatScanQName:
843 * @ctxt: the XPath Parser context
844 * @prefix: the place to store the prefix
845 *
846 * Parse a qualified name
847 *
848 * Returns the Name parsed or NULL
849 */
850
851static xmlChar *
852xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
853 xmlChar *ret = NULL;
854
855 *prefix = NULL;
856 ret = xmlPatScanNCName(ctxt);
857 if (CUR == ':') {
858 *prefix = ret;
859 NEXT;
860 ret = xmlPatScanNCName(ctxt);
861 }
862 return(ret);
863}
864#endif
865
866/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000867 * xmlCompileAttributeTest:
868 * @ctxt: the compilation context
869 *
870 * Compile an attribute test.
871 */
872static void
873xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
874 xmlChar *token = NULL;
875 xmlChar *name = NULL;
876 xmlChar *URL = NULL;
877
878 name = xmlPatScanNCName(ctxt);
879 if (name == NULL) {
880 if (CUR == '*') {
881 PUSH(XML_OP_ATTR, NULL, NULL);
882 } else {
883 ERROR(NULL, NULL, NULL,
884 "xmlCompileAttributeTest : Name expected\n");
885 ctxt->error = 1;
886 }
887 return;
888 }
889 if (CUR == ':') {
890 int i;
891 xmlChar *prefix = name;
892
893 NEXT;
894 /*
895 * This is a namespace match
896 */
897 token = xmlPatScanName(ctxt);
898 for (i = 0;i < ctxt->nb_namespaces;i++) {
899 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
900 URL = xmlStrdup(ctxt->namespaces[2 * i]);
901 break;
902 }
903 }
904 if (i >= ctxt->nb_namespaces) {
905 ERROR5(NULL, NULL, NULL,
906 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
907 prefix);
908 ctxt->error = 1;
909 goto error;
910 }
911
912 xmlFree(prefix);
913 if (token == NULL) {
914 if (CUR == '*') {
915 NEXT;
916 PUSH(XML_OP_ATTR, NULL, URL);
917 } else {
918 ERROR(NULL, NULL, NULL,
919 "xmlCompileAttributeTest : Name expected\n");
920 ctxt->error = 1;
921 goto error;
922 }
923 } else {
924 PUSH(XML_OP_ATTR, token, URL);
925 }
926 } else {
927 PUSH(XML_OP_ATTR, name, NULL);
928 }
929 return;
930error:
931 if (URL != NULL)
932 xmlFree(URL);
933 if (token != NULL)
934 xmlFree(token);
935}
936
937
938/**
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000939 * xmlCompileStepPattern:
940 * @ctxt: the compilation context
941 *
942 * Compile the Step Pattern and generates a precompiled
943 * form suitable for fast matching.
944 *
945 * [3] Step ::= '.' | NameTest
946 * [4] NameTest ::= QName | '*' | NCName ':' '*'
947 */
948
949static void
950xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
951 xmlChar *token = NULL;
952 xmlChar *name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000953 xmlChar *URL = NULL;
954
955 SKIP_BLANKS;
956 if (CUR == '.') {
957 NEXT;
958 PUSH(XML_OP_ELEM, NULL, NULL);
959 return;
960 }
961 name = xmlPatScanNCName(ctxt);
962 if (name == NULL) {
963 if (CUR == '*') {
964 NEXT;
965 PUSH(XML_OP_ALL, NULL, NULL);
966 return;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000967 } else if (CUR == '@') {
968 NEXT;
969 xmlCompileAttributeTest(ctxt);
970 if (ctxt->error != 0)
971 goto error;
972 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000973 } else {
974 ERROR(NULL, NULL, NULL,
975 "xmlCompileStepPattern : Name expected\n");
976 ctxt->error = 1;
977 return;
978 }
979 }
980 SKIP_BLANKS;
981 if (CUR == ':') {
982 NEXT;
983 if (CUR != ':') {
984 xmlChar *prefix = name;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000985 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000986
987 /*
988 * This is a namespace match
989 */
990 token = xmlPatScanName(ctxt);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000991 for (i = 0;i < ctxt->nb_namespaces;i++) {
992 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Daniel Veillard0996a162005-02-05 14:00:10 +0000993 URL = xmlStrdup(ctxt->namespaces[2 * i]);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000994 break;
995 }
996 }
997 if (i >= ctxt->nb_namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000998 ERROR5(NULL, NULL, NULL,
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000999 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1000 prefix);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001001 ctxt->error = 1;
1002 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001003 }
1004 xmlFree(prefix);
1005 if (token == NULL) {
1006 if (CUR == '*') {
1007 NEXT;
1008 PUSH(XML_OP_NS, URL, NULL);
1009 } else {
1010 ERROR(NULL, NULL, NULL,
1011 "xmlCompileStepPattern : Name expected\n");
1012 ctxt->error = 1;
1013 goto error;
1014 }
1015 } else {
1016 PUSH(XML_OP_ELEM, token, URL);
1017 }
1018 } else {
1019 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001020 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1021 xmlFree(name);
1022 name = xmlPatScanName(ctxt);
1023 if (name == NULL) {
1024 if (CUR == '*') {
1025 NEXT;
1026 PUSH(XML_OP_ALL, NULL, NULL);
1027 return;
1028 } else {
1029 ERROR(NULL, NULL, NULL,
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001030 "xmlCompileStepPattern : QName expected\n");
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001031 ctxt->error = 1;
1032 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001033 }
1034 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001035 if (CUR == ':') {
1036 xmlChar *prefix = name;
1037 int i;
1038
1039 NEXT;
1040 /*
1041 * This is a namespace match
1042 */
1043 token = xmlPatScanName(ctxt);
1044 for (i = 0;i < ctxt->nb_namespaces;i++) {
1045 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1046 URL = xmlStrdup(ctxt->namespaces[2 * i]);
1047 break;
1048 }
1049 }
1050 if (i >= ctxt->nb_namespaces) {
1051 ERROR5(NULL, NULL, NULL,
William M. Brackea152c02005-06-09 18:12:28 +00001052 "xmlCompileStepPattern : no namespace bound "
1053 "to prefix %s\n", prefix);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001054 ctxt->error = 1;
1055 goto error;
1056 }
1057 xmlFree(prefix);
1058 if (token == NULL) {
1059 if (CUR == '*') {
1060 NEXT;
1061 PUSH(XML_OP_NS, URL, NULL);
1062 } else {
1063 ERROR(NULL, NULL, NULL,
1064 "xmlCompileStepPattern : Name expected\n");
1065 ctxt->error = 1;
1066 goto error;
1067 }
1068 } else {
1069 PUSH(XML_OP_CHILD, token, URL);
1070 }
1071 } else
1072 PUSH(XML_OP_CHILD, name, NULL);
1073 return;
1074 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1075 xmlFree(name);
1076 name = NULL;
1077 xmlCompileAttributeTest(ctxt);
1078 if (ctxt->error != 0)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001079 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001080 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001081 } else {
1082 ERROR(NULL, NULL, NULL,
1083 "xmlCompileStepPattern : 'child' or 'attribute' expected\n");
1084 ctxt->error = 1;
1085 goto error;
1086 }
Daniel Veillard0e460da2005-03-30 22:47:10 +00001087 /* NOT REACHED xmlFree(name); */
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001088 }
1089 } else if (CUR == '*') {
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001090 if (name != NULL) {
1091 ctxt->error = 1;
1092 goto error;
1093 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001094 NEXT;
1095 PUSH(XML_OP_ALL, token, NULL);
1096 } else {
1097 if (name == NULL) {
1098 ctxt->error = 1;
1099 goto error;
1100 }
1101 PUSH(XML_OP_ELEM, name, NULL);
1102 }
1103 return;
1104error:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001105 if (URL != NULL)
1106 xmlFree(URL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001107 if (token != NULL)
1108 xmlFree(token);
1109 if (name != NULL)
1110 xmlFree(name);
1111}
1112
1113/**
1114 * xmlCompilePathPattern:
1115 * @ctxt: the compilation context
1116 *
1117 * Compile the Path Pattern and generates a precompiled
1118 * form suitable for fast matching.
1119 *
1120 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1121 */
1122static void
1123xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1124 SKIP_BLANKS;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001125 if (CUR == '/') {
1126 ctxt->comp->flags |= PAT_FROM_ROOT;
William M. Brackea152c02005-06-09 18:12:28 +00001127 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001128 ctxt->comp->flags |= PAT_FROM_CUR;
1129 }
William M. Brackea152c02005-06-09 18:12:28 +00001130
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001131 if ((CUR == '/') && (NXT(1) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001132 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001133 NEXT;
1134 NEXT;
1135 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001136 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001137 NEXT;
1138 NEXT;
1139 NEXT;
1140 }
1141 if (CUR == '@') {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001142 NEXT;
1143 xmlCompileAttributeTest(ctxt);
1144 SKIP_BLANKS;
William M. Brackfbb619f2005-06-06 13:49:18 +00001145 if (CUR != 0) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001146 xmlCompileStepPattern(ctxt);
1147 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001148 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001149 if (CUR == '/') {
1150 PUSH(XML_OP_ROOT, NULL, NULL);
1151 NEXT;
1152 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001153 xmlCompileStepPattern(ctxt);
1154 SKIP_BLANKS;
1155 while (CUR == '/') {
William M. Brackfbb619f2005-06-06 13:49:18 +00001156 if (NXT(1) == '/') {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001157 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1158 NEXT;
1159 NEXT;
1160 SKIP_BLANKS;
1161 xmlCompileStepPattern(ctxt);
1162 } else {
1163 PUSH(XML_OP_PARENT, NULL, NULL);
1164 NEXT;
1165 SKIP_BLANKS;
William M. Brackfbb619f2005-06-06 13:49:18 +00001166 if (CUR != 0) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001167 xmlCompileStepPattern(ctxt);
1168 }
1169 }
1170 }
1171 }
Daniel Veillard56de87e2005-02-16 00:22:29 +00001172 if (CUR != 0) {
1173 ERROR5(NULL, NULL, NULL,
1174 "Failed to compile pattern %s\n", ctxt->base);
1175 ctxt->error = 1;
1176 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001177error:
1178 return;
1179}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001180
1181/************************************************************************
1182 * *
1183 * The streaming code *
1184 * *
1185 ************************************************************************/
1186
1187#ifdef DEBUG_STREAMING
1188static void
1189xmlDebugStreamComp(xmlStreamCompPtr stream) {
1190 int i;
1191
1192 if (stream == NULL) {
1193 printf("Stream: NULL\n");
1194 return;
1195 }
1196 printf("Stream: %d steps\n", stream->nbStep);
1197 for (i = 0;i < stream->nbStep;i++) {
1198 if (stream->steps[i].ns != NULL) {
1199 printf("{%s}", stream->steps[i].ns);
1200 }
1201 if (stream->steps[i].name == NULL) {
1202 printf("* ");
1203 } else {
1204 printf("%s ", stream->steps[i].name);
1205 }
1206 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1207 printf("root ");
1208 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1209 printf("// ");
1210 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1211 printf("final ");
1212 printf("\n");
1213 }
1214}
1215static void
1216xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1217 int i;
1218
1219 if (ctxt == NULL) {
1220 printf("Stream: NULL\n");
1221 return;
1222 }
1223 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1224 if (match)
1225 printf("matches\n");
1226 else
1227 printf("\n");
1228 for (i = 0;i < ctxt->nbState;i++) {
1229 if (ctxt->states[2 * i] < 0)
1230 printf(" %d: free\n", i);
1231 else {
1232 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1233 ctxt->states[(2 * i) + 1]);
1234 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1235 XML_STREAM_STEP_DESC)
1236 printf(" //\n");
1237 else
1238 printf("\n");
1239 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001240 }
1241}
1242#endif
1243/**
1244 * xmlNewStreamComp:
1245 * @size: the number of expected steps
1246 *
1247 * build a new compiled pattern for streaming
1248 *
1249 * Returns the new structure or NULL in case of error.
1250 */
1251static xmlStreamCompPtr
1252xmlNewStreamComp(int size) {
1253 xmlStreamCompPtr cur;
1254
1255 if (size < 4)
1256 size = 4;
1257
1258 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1259 if (cur == NULL) {
1260 ERROR(NULL, NULL, NULL,
1261 "xmlNewStreamComp: malloc failed\n");
1262 return(NULL);
1263 }
1264 memset(cur, 0, sizeof(xmlStreamComp));
1265 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1266 if (cur->steps == NULL) {
1267 xmlFree(cur);
1268 ERROR(NULL, NULL, NULL,
1269 "xmlNewStreamComp: malloc failed\n");
1270 return(NULL);
1271 }
1272 cur->nbStep = 0;
1273 cur->maxStep = size;
1274 return(cur);
1275}
1276
1277/**
1278 * xmlFreeStreamComp:
1279 * @comp: the compiled pattern for streaming
1280 *
1281 * Free the compiled pattern for streaming
1282 */
1283static void
1284xmlFreeStreamComp(xmlStreamCompPtr comp) {
1285 if (comp != NULL) {
1286 if (comp->steps != NULL)
1287 xmlFree(comp->steps);
1288 if (comp->dict != NULL)
1289 xmlDictFree(comp->dict);
1290 xmlFree(comp);
1291 }
1292}
1293
1294/**
1295 * xmlStreamCompAddStep:
1296 * @comp: the compiled pattern for streaming
1297 * @name: the first string, the name, or NULL for *
1298 * @ns: the second step, the namespace name
1299 * @flags: the flags for that step
1300 *
1301 * Add a new step to the compiled pattern
1302 *
1303 * Returns -1 in case of error or the step index if successful
1304 */
1305static int
1306xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1307 const xmlChar *ns, int flags) {
1308 xmlStreamStepPtr cur;
1309
1310 if (comp->nbStep >= comp->maxStep) {
1311 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1312 comp->maxStep * 2 * sizeof(xmlStreamStep));
1313 if (cur == NULL) {
1314 ERROR(NULL, NULL, NULL,
1315 "xmlNewStreamComp: malloc failed\n");
1316 return(-1);
1317 }
1318 comp->steps = cur;
1319 comp->maxStep *= 2;
1320 }
1321 cur = &comp->steps[comp->nbStep++];
1322 cur->flags = flags;
1323 cur->name = name;
1324 cur->ns = ns;
1325 return(comp->nbStep - 1);
1326}
1327
1328/**
1329 * xmlStreamCompile:
1330 * @comp: the precompiled pattern
1331 *
1332 * Tries to stream compile a pattern
1333 *
1334 * Returns -1 in case of failure and 0 in case of success.
1335 */
1336static int
1337xmlStreamCompile(xmlPatternPtr comp) {
1338 xmlStreamCompPtr stream;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001339 int i, s = 0, root = 0, flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001340
1341 if ((comp == NULL) || (comp->steps == NULL))
1342 return(-1);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001343 /*
1344 * special case for .
1345 */
1346 if ((comp->nbStep == 1) &&
1347 (comp->steps[0].op == XML_OP_ELEM) &&
1348 (comp->steps[0].value == NULL) &&
1349 (comp->steps[0].value2 == NULL)) {
1350 stream = xmlNewStreamComp(0);
1351 if (stream == NULL)
1352 return(-1);
1353 comp->stream = stream;
1354 return(0);
1355 }
1356
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001357 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1358 if (stream == NULL)
1359 return(-1);
1360 if (comp->dict != NULL) {
1361 stream->dict = comp->dict;
1362 xmlDictReference(stream->dict);
1363 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001364
1365 /*
1366 * Skip leading ./ on relative paths
1367 */
1368 i = 0;
1369 while ((comp->flags & PAT_FROM_CUR) && (comp->nbStep > i + 2) &&
1370 (comp->steps[i].op == XML_OP_ELEM) &&
1371 (comp->steps[i].value == NULL) &&
1372 (comp->steps[i].value2 == NULL) &&
1373 (comp->steps[i + 1].op == XML_OP_PARENT)) {
1374 i += 2;
1375 }
1376 for (;i < comp->nbStep;i++) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001377 switch (comp->steps[i].op) {
1378 case XML_OP_END:
1379 break;
1380 case XML_OP_ROOT:
1381 if (i != 0)
1382 goto error;
1383 root = 1;
1384 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001385 case XML_OP_NS:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001386 s = xmlStreamCompAddStep(stream, NULL,
1387 comp->steps[i].value, flags);
1388 flags = 0;
1389 if (s < 0)
1390 goto error;
1391 break;
1392 case XML_OP_ATTR:
1393 flags |= XML_STREAM_STEP_ATTR;
1394 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1395 comp->steps[i].value2, flags);
1396 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001397 if (s < 0)
1398 goto error;
1399 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001400 case XML_OP_ELEM:
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001401 if ((comp->steps[i].value == NULL) &&
1402 (comp->steps[i].value2 == NULL) &&
1403 (comp->nbStep > i + 2) &&
1404 (comp->steps[i + 1].op == XML_OP_PARENT)) {
1405 i++;
1406 continue;
1407 }
1408 case XML_OP_CHILD:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001409 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1410 comp->steps[i].value2, flags);
1411 flags = 0;
1412 if (s < 0)
1413 goto error;
1414 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001415 case XML_OP_ALL:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001416 s = xmlStreamCompAddStep(stream, NULL, NULL, flags);
1417 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001418 if (s < 0)
1419 goto error;
1420 break;
1421 case XML_OP_PARENT:
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001422 if ((comp->nbStep > i + 1) &&
1423 (comp->steps[i + 1].op == XML_OP_ELEM) &&
1424 (comp->steps[i + 1].value == NULL) &&
1425 (comp->steps[i + 1].value2 == NULL)) {
1426 i++;
1427 continue;
1428 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001429 break;
1430 case XML_OP_ANCESTOR:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001431 flags |= XML_STREAM_STEP_DESC;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001432 break;
1433 }
1434 }
1435 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1436 if (root)
1437 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1438#ifdef DEBUG_STREAMING
1439 xmlDebugStreamComp(stream);
1440#endif
1441 comp->stream = stream;
1442 return(0);
1443error:
1444 xmlFreeStreamComp(stream);
1445 return(0);
1446}
1447
1448/**
1449 * xmlNewStreamCtxt:
1450 * @size: the number of expected states
1451 *
1452 * build a new stream context
1453 *
1454 * Returns the new structure or NULL in case of error.
1455 */
1456static xmlStreamCtxtPtr
1457xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1458 xmlStreamCtxtPtr cur;
1459
1460 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1461 if (cur == NULL) {
1462 ERROR(NULL, NULL, NULL,
1463 "xmlNewStreamCtxt: malloc failed\n");
1464 return(NULL);
1465 }
1466 memset(cur, 0, sizeof(xmlStreamCtxt));
1467 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1468 if (cur->states == NULL) {
1469 xmlFree(cur);
1470 ERROR(NULL, NULL, NULL,
1471 "xmlNewStreamCtxt: malloc failed\n");
1472 return(NULL);
1473 }
1474 cur->nbState = 0;
1475 cur->maxState = 4;
1476 cur->level = 0;
1477 cur->comp = stream;
1478 return(cur);
1479}
1480
1481/**
1482 * xmlFreeStreamCtxt:
1483 * @stream: the stream context
1484 *
1485 * Free the stream context
1486 */
1487void
1488xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001489 xmlStreamCtxtPtr next;
1490
1491 while (stream != NULL) {
1492 next = stream->next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001493 if (stream->states != NULL)
1494 xmlFree(stream->states);
1495 xmlFree(stream);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001496 stream = next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001497 }
1498}
1499
1500/**
1501 * xmlStreamCtxtAddState:
1502 * @comp: the stream context
1503 * @idx: the step index for that streaming state
1504 *
1505 * Add a new state to the stream context
1506 *
1507 * Returns -1 in case of error or the state index if successful
1508 */
1509static int
1510xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1511 int i;
1512 for (i = 0;i < comp->nbState;i++) {
1513 if (comp->states[2 * i] < 0) {
1514 comp->states[2 * i] = idx;
1515 comp->states[2 * i + 1] = level;
1516 return(i);
1517 }
1518 }
1519 if (comp->nbState >= comp->maxState) {
1520 int *cur;
1521
1522 cur = (int *) xmlRealloc(comp->states,
1523 comp->maxState * 4 * sizeof(int));
1524 if (cur == NULL) {
1525 ERROR(NULL, NULL, NULL,
1526 "xmlNewStreamCtxt: malloc failed\n");
1527 return(-1);
1528 }
1529 comp->states = cur;
1530 comp->maxState *= 2;
1531 }
1532 comp->states[2 * comp->nbState] = idx;
1533 comp->states[2 * comp->nbState++ + 1] = level;
1534 return(comp->nbState - 1);
1535}
1536
1537/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001538 * xmlStreamPushInternal:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001539 * @stream: the stream context
1540 * @name: the current name
1541 * @ns: the namespace name
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001542 * @nodeType: the type of the node
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001543 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001544 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1545 * indicated a dictionary, then strings for name and ns will be expected
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001546 * to come from the dictionary.
1547 * Both @name and @ns being NULL means the / i.e. the root of the document.
1548 * This can also act as a reset.
1549 *
1550 * Returns: -1 in case of error, 1 if the current state in the stream is a
1551 * match and 0 otherwise.
1552 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001553static int
1554xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1555 const xmlChar *name, const xmlChar *ns,
1556 xmlElementType nodeType) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001557 int ret = 0, err = 0, tmp, i, m, match, step, desc, final;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001558 xmlStreamCompPtr comp;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001559#ifdef DEBUG_STREAMING
1560 xmlStreamCtxtPtr orig = stream;
1561#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001562
1563 if ((stream == NULL) || (stream->nbState < 0))
1564 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001565
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001566 while (stream != NULL) {
1567 comp = stream->comp;
1568 if ((name == NULL) && (ns == NULL)) {
1569 stream->nbState = 0;
1570 stream->level = 0;
1571 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1572 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1573 if (tmp < 0)
1574 err++;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001575 if (comp->nbStep == 0)
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001576 ret = 1;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001577 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001578 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001579 continue; /* while */
1580 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001581
1582 /*
1583 * Fast check for ".".
1584 */
1585 if (comp->nbStep == 0) {
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001586 /*
William M. Brackea152c02005-06-09 18:12:28 +00001587 * For non-pattern like evaluation like XML Schema IDCs
1588 * or traditional XPath expressions, this will match if
1589 * we are at the first level only, otherwise on every level.
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001590 */
1591 if ((nodeType == XML_ELEMENT_NODE) &&
1592 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1593 (stream->level == 0))) {
1594 ret = 1;
1595 }
1596 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001597 goto stream_next;
1598 }
William M. Brackea152c02005-06-09 18:12:28 +00001599 if ((stream->flags & XML_PATTERN_NOTPATTERN) != 0) {
1600 tmp = stream->level;
1601 for (i = 0; i < comp->nbStep; i++) {
1602 if (comp->steps[i].flags & XML_STREAM_STEP_DESC) {
1603 tmp = -2;
1604 break;
1605 }
William M. Brackfbb619f2005-06-06 13:49:18 +00001606 }
William M. Brackea152c02005-06-09 18:12:28 +00001607 if (comp->nbStep <= tmp) {
1608 stream->level++;
1609 goto stream_next;
1610 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001611
William M. Brackea152c02005-06-09 18:12:28 +00001612 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001613 /*
1614 * Check evolution of existing states
1615 */
1616 m = stream->nbState;
1617 for (i = 0;i < m;i++) {
1618 match = 0;
1619 step = stream->states[2 * i];
1620 /* dead states */
1621 if (step < 0) continue;
1622 /* skip new states just added */
1623 if (stream->states[(2 * i) + 1] > stream->level)
1624 continue;
1625 /* skip continuations */
1626 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1627 if ((stream->states[(2 * i) + 1] < stream->level) && (!desc))
1628 continue;
1629
1630 /* discard old states */
1631 /* something needed about old level discarded */
1632
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001633 /*
1634 * Check for correct node-type.
1635 */
1636 if ((comp->steps[step].flags & XML_STREAM_STEP_ATTR) &&
1637 (nodeType != XML_ATTRIBUTE_NODE))
1638 continue;
1639
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001640 if (comp->dict) {
1641 if (comp->steps[step].name == NULL) {
1642 if (comp->steps[step].ns == NULL)
1643 match = 1;
1644 else
1645 match = (comp->steps[step].ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001646 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001647 match = ((comp->steps[step].name == name) &&
1648 (comp->steps[step].ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001649 }
1650 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001651 if (comp->steps[step].name == NULL) {
1652 if (comp->steps[step].ns == NULL)
1653 match = 1;
1654 else
1655 match = xmlStrEqual(comp->steps[step].ns, ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001656 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001657 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
1658 (xmlStrEqual(comp->steps[step].ns, ns)));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001659 }
1660 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001661 if (match) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001662 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
1663 if (desc) {
1664 if (final) {
1665 ret = 1;
1666 } else {
1667 /* descending match create a new state */
1668 xmlStreamCtxtAddState(stream, step + 1,
1669 stream->level + 1);
1670 }
1671 } else {
1672 if (final) {
1673 ret = 1;
1674 } else {
1675 xmlStreamCtxtAddState(stream, step + 1,
1676 stream->level + 1);
1677 }
1678 }
1679 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001680 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001681
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001682 /*
1683 * Check creating a new state.
1684 */
1685 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001686
1687 /*
1688 * Check the start only if this is a "desc" evaluation
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001689 * or if we are at the first level of evaluation.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001690 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001691 desc = comp->steps[0].flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001692 if ( ((comp->steps[0].flags & XML_STREAM_STEP_ROOT) == 0) &&
William M. Brackea152c02005-06-09 18:12:28 +00001693 ( ((stream->flags & XML_PATTERN_XSD) == 0) ||
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001694 ( (desc || (stream->level == 1)) )
1695 )
1696 ) {
1697
1698/*
1699#ifdef SUPPORT_IDC
1700
1701
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001702 if ((desc || (stream->level == 1)) &&
1703 (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT))) {
1704
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001705 *
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001706 * Workaround for missing "self::node()" on "@foo".
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001707 *
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001708 if (comp->steps[0].flags & XML_STREAM_STEP_ATTR) {
1709 xmlStreamCtxtAddState(stream, 0, stream->level);
1710 goto stream_next;
1711 }
1712#else
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001713
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001714 if (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT)) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001715#endif
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001716 */
William M. Brackea152c02005-06-09 18:12:28 +00001717 if (comp->steps[0].name == NULL) {
1718 if (comp->steps[0].ns == NULL)
1719 match = 1;
1720 else {
1721 if (comp->dict)
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001722 match = (comp->steps[0].ns == ns);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001723 else
1724 match = xmlStrEqual(comp->steps[0].ns, ns);
William M. Brackea152c02005-06-09 18:12:28 +00001725 }
1726 } else {
1727 if ((stream->flags & XML_PATTERN_XSD) && (!desc)) {
1728 /*
1729 * Workaround for missing "self::node() on "foo".
1730 */
1731 xmlStreamCtxtAddState(stream, 0, stream->level);
1732 goto stream_next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001733 } else {
William M. Brackea152c02005-06-09 18:12:28 +00001734 if (comp->dict) {
1735 match = ((comp->steps[0].name == name) &&
1736 (comp->steps[0].ns == ns));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001737 } else {
1738 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1739 (xmlStrEqual(comp->steps[0].ns, ns)));
1740 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001741 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001742 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001743 if (match) {
1744 if (comp->steps[0].flags & XML_STREAM_STEP_FINAL)
1745 ret = 1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001746 else
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001747 xmlStreamCtxtAddState(stream, 1, stream->level);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001748 }
1749 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001750stream_next:
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001751 stream = stream->next;
1752 } /* while stream != NULL */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001753
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001754 if (err > 0)
1755 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001756#ifdef DEBUG_STREAMING
1757 xmlDebugStreamCtxt(orig, ret);
1758#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001759 return(ret);
1760}
1761
1762/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001763 * xmlStreamPush:
1764 * @stream: the stream context
1765 * @name: the current name
1766 * @ns: the namespace name
1767 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001768 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1769 * indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001770 * to come from the dictionary.
1771 * Both @name and @ns being NULL means the / i.e. the root of the document.
1772 * This can also act as a reset.
1773 *
1774 * Returns: -1 in case of error, 1 if the current state in the stream is a
1775 * match and 0 otherwise.
1776 */
1777int
1778xmlStreamPush(xmlStreamCtxtPtr stream,
1779 const xmlChar *name, const xmlChar *ns) {
1780 return (xmlStreamPushInternal(stream, name, ns, XML_ELEMENT_NODE));
1781}
1782
1783/**
1784* xmlStreamPushAttr:
1785* @stream: the stream context
1786* @name: the current name
1787* @ns: the namespace name
1788*
William M. Brackfbb619f2005-06-06 13:49:18 +00001789* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
1790* indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001791* to come from the dictionary.
1792* Both @name and @ns being NULL means the / i.e. the root of the document.
1793* This can also act as a reset.
1794*
1795* Returns: -1 in case of error, 1 if the current state in the stream is a
1796* match and 0 otherwise.
1797*/
1798int
1799xmlStreamPushAttr(xmlStreamCtxtPtr stream,
1800 const xmlChar *name, const xmlChar *ns) {
1801 return (xmlStreamPushInternal(stream, name, ns, XML_ATTRIBUTE_NODE));
1802}
1803
1804/**
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001805 * xmlStreamPop:
1806 * @stream: the stream context
1807 *
1808 * push one level from the stream.
1809 *
1810 * Returns: -1 in case of error, 0 otherwise.
1811 */
1812int
1813xmlStreamPop(xmlStreamCtxtPtr stream) {
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001814 int i, m;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001815 int ret;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001816
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001817 if (stream == NULL)
1818 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001819 ret = 0;
1820 while (stream != NULL) {
1821 stream->level--;
1822 if (stream->level < 0)
1823 ret = -1;
1824
1825 /*
1826 * Check evolution of existing states
1827 */
1828 m = stream->nbState;
1829 for (i = 0;i < m;i++) {
1830 if (stream->states[(2 * i)] < 0) break;
1831 /* discard obsoleted states */
1832 if (stream->states[(2 * i) + 1] > stream->level)
1833 stream->states[(2 * i)] = -1;
1834 }
1835 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001836 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001837 return(0);
1838}
1839
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001840/************************************************************************
1841 * *
1842 * The public interfaces *
1843 * *
1844 ************************************************************************/
1845
1846/**
1847 * xmlPatterncompile:
1848 * @pattern: the pattern to compile
William M. Brackfbb619f2005-06-06 13:49:18 +00001849 * @dict: an optional dictionary for interned strings
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001850 * @flags: compilation flags, undefined yet
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001851 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001852 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001853 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001854 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001855 * Returns the compiled form of the pattern or NULL in case of error
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001856 */
1857xmlPatternPtr
Daniel Veillard427174f2003-12-10 10:42:59 +00001858xmlPatterncompile(const xmlChar *pattern, xmlDict *dict,
William M. Brackea152c02005-06-09 18:12:28 +00001859 xmlPatternFlags flags,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001860 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001861 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001862 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001863 const xmlChar *or, *start;
1864 xmlChar *tmp = NULL;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001865 int type = 0;
1866 int streamable = 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001867
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001868 if (pattern == NULL)
1869 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001870
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001871 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001872 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001873 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001874 tmp = NULL;
1875 while ((*or != 0) && (*or != '|')) or++;
1876 if (*or == 0)
1877 ctxt = xmlNewPatParserContext(start, dict, namespaces);
1878 else {
1879 tmp = xmlStrndup(start, or - start);
1880 if (tmp != NULL) {
1881 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
1882 }
1883 or++;
1884 }
1885 if (ctxt == NULL) goto error;
1886 cur = xmlNewPattern();
1887 if (cur == NULL) goto error;
1888 if (ret == NULL)
1889 ret = cur;
1890 else {
1891 cur->next = ret->next;
1892 ret->next = cur;
1893 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001894 cur->flags = flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001895 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001896
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001897 xmlCompilePathPattern(ctxt);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001898 if (ctxt->error != 0)
1899 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001900 xmlFreePatParserContext(ctxt);
William M. Brackfbb619f2005-06-06 13:49:18 +00001901 ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001902
1903
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001904 if (streamable) {
1905 if (type == 0) {
1906 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
1907 } else if (type == PAT_FROM_ROOT) {
1908 if (cur->flags & PAT_FROM_CUR)
1909 streamable = 0;
1910 } else if (type == PAT_FROM_CUR) {
1911 if (cur->flags & PAT_FROM_ROOT)
1912 streamable = 0;
1913 }
1914 }
1915 if (streamable)
1916 xmlStreamCompile(cur);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001917 if (xmlReversePattern(cur) < 0)
1918 goto error;
William M. Brackea152c02005-06-09 18:12:28 +00001919 if (tmp != NULL) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001920 xmlFree(tmp);
William M. Brackea152c02005-06-09 18:12:28 +00001921 tmp = NULL;
1922 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001923 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001924 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001925 if (streamable == 0) {
1926 cur = ret;
1927 while (cur != NULL) {
1928 if (cur->stream != NULL) {
1929 xmlFreeStreamComp(cur->stream);
1930 cur->stream = NULL;
1931 }
1932 cur = cur->next;
1933 }
1934 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001935
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001936 return(ret);
1937error:
1938 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
1939 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001940 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001941 return(NULL);
1942}
1943
1944/**
1945 * xmlPatternMatch:
1946 * @comp: the precompiled pattern
1947 * @node: a node
1948 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001949 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001950 *
1951 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1952 */
1953int
1954xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
1955{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001956 int ret = 0;
1957
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001958 if ((comp == NULL) || (node == NULL))
1959 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001960
1961 while (comp != NULL) {
1962 ret = xmlPatMatch(comp, node);
1963 if (ret != 0)
1964 return(ret);
1965 comp = comp->next;
1966 }
1967 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001968}
1969
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001970/**
1971 * xmlPatternGetStreamCtxt:
1972 * @comp: the precompiled pattern
1973 *
1974 * Get a streaming context for that pattern
1975 * Use xmlFreeStreamCtxt to free the context.
1976 *
1977 * Returns a pointer to the context or NULL in case of failure
1978 */
1979xmlStreamCtxtPtr
1980xmlPatternGetStreamCtxt(xmlPatternPtr comp)
1981{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001982 xmlStreamCtxtPtr ret = NULL, cur;
1983
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001984 if ((comp == NULL) || (comp->stream == NULL))
1985 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001986
1987 while (comp != NULL) {
1988 if (comp->stream == NULL)
1989 goto failed;
1990 cur = xmlNewStreamCtxt(comp->stream);
1991 if (cur == NULL)
1992 goto failed;
1993 if (ret == NULL)
1994 ret = cur;
1995 else {
1996 cur->next = ret->next;
1997 ret->next = cur;
1998 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001999 cur->flags = comp->flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002000 comp = comp->next;
2001 }
2002 return(ret);
2003failed:
2004 xmlFreeStreamCtxt(ret);
2005 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002006}
2007
Daniel Veillard56de87e2005-02-16 00:22:29 +00002008/**
2009 * xmlPatternStreamable:
2010 * @comp: the precompiled pattern
2011 *
2012 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2013 * should work.
2014 *
2015 * Returns 1 if streamable, 0 if not and -1 in case of error.
2016 */
2017int
2018xmlPatternStreamable(xmlPatternPtr comp) {
2019 if (comp == NULL)
2020 return(-1);
2021 while (comp != NULL) {
2022 if (comp->stream == NULL)
2023 return(0);
2024 comp = comp->next;
2025 }
2026 return(1);
2027}
2028
2029/**
2030 * xmlPatternMaxDepth:
2031 * @comp: the precompiled pattern
2032 *
2033 * Check the maximum depth reachable by a pattern
2034 *
2035 * Returns -2 if no limit (using //), otherwise the depth,
2036 * and -1 in case of error
2037 */
2038int
2039xmlPatternMaxDepth(xmlPatternPtr comp) {
2040 int ret = 0, i;
2041 if (comp == NULL)
2042 return(-1);
2043 while (comp != NULL) {
2044 if (comp->stream == NULL)
2045 return(-1);
2046 for (i = 0;i < comp->stream->nbStep;i++)
2047 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2048 return(-2);
2049 if (comp->stream->nbStep > ret)
2050 ret = comp->stream->nbStep;
2051 comp = comp->next;
2052 }
2053 return(ret);
2054
2055}
2056
2057/**
2058 * xmlPatternFromRoot:
2059 * @comp: the precompiled pattern
2060 *
2061 * Check if the pattern must be looked at from the root.
2062 *
2063 * Returns 1 if true, 0 if false and -1 in case of error
2064 */
2065int
2066xmlPatternFromRoot(xmlPatternPtr comp) {
2067 if (comp == NULL)
2068 return(-1);
2069 while (comp != NULL) {
2070 if (comp->stream == NULL)
2071 return(-1);
2072 if (comp->flags & PAT_FROM_ROOT)
2073 return(1);
2074 comp = comp->next;
2075 }
2076 return(0);
2077
2078}
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002079
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002080#define bottom_pattern
2081#include "elfgcchack.h"
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002082#endif /* LIBXML_PATTERN_ENABLED */