blob: fdd9f162eb8a05645635195425af58e929989270 [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
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000051#define XML_PATTERN_NOTPATTERN 1
52
Daniel Veillard2fc6df92005-01-30 18:42:55 +000053typedef struct _xmlStreamStep xmlStreamStep;
54typedef xmlStreamStep *xmlStreamStepPtr;
55struct _xmlStreamStep {
56 int flags; /* properties of that step */
57 const xmlChar *name; /* first string value if NULL accept all */
58 const xmlChar *ns; /* second string value */
59};
60
61typedef struct _xmlStreamComp xmlStreamComp;
62typedef xmlStreamComp *xmlStreamCompPtr;
63struct _xmlStreamComp {
William M. Brackfbb619f2005-06-06 13:49:18 +000064 xmlDict *dict; /* the dictionary if any */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000065 int nbStep; /* number of steps in the automata */
66 int maxStep; /* allocated number of steps */
67 xmlStreamStepPtr steps; /* the array of steps */
68};
69
70struct _xmlStreamCtxt {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +000071 struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000072 xmlStreamCompPtr comp; /* the compiled stream */
William M. Brackfbb619f2005-06-06 13:49:18 +000073 int nbState; /* number of states in the automata */
74 int maxState; /* allocated number of states */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000075 int level; /* how deep are we ? */
76 int *states; /* the array of step indexes */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000077 int flags; /* validation options */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000078};
79
80static void xmlFreeStreamComp(xmlStreamCompPtr comp);
81
Daniel Veillardb3de70c2003-12-02 22:32:15 +000082/*
83 * Types are private:
84 */
85
86typedef enum {
87 XML_OP_END=0,
88 XML_OP_ROOT,
89 XML_OP_ELEM,
90 XML_OP_CHILD,
91 XML_OP_ATTR,
92 XML_OP_PARENT,
93 XML_OP_ANCESTOR,
94 XML_OP_NS,
95 XML_OP_ALL
96} xmlPatOp;
97
98
Daniel Veillardd4301ab2005-02-03 22:24:10 +000099typedef struct _xmlStepState xmlStepState;
100typedef xmlStepState *xmlStepStatePtr;
101struct _xmlStepState {
102 int step;
103 xmlNodePtr node;
104};
105
106typedef struct _xmlStepStates xmlStepStates;
107typedef xmlStepStates *xmlStepStatesPtr;
108struct _xmlStepStates {
109 int nbstates;
110 int maxstates;
111 xmlStepStatePtr states;
112};
113
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000114typedef struct _xmlStepOp xmlStepOp;
115typedef xmlStepOp *xmlStepOpPtr;
116struct _xmlStepOp {
117 xmlPatOp op;
118 const xmlChar *value;
119 const xmlChar *value2;
120};
121
Daniel Veillard56de87e2005-02-16 00:22:29 +0000122#define PAT_FROM_ROOT 1
123#define PAT_FROM_CUR 2
124
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000125struct _xmlPattern {
126 void *data; /* the associated template */
William M. Brackfbb619f2005-06-06 13:49:18 +0000127 xmlDictPtr dict; /* the optional dictionary */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000128 struct _xmlPattern *next; /* next pattern if | is used */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000129 const xmlChar *pattern; /* the pattern */
130
Daniel Veillard56de87e2005-02-16 00:22:29 +0000131 int flags; /* flags */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000132 int nbStep;
133 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000134 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000135 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000136};
137
138typedef struct _xmlPatParserContext xmlPatParserContext;
139typedef xmlPatParserContext *xmlPatParserContextPtr;
140struct _xmlPatParserContext {
141 const xmlChar *cur; /* the current char being parsed */
142 const xmlChar *base; /* the full expression */
143 int error; /* error code */
William M. Brackfbb619f2005-06-06 13:49:18 +0000144 xmlDictPtr dict; /* the dictionary if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000145 xmlPatternPtr comp; /* the result */
146 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000147 const xmlChar **namespaces; /* the namespaces definitions */
148 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000149};
150
151/************************************************************************
152 * *
153 * Type functions *
154 * *
155 ************************************************************************/
156
157/**
158 * xmlNewPattern:
159 *
160 * Create a new XSLT Pattern
161 *
162 * Returns the newly allocated xmlPatternPtr or NULL in case of error
163 */
164static xmlPatternPtr
165xmlNewPattern(void) {
166 xmlPatternPtr cur;
167
168 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
169 if (cur == NULL) {
170 ERROR(NULL, NULL, NULL,
171 "xmlNewPattern : malloc failed\n");
172 return(NULL);
173 }
174 memset(cur, 0, sizeof(xmlPattern));
175 cur->maxStep = 10;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000176 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
177 if (cur->steps == NULL) {
178 xmlFree(cur);
179 ERROR(NULL, NULL, NULL,
180 "xmlNewPattern : malloc failed\n");
181 return(NULL);
182 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000183 return(cur);
184}
185
186/**
187 * xmlFreePattern:
188 * @comp: an XSLT comp
189 *
190 * Free up the memory allocated by @comp
191 */
192void
193xmlFreePattern(xmlPatternPtr comp) {
194 xmlStepOpPtr op;
195 int i;
196
197 if (comp == NULL)
198 return;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000199 if (comp->next != NULL)
200 xmlFreePattern(comp->next);
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000201 if (comp->stream != NULL)
202 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000203 if (comp->pattern != NULL)
204 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000205 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000206 if (comp->dict == NULL) {
207 for (i = 0;i < comp->nbStep;i++) {
208 op = &comp->steps[i];
209 if (op->value != NULL)
210 xmlFree((xmlChar *) op->value);
211 if (op->value2 != NULL)
212 xmlFree((xmlChar *) op->value2);
213 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000214 }
215 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000216 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000217 if (comp->dict != NULL)
218 xmlDictFree(comp->dict);
219
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000220 memset(comp, -1, sizeof(xmlPattern));
221 xmlFree(comp);
222}
223
224/**
225 * xmlFreePatternList:
226 * @comp: an XSLT comp list
227 *
228 * Free up the memory allocated by all the elements of @comp
229 */
230void
231xmlFreePatternList(xmlPatternPtr comp) {
232 xmlPatternPtr cur;
233
234 while (comp != NULL) {
235 cur = comp;
236 comp = comp->next;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +0000237 cur->next = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000238 xmlFreePattern(cur);
239 }
240}
241
242/**
243 * xmlNewPatParserContext:
244 * @pattern: the pattern context
William M. Brackfbb619f2005-06-06 13:49:18 +0000245 * @dict: the inherited dictionary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000246 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
247 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000248 *
249 * Create a new XML pattern parser context
250 *
251 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
252 */
253static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000254xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
255 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000256 xmlPatParserContextPtr cur;
257
258 if (pattern == NULL)
259 return(NULL);
260
261 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
262 if (cur == NULL) {
263 ERROR(NULL, NULL, NULL,
264 "xmlNewPatParserContext : malloc failed\n");
265 return(NULL);
266 }
267 memset(cur, 0, sizeof(xmlPatParserContext));
268 cur->dict = dict;
269 cur->cur = pattern;
270 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000271 if (namespaces != NULL) {
272 int i;
273 for (i = 0;namespaces[2 * i] != NULL;i++);
274 cur->nb_namespaces = i;
275 } else {
276 cur->nb_namespaces = 0;
277 }
278 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000279 return(cur);
280}
281
282/**
283 * xmlFreePatParserContext:
284 * @ctxt: an XSLT parser context
285 *
286 * Free up the memory allocated by @ctxt
287 */
288static void
289xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
290 if (ctxt == NULL)
291 return;
292 memset(ctxt, -1, sizeof(xmlPatParserContext));
293 xmlFree(ctxt);
294}
295
296/**
297 * xmlPatternAdd:
298 * @comp: the compiled match expression
299 * @op: an op
300 * @value: the first value
301 * @value2: the second value
302 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000303 * Add a step to an XSLT Compiled Match
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000304 *
305 * Returns -1 in case of failure, 0 otherwise.
306 */
307static int
308xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
309 xmlPatternPtr comp,
310 xmlPatOp op, xmlChar * value, xmlChar * value2)
311{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000312 if (comp->nbStep >= comp->maxStep) {
313 xmlStepOpPtr temp;
314 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
315 sizeof(xmlStepOp));
316 if (temp == NULL) {
317 ERROR(ctxt, NULL, NULL,
318 "xmlPatternAdd: realloc failed\n");
319 return (-1);
320 }
321 comp->steps = temp;
322 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000323 }
324 comp->steps[comp->nbStep].op = op;
325 comp->steps[comp->nbStep].value = value;
326 comp->steps[comp->nbStep].value2 = value2;
327 comp->nbStep++;
328 return (0);
329}
330
331#if 0
332/**
333 * xsltSwapTopPattern:
334 * @comp: the compiled match expression
335 *
336 * reverse the two top steps.
337 */
338static void
339xsltSwapTopPattern(xmlPatternPtr comp) {
340 int i;
341 int j = comp->nbStep - 1;
342
343 if (j > 0) {
344 register const xmlChar *tmp;
345 register xmlPatOp op;
346 i = j - 1;
347 tmp = comp->steps[i].value;
348 comp->steps[i].value = comp->steps[j].value;
349 comp->steps[j].value = tmp;
350 tmp = comp->steps[i].value2;
351 comp->steps[i].value2 = comp->steps[j].value2;
352 comp->steps[j].value2 = tmp;
353 op = comp->steps[i].op;
354 comp->steps[i].op = comp->steps[j].op;
355 comp->steps[j].op = op;
356 }
357}
358#endif
359
360/**
361 * xmlReversePattern:
362 * @comp: the compiled match expression
363 *
364 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000365 *
366 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000367 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000368static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000369xmlReversePattern(xmlPatternPtr comp) {
Daniel Veillard56de87e2005-02-16 00:22:29 +0000370 int i, j;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000371
Daniel Veillard56de87e2005-02-16 00:22:29 +0000372 /*
373 * remove the leading // for //a or .//a
374 */
375 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
376 for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
377 comp->steps[i].value = comp->steps[j].value;
378 comp->steps[i].value2 = comp->steps[j].value2;
379 comp->steps[i].op = comp->steps[j].op;
380 }
381 comp->nbStep--;
382 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000383 if (comp->nbStep >= comp->maxStep) {
384 xmlStepOpPtr temp;
385 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
386 sizeof(xmlStepOp));
387 if (temp == NULL) {
388 ERROR(ctxt, NULL, NULL,
389 "xmlReversePattern: realloc failed\n");
390 return (-1);
391 }
392 comp->steps = temp;
393 comp->maxStep *= 2;
394 }
Daniel Veillard56de87e2005-02-16 00:22:29 +0000395 i = 0;
396 j = comp->nbStep - 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000397 while (j > i) {
398 register const xmlChar *tmp;
399 register xmlPatOp op;
400 tmp = comp->steps[i].value;
401 comp->steps[i].value = comp->steps[j].value;
402 comp->steps[j].value = tmp;
403 tmp = comp->steps[i].value2;
404 comp->steps[i].value2 = comp->steps[j].value2;
405 comp->steps[j].value2 = tmp;
406 op = comp->steps[i].op;
407 comp->steps[i].op = comp->steps[j].op;
408 comp->steps[j].op = op;
409 j--;
410 i++;
411 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000412 comp->steps[comp->nbStep].value = NULL;
413 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000414 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000415 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000416}
417
418/************************************************************************
419 * *
420 * The interpreter for the precompiled patterns *
421 * *
422 ************************************************************************/
423
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000424static int
425xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
426 if ((states->states == NULL) || (states->maxstates <= 0)) {
427 states->maxstates = 4;
428 states->nbstates = 0;
429 states->states = xmlMalloc(4 * sizeof(xmlStepState));
430 }
431 else if (states->maxstates <= states->nbstates) {
432 xmlStepState *tmp;
433
434 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
435 2 * states->maxstates * sizeof(xmlStepState));
436 if (tmp == NULL)
437 return(-1);
438 states->states = tmp;
439 states->maxstates *= 2;
440 }
441 states->states[states->nbstates].step = step;
442 states->states[states->nbstates++].node = node;
443#if 0
444 fprintf(stderr, "Push: %d, %s\n", step, node->name);
445#endif
446 return(0);
447}
448
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000449/**
450 * xmlPatMatch:
451 * @comp: the precompiled pattern
452 * @node: a node
453 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000454 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000455 *
456 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
457 */
458static int
459xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
460 int i;
461 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000462 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000463
464 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000465 i = 0;
466restart:
467 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000468 step = &comp->steps[i];
469 switch (step->op) {
470 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000471 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000472 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000473 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000474 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000475 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000476 if ((node->type == XML_DOCUMENT_NODE) ||
477#ifdef LIBXML_DOCB_ENABLED
478 (node->type == XML_DOCB_DOCUMENT_NODE) ||
479#endif
480 (node->type == XML_HTML_DOCUMENT_NODE))
481 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000482 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000483 case XML_OP_ELEM:
484 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000485 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000486 if (step->value == NULL)
487 continue;
488 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000489 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000490 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000491 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000492
493 /* Namespace test */
494 if (node->ns == NULL) {
495 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000496 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000497 } else if (node->ns->href != NULL) {
498 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000499 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000500 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000501 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000502 }
503 continue;
504 case XML_OP_CHILD: {
505 xmlNodePtr lst;
506
507 if ((node->type != XML_ELEMENT_NODE) &&
508 (node->type != XML_DOCUMENT_NODE) &&
509#ifdef LIBXML_DOCB_ENABLED
510 (node->type != XML_DOCB_DOCUMENT_NODE) &&
511#endif
512 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000513 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000514
515 lst = node->children;
516
517 if (step->value != NULL) {
518 while (lst != NULL) {
519 if ((lst->type == XML_ELEMENT_NODE) &&
520 (step->value[0] == lst->name[0]) &&
521 (xmlStrEqual(step->value, lst->name)))
522 break;
523 lst = lst->next;
524 }
525 if (lst != NULL)
526 continue;
527 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000528 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000529 }
530 case XML_OP_ATTR:
531 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000532 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000533 if (step->value != NULL) {
534 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000535 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000536 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000537 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000538 }
539 /* Namespace test */
540 if (node->ns == NULL) {
541 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000542 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000543 } else if (step->value2 != NULL) {
544 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000545 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000546 }
547 continue;
548 case XML_OP_PARENT:
549 if ((node->type == XML_DOCUMENT_NODE) ||
550 (node->type == XML_HTML_DOCUMENT_NODE) ||
551#ifdef LIBXML_DOCB_ENABLED
552 (node->type == XML_DOCB_DOCUMENT_NODE) ||
553#endif
554 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000555 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000556 node = node->parent;
557 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000558 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000559 if (step->value == NULL)
560 continue;
561 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000562 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000563 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000564 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000565 /* Namespace test */
566 if (node->ns == NULL) {
567 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000568 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000569 } else if (node->ns->href != NULL) {
570 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000571 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000572 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_ANCESTOR:
577 /* TODO: implement coalescing of ANCESTOR/NODE ops */
578 if (step->value == NULL) {
579 i++;
580 step = &comp->steps[i];
581 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000582 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000583 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000584 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000585 if (step->value == NULL)
586 return(-1);
587 }
588 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000589 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000590 if ((node->type == XML_DOCUMENT_NODE) ||
591 (node->type == XML_HTML_DOCUMENT_NODE) ||
592#ifdef LIBXML_DOCB_ENABLED
593 (node->type == XML_DOCB_DOCUMENT_NODE) ||
594#endif
595 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000596 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000597 node = node->parent;
598 while (node != NULL) {
599 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000600 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000601 if ((node->type == XML_ELEMENT_NODE) &&
602 (step->value[0] == node->name[0]) &&
603 (xmlStrEqual(step->value, node->name))) {
604 /* Namespace test */
605 if (node->ns == NULL) {
606 if (step->value2 == NULL)
607 break;
608 } else if (node->ns->href != NULL) {
609 if ((step->value2 != NULL) &&
610 (xmlStrEqual(step->value2, node->ns->href)))
611 break;
612 }
613 }
614 node = node->parent;
615 }
616 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000617 goto rollback;
618 /*
619 * prepare a potential rollback from here
620 * for ancestors of that node.
621 */
622 if (step->op == XML_OP_ANCESTOR)
623 xmlPatPushState(&states, i, node);
624 else
625 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000626 continue;
627 case XML_OP_NS:
628 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000629 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000630 if (node->ns == NULL) {
631 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000632 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000633 } else if (node->ns->href != NULL) {
634 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000635 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000636 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000637 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000638 }
639 break;
640 case XML_OP_ALL:
641 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000642 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000643 break;
644 }
645 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000646found:
647 if (states.states != NULL) {
648 /* Free the rollback states */
649 xmlFree(states.states);
650 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000651 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000652rollback:
653 /* got an error try to rollback */
654 if (states.states == NULL)
655 return(0);
656 if (states.nbstates <= 0) {
657 xmlFree(states.states);
658 return(0);
659 }
660 states.nbstates--;
661 i = states.states[states.nbstates].step;
662 node = states.states[states.nbstates].node;
663#if 0
664 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
665#endif
666 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000667}
668
669/************************************************************************
670 * *
671 * Dedicated parser for templates *
672 * *
673 ************************************************************************/
674
675#define TODO \
676 xmlGenericError(xmlGenericErrorContext, \
677 "Unimplemented block at %s:%d\n", \
678 __FILE__, __LINE__);
679#define CUR (*ctxt->cur)
680#define SKIP(val) ctxt->cur += (val)
681#define NXT(val) ctxt->cur[(val)]
682#define CUR_PTR ctxt->cur
683
684#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000685 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000686
687#define CURRENT (*ctxt->cur)
688#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
689
690
691#define PUSH(op, val, val2) \
692 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
693
694#define XSLT_ERROR(X) \
695 { xsltError(ctxt, __FILE__, __LINE__, X); \
696 ctxt->error = (X); return; }
697
698#define XSLT_ERROR0(X) \
699 { xsltError(ctxt, __FILE__, __LINE__, X); \
700 ctxt->error = (X); return(0); }
701
702#if 0
703/**
704 * xmlPatScanLiteral:
705 * @ctxt: the XPath Parser context
706 *
707 * Parse an XPath Litteral:
708 *
709 * [29] Literal ::= '"' [^"]* '"'
710 * | "'" [^']* "'"
711 *
712 * Returns the Literal parsed or NULL
713 */
714
715static xmlChar *
716xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
717 const xmlChar *q, *cur;
718 xmlChar *ret = NULL;
719 int val, len;
720
721 SKIP_BLANKS;
722 if (CUR == '"') {
723 NEXT;
724 cur = q = CUR_PTR;
725 val = xmlStringCurrentChar(NULL, cur, &len);
726 while ((IS_CHAR(val)) && (val != '"')) {
727 cur += len;
728 val = xmlStringCurrentChar(NULL, cur, &len);
729 }
730 if (!IS_CHAR(val)) {
731 ctxt->error = 1;
732 return(NULL);
733 } else {
734 ret = xmlStrndup(q, cur - q);
735 }
736 cur += len;
737 CUR_PTR = cur;
738 } else if (CUR == '\'') {
739 NEXT;
740 cur = q = CUR_PTR;
741 val = xmlStringCurrentChar(NULL, cur, &len);
742 while ((IS_CHAR(val)) && (val != '\'')) {
743 cur += len;
744 val = xmlStringCurrentChar(NULL, cur, &len);
745 }
746 if (!IS_CHAR(val)) {
747 ctxt->error = 1;
748 return(NULL);
749 } else {
750 ret = xmlStrndup(q, cur - q);
751 }
752 cur += len;
753 CUR_PTR = cur;
754 } else {
755 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
756 ctxt->error = 1;
757 return(NULL);
758 }
759 return(ret);
760}
761#endif
762
763/**
764 * xmlPatScanName:
765 * @ctxt: the XPath Parser context
766 *
767 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
768 * CombiningChar | Extender
769 *
770 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
771 *
772 * [6] Names ::= Name (S Name)*
773 *
774 * Returns the Name parsed or NULL
775 */
776
777static xmlChar *
778xmlPatScanName(xmlPatParserContextPtr ctxt) {
779 const xmlChar *q, *cur;
780 xmlChar *ret = NULL;
781 int val, len;
782
783 SKIP_BLANKS;
784
785 cur = q = CUR_PTR;
786 val = xmlStringCurrentChar(NULL, cur, &len);
787 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
788 return(NULL);
789
790 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
791 (val == '.') || (val == '-') ||
792 (val == '_') ||
793 (IS_COMBINING(val)) ||
794 (IS_EXTENDER(val))) {
795 cur += len;
796 val = xmlStringCurrentChar(NULL, cur, &len);
797 }
798 ret = xmlStrndup(q, cur - q);
799 CUR_PTR = cur;
800 return(ret);
801}
802
803/**
804 * xmlPatScanNCName:
805 * @ctxt: the XPath Parser context
806 *
807 * Parses a non qualified name
808 *
809 * Returns the Name parsed or NULL
810 */
811
812static xmlChar *
813xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
814 const xmlChar *q, *cur;
815 xmlChar *ret = NULL;
816 int val, len;
817
818 SKIP_BLANKS;
819
820 cur = q = CUR_PTR;
821 val = xmlStringCurrentChar(NULL, cur, &len);
822 if (!IS_LETTER(val) && (val != '_'))
823 return(NULL);
824
825 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
826 (val == '.') || (val == '-') ||
827 (val == '_') ||
828 (IS_COMBINING(val)) ||
829 (IS_EXTENDER(val))) {
830 cur += len;
831 val = xmlStringCurrentChar(NULL, cur, &len);
832 }
833 ret = xmlStrndup(q, cur - q);
834 CUR_PTR = cur;
835 return(ret);
836}
837
838#if 0
839/**
840 * xmlPatScanQName:
841 * @ctxt: the XPath Parser context
842 * @prefix: the place to store the prefix
843 *
844 * Parse a qualified name
845 *
846 * Returns the Name parsed or NULL
847 */
848
849static xmlChar *
850xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
851 xmlChar *ret = NULL;
852
853 *prefix = NULL;
854 ret = xmlPatScanNCName(ctxt);
855 if (CUR == ':') {
856 *prefix = ret;
857 NEXT;
858 ret = xmlPatScanNCName(ctxt);
859 }
860 return(ret);
861}
862#endif
863
864/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000865 * xmlCompileAttributeTest:
866 * @ctxt: the compilation context
867 *
868 * Compile an attribute test.
869 */
870static void
871xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
872 xmlChar *token = NULL;
873 xmlChar *name = NULL;
874 xmlChar *URL = NULL;
875
876 name = xmlPatScanNCName(ctxt);
877 if (name == NULL) {
878 if (CUR == '*') {
879 PUSH(XML_OP_ATTR, NULL, NULL);
880 } else {
881 ERROR(NULL, NULL, NULL,
882 "xmlCompileAttributeTest : Name expected\n");
883 ctxt->error = 1;
884 }
885 return;
886 }
887 if (CUR == ':') {
888 int i;
889 xmlChar *prefix = name;
890
891 NEXT;
892 /*
893 * This is a namespace match
894 */
895 token = xmlPatScanName(ctxt);
896 for (i = 0;i < ctxt->nb_namespaces;i++) {
897 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
898 URL = xmlStrdup(ctxt->namespaces[2 * i]);
899 break;
900 }
901 }
902 if (i >= ctxt->nb_namespaces) {
903 ERROR5(NULL, NULL, NULL,
904 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
905 prefix);
906 ctxt->error = 1;
907 goto error;
908 }
909
910 xmlFree(prefix);
911 if (token == NULL) {
912 if (CUR == '*') {
913 NEXT;
914 PUSH(XML_OP_ATTR, NULL, URL);
915 } else {
916 ERROR(NULL, NULL, NULL,
917 "xmlCompileAttributeTest : Name expected\n");
918 ctxt->error = 1;
919 goto error;
920 }
921 } else {
922 PUSH(XML_OP_ATTR, token, URL);
923 }
924 } else {
925 PUSH(XML_OP_ATTR, name, NULL);
926 }
927 return;
928error:
929 if (URL != NULL)
930 xmlFree(URL);
931 if (token != NULL)
932 xmlFree(token);
933}
934
935
936/**
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000937 * xmlCompileStepPattern:
938 * @ctxt: the compilation context
939 *
940 * Compile the Step Pattern and generates a precompiled
941 * form suitable for fast matching.
942 *
943 * [3] Step ::= '.' | NameTest
944 * [4] NameTest ::= QName | '*' | NCName ':' '*'
945 */
946
947static void
948xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
949 xmlChar *token = NULL;
950 xmlChar *name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000951 xmlChar *URL = NULL;
952
953 SKIP_BLANKS;
954 if (CUR == '.') {
955 NEXT;
956 PUSH(XML_OP_ELEM, NULL, NULL);
957 return;
958 }
959 name = xmlPatScanNCName(ctxt);
960 if (name == NULL) {
961 if (CUR == '*') {
962 NEXT;
963 PUSH(XML_OP_ALL, NULL, NULL);
964 return;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000965 } else if (CUR == '@') {
966 NEXT;
967 xmlCompileAttributeTest(ctxt);
968 if (ctxt->error != 0)
969 goto error;
970 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000971 } else {
972 ERROR(NULL, NULL, NULL,
973 "xmlCompileStepPattern : Name expected\n");
974 ctxt->error = 1;
975 return;
976 }
977 }
978 SKIP_BLANKS;
979 if (CUR == ':') {
980 NEXT;
981 if (CUR != ':') {
982 xmlChar *prefix = name;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000983 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000984
985 /*
986 * This is a namespace match
987 */
988 token = xmlPatScanName(ctxt);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000989 for (i = 0;i < ctxt->nb_namespaces;i++) {
990 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Daniel Veillard0996a162005-02-05 14:00:10 +0000991 URL = xmlStrdup(ctxt->namespaces[2 * i]);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000992 break;
993 }
994 }
995 if (i >= ctxt->nb_namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000996 ERROR5(NULL, NULL, NULL,
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000997 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
998 prefix);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000999 ctxt->error = 1;
1000 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001001 }
1002 xmlFree(prefix);
1003 if (token == NULL) {
1004 if (CUR == '*') {
1005 NEXT;
1006 PUSH(XML_OP_NS, URL, NULL);
1007 } else {
1008 ERROR(NULL, NULL, NULL,
1009 "xmlCompileStepPattern : Name expected\n");
1010 ctxt->error = 1;
1011 goto error;
1012 }
1013 } else {
1014 PUSH(XML_OP_ELEM, token, URL);
1015 }
1016 } else {
1017 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001018 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1019 xmlFree(name);
1020 name = xmlPatScanName(ctxt);
1021 if (name == NULL) {
1022 if (CUR == '*') {
1023 NEXT;
1024 PUSH(XML_OP_ALL, NULL, NULL);
1025 return;
1026 } else {
1027 ERROR(NULL, NULL, NULL,
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001028 "xmlCompileStepPattern : QName expected\n");
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001029 ctxt->error = 1;
1030 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001031 }
1032 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001033 if (CUR == ':') {
1034 xmlChar *prefix = name;
1035 int i;
1036
1037 NEXT;
1038 /*
1039 * This is a namespace match
1040 */
1041 token = xmlPatScanName(ctxt);
1042 for (i = 0;i < ctxt->nb_namespaces;i++) {
1043 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1044 URL = xmlStrdup(ctxt->namespaces[2 * i]);
1045 break;
1046 }
1047 }
1048 if (i >= ctxt->nb_namespaces) {
1049 ERROR5(NULL, NULL, NULL,
1050 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1051 prefix);
1052 ctxt->error = 1;
1053 goto error;
1054 }
1055 xmlFree(prefix);
1056 if (token == NULL) {
1057 if (CUR == '*') {
1058 NEXT;
1059 PUSH(XML_OP_NS, URL, NULL);
1060 } else {
1061 ERROR(NULL, NULL, NULL,
1062 "xmlCompileStepPattern : Name expected\n");
1063 ctxt->error = 1;
1064 goto error;
1065 }
1066 } else {
1067 PUSH(XML_OP_CHILD, token, URL);
1068 }
1069 } else
1070 PUSH(XML_OP_CHILD, name, NULL);
1071 return;
1072 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1073 xmlFree(name);
1074 name = NULL;
1075 xmlCompileAttributeTest(ctxt);
1076 if (ctxt->error != 0)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001077 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001078 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001079 } else {
1080 ERROR(NULL, NULL, NULL,
1081 "xmlCompileStepPattern : 'child' or 'attribute' expected\n");
1082 ctxt->error = 1;
1083 goto error;
1084 }
Daniel Veillard0e460da2005-03-30 22:47:10 +00001085 /* NOT REACHED xmlFree(name); */
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001086 }
1087 } else if (CUR == '*') {
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001088 if (name != NULL) {
1089 ctxt->error = 1;
1090 goto error;
1091 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001092 NEXT;
1093 PUSH(XML_OP_ALL, token, NULL);
1094 } else {
1095 if (name == NULL) {
1096 ctxt->error = 1;
1097 goto error;
1098 }
1099 PUSH(XML_OP_ELEM, name, NULL);
1100 }
1101 return;
1102error:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001103 if (URL != NULL)
1104 xmlFree(URL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001105 if (token != NULL)
1106 xmlFree(token);
1107 if (name != NULL)
1108 xmlFree(name);
1109}
1110
1111/**
1112 * xmlCompilePathPattern:
1113 * @ctxt: the compilation context
1114 *
1115 * Compile the Path Pattern and generates a precompiled
1116 * form suitable for fast matching.
1117 *
1118 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1119 */
1120static void
1121xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1122 SKIP_BLANKS;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001123 if (CUR == '/') {
1124 ctxt->comp->flags |= PAT_FROM_ROOT;
1125 } else if (CUR == '.') {
1126 ctxt->comp->flags |= PAT_FROM_CUR;
1127 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001128 if ((CUR == '/') && (NXT(1) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001129 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001130 NEXT;
1131 NEXT;
1132 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001133 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001134 NEXT;
1135 NEXT;
1136 NEXT;
1137 }
1138 if (CUR == '@') {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001139 NEXT;
1140 xmlCompileAttributeTest(ctxt);
1141 SKIP_BLANKS;
William M. Brackfbb619f2005-06-06 13:49:18 +00001142 if (CUR != 0) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001143 xmlCompileStepPattern(ctxt);
1144 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001145 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001146 if (CUR == '/') {
1147 PUSH(XML_OP_ROOT, NULL, NULL);
1148 NEXT;
1149 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001150 xmlCompileStepPattern(ctxt);
1151 SKIP_BLANKS;
1152 while (CUR == '/') {
William M. Brackfbb619f2005-06-06 13:49:18 +00001153 if (NXT(1) == '/') {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001154 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1155 NEXT;
1156 NEXT;
1157 SKIP_BLANKS;
1158 xmlCompileStepPattern(ctxt);
1159 } else {
1160 PUSH(XML_OP_PARENT, NULL, NULL);
1161 NEXT;
1162 SKIP_BLANKS;
William M. Brackfbb619f2005-06-06 13:49:18 +00001163 if (CUR != 0) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001164 xmlCompileStepPattern(ctxt);
1165 }
1166 }
1167 }
1168 }
Daniel Veillard56de87e2005-02-16 00:22:29 +00001169 if (CUR != 0) {
1170 ERROR5(NULL, NULL, NULL,
1171 "Failed to compile pattern %s\n", ctxt->base);
1172 ctxt->error = 1;
1173 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001174error:
1175 return;
1176}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001177
1178/************************************************************************
1179 * *
1180 * The streaming code *
1181 * *
1182 ************************************************************************/
1183
1184#ifdef DEBUG_STREAMING
1185static void
1186xmlDebugStreamComp(xmlStreamCompPtr stream) {
1187 int i;
1188
1189 if (stream == NULL) {
1190 printf("Stream: NULL\n");
1191 return;
1192 }
1193 printf("Stream: %d steps\n", stream->nbStep);
1194 for (i = 0;i < stream->nbStep;i++) {
1195 if (stream->steps[i].ns != NULL) {
1196 printf("{%s}", stream->steps[i].ns);
1197 }
1198 if (stream->steps[i].name == NULL) {
1199 printf("* ");
1200 } else {
1201 printf("%s ", stream->steps[i].name);
1202 }
1203 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1204 printf("root ");
1205 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1206 printf("// ");
1207 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1208 printf("final ");
1209 printf("\n");
1210 }
1211}
1212static void
1213xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1214 int i;
1215
1216 if (ctxt == NULL) {
1217 printf("Stream: NULL\n");
1218 return;
1219 }
1220 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1221 if (match)
1222 printf("matches\n");
1223 else
1224 printf("\n");
1225 for (i = 0;i < ctxt->nbState;i++) {
1226 if (ctxt->states[2 * i] < 0)
1227 printf(" %d: free\n", i);
1228 else {
1229 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1230 ctxt->states[(2 * i) + 1]);
1231 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1232 XML_STREAM_STEP_DESC)
1233 printf(" //\n");
1234 else
1235 printf("\n");
1236 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001237 }
1238}
1239#endif
1240/**
1241 * xmlNewStreamComp:
1242 * @size: the number of expected steps
1243 *
1244 * build a new compiled pattern for streaming
1245 *
1246 * Returns the new structure or NULL in case of error.
1247 */
1248static xmlStreamCompPtr
1249xmlNewStreamComp(int size) {
1250 xmlStreamCompPtr cur;
1251
1252 if (size < 4)
1253 size = 4;
1254
1255 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1256 if (cur == NULL) {
1257 ERROR(NULL, NULL, NULL,
1258 "xmlNewStreamComp: malloc failed\n");
1259 return(NULL);
1260 }
1261 memset(cur, 0, sizeof(xmlStreamComp));
1262 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1263 if (cur->steps == NULL) {
1264 xmlFree(cur);
1265 ERROR(NULL, NULL, NULL,
1266 "xmlNewStreamComp: malloc failed\n");
1267 return(NULL);
1268 }
1269 cur->nbStep = 0;
1270 cur->maxStep = size;
1271 return(cur);
1272}
1273
1274/**
1275 * xmlFreeStreamComp:
1276 * @comp: the compiled pattern for streaming
1277 *
1278 * Free the compiled pattern for streaming
1279 */
1280static void
1281xmlFreeStreamComp(xmlStreamCompPtr comp) {
1282 if (comp != NULL) {
1283 if (comp->steps != NULL)
1284 xmlFree(comp->steps);
1285 if (comp->dict != NULL)
1286 xmlDictFree(comp->dict);
1287 xmlFree(comp);
1288 }
1289}
1290
1291/**
1292 * xmlStreamCompAddStep:
1293 * @comp: the compiled pattern for streaming
1294 * @name: the first string, the name, or NULL for *
1295 * @ns: the second step, the namespace name
1296 * @flags: the flags for that step
1297 *
1298 * Add a new step to the compiled pattern
1299 *
1300 * Returns -1 in case of error or the step index if successful
1301 */
1302static int
1303xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1304 const xmlChar *ns, int flags) {
1305 xmlStreamStepPtr cur;
1306
1307 if (comp->nbStep >= comp->maxStep) {
1308 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1309 comp->maxStep * 2 * sizeof(xmlStreamStep));
1310 if (cur == NULL) {
1311 ERROR(NULL, NULL, NULL,
1312 "xmlNewStreamComp: malloc failed\n");
1313 return(-1);
1314 }
1315 comp->steps = cur;
1316 comp->maxStep *= 2;
1317 }
1318 cur = &comp->steps[comp->nbStep++];
1319 cur->flags = flags;
1320 cur->name = name;
1321 cur->ns = ns;
1322 return(comp->nbStep - 1);
1323}
1324
1325/**
1326 * xmlStreamCompile:
1327 * @comp: the precompiled pattern
1328 *
1329 * Tries to stream compile a pattern
1330 *
1331 * Returns -1 in case of failure and 0 in case of success.
1332 */
1333static int
1334xmlStreamCompile(xmlPatternPtr comp) {
1335 xmlStreamCompPtr stream;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001336 int i, s = 0, root = 0, flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001337
1338 if ((comp == NULL) || (comp->steps == NULL))
1339 return(-1);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001340 /*
1341 * special case for .
1342 */
1343 if ((comp->nbStep == 1) &&
1344 (comp->steps[0].op == XML_OP_ELEM) &&
1345 (comp->steps[0].value == NULL) &&
1346 (comp->steps[0].value2 == NULL)) {
1347 stream = xmlNewStreamComp(0);
1348 if (stream == NULL)
1349 return(-1);
1350 comp->stream = stream;
1351 return(0);
1352 }
1353
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001354 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1355 if (stream == NULL)
1356 return(-1);
1357 if (comp->dict != NULL) {
1358 stream->dict = comp->dict;
1359 xmlDictReference(stream->dict);
1360 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001361
1362 /*
1363 * Skip leading ./ on relative paths
1364 */
1365 i = 0;
1366 while ((comp->flags & PAT_FROM_CUR) && (comp->nbStep > i + 2) &&
1367 (comp->steps[i].op == XML_OP_ELEM) &&
1368 (comp->steps[i].value == NULL) &&
1369 (comp->steps[i].value2 == NULL) &&
1370 (comp->steps[i + 1].op == XML_OP_PARENT)) {
1371 i += 2;
1372 }
1373 for (;i < comp->nbStep;i++) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001374 switch (comp->steps[i].op) {
1375 case XML_OP_END:
1376 break;
1377 case XML_OP_ROOT:
1378 if (i != 0)
1379 goto error;
1380 root = 1;
1381 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001382 case XML_OP_NS:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001383 s = xmlStreamCompAddStep(stream, NULL,
1384 comp->steps[i].value, flags);
1385 flags = 0;
1386 if (s < 0)
1387 goto error;
1388 break;
1389 case XML_OP_ATTR:
1390 flags |= XML_STREAM_STEP_ATTR;
1391 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1392 comp->steps[i].value2, flags);
1393 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001394 if (s < 0)
1395 goto error;
1396 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001397 case XML_OP_ELEM:
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001398 if ((comp->steps[i].value == NULL) &&
1399 (comp->steps[i].value2 == NULL) &&
1400 (comp->nbStep > i + 2) &&
1401 (comp->steps[i + 1].op == XML_OP_PARENT)) {
1402 i++;
1403 continue;
1404 }
1405 case XML_OP_CHILD:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001406 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1407 comp->steps[i].value2, flags);
1408 flags = 0;
1409 if (s < 0)
1410 goto error;
1411 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001412 case XML_OP_ALL:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001413 s = xmlStreamCompAddStep(stream, NULL, NULL, flags);
1414 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001415 if (s < 0)
1416 goto error;
1417 break;
1418 case XML_OP_PARENT:
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001419 if ((comp->nbStep > i + 1) &&
1420 (comp->steps[i + 1].op == XML_OP_ELEM) &&
1421 (comp->steps[i + 1].value == NULL) &&
1422 (comp->steps[i + 1].value2 == NULL)) {
1423 i++;
1424 continue;
1425 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001426 break;
1427 case XML_OP_ANCESTOR:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001428 flags |= XML_STREAM_STEP_DESC;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001429 break;
1430 }
1431 }
1432 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1433 if (root)
1434 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1435#ifdef DEBUG_STREAMING
1436 xmlDebugStreamComp(stream);
1437#endif
1438 comp->stream = stream;
1439 return(0);
1440error:
1441 xmlFreeStreamComp(stream);
1442 return(0);
1443}
1444
1445/**
1446 * xmlNewStreamCtxt:
1447 * @size: the number of expected states
1448 *
1449 * build a new stream context
1450 *
1451 * Returns the new structure or NULL in case of error.
1452 */
1453static xmlStreamCtxtPtr
1454xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1455 xmlStreamCtxtPtr cur;
1456
1457 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1458 if (cur == NULL) {
1459 ERROR(NULL, NULL, NULL,
1460 "xmlNewStreamCtxt: malloc failed\n");
1461 return(NULL);
1462 }
1463 memset(cur, 0, sizeof(xmlStreamCtxt));
1464 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1465 if (cur->states == NULL) {
1466 xmlFree(cur);
1467 ERROR(NULL, NULL, NULL,
1468 "xmlNewStreamCtxt: malloc failed\n");
1469 return(NULL);
1470 }
1471 cur->nbState = 0;
1472 cur->maxState = 4;
1473 cur->level = 0;
1474 cur->comp = stream;
1475 return(cur);
1476}
1477
1478/**
1479 * xmlFreeStreamCtxt:
1480 * @stream: the stream context
1481 *
1482 * Free the stream context
1483 */
1484void
1485xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001486 xmlStreamCtxtPtr next;
1487
1488 while (stream != NULL) {
1489 next = stream->next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001490 if (stream->states != NULL)
1491 xmlFree(stream->states);
1492 xmlFree(stream);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001493 stream = next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001494 }
1495}
1496
1497/**
1498 * xmlStreamCtxtAddState:
1499 * @comp: the stream context
1500 * @idx: the step index for that streaming state
1501 *
1502 * Add a new state to the stream context
1503 *
1504 * Returns -1 in case of error or the state index if successful
1505 */
1506static int
1507xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1508 int i;
1509 for (i = 0;i < comp->nbState;i++) {
1510 if (comp->states[2 * i] < 0) {
1511 comp->states[2 * i] = idx;
1512 comp->states[2 * i + 1] = level;
1513 return(i);
1514 }
1515 }
1516 if (comp->nbState >= comp->maxState) {
1517 int *cur;
1518
1519 cur = (int *) xmlRealloc(comp->states,
1520 comp->maxState * 4 * sizeof(int));
1521 if (cur == NULL) {
1522 ERROR(NULL, NULL, NULL,
1523 "xmlNewStreamCtxt: malloc failed\n");
1524 return(-1);
1525 }
1526 comp->states = cur;
1527 comp->maxState *= 2;
1528 }
1529 comp->states[2 * comp->nbState] = idx;
1530 comp->states[2 * comp->nbState++ + 1] = level;
1531 return(comp->nbState - 1);
1532}
1533
1534/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001535 * xmlStreamPushInternal:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001536 * @stream: the stream context
1537 * @name: the current name
1538 * @ns: the namespace name
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001539 * @nodeType: the type of the node
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001540 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001541 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1542 * indicated a dictionary, then strings for name and ns will be expected
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001543 * to come from the dictionary.
1544 * Both @name and @ns being NULL means the / i.e. the root of the document.
1545 * This can also act as a reset.
1546 *
1547 * Returns: -1 in case of error, 1 if the current state in the stream is a
1548 * match and 0 otherwise.
1549 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001550static int
1551xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1552 const xmlChar *name, const xmlChar *ns,
1553 xmlElementType nodeType) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001554 int ret = 0, err = 0, tmp, i, m, match, step, desc, final;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001555 xmlStreamCompPtr comp;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001556#ifdef DEBUG_STREAMING
1557 xmlStreamCtxtPtr orig = stream;
1558#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001559
1560 if ((stream == NULL) || (stream->nbState < 0))
1561 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001562
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001563 while (stream != NULL) {
1564 comp = stream->comp;
1565 if ((name == NULL) && (ns == NULL)) {
1566 stream->nbState = 0;
1567 stream->level = 0;
1568 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1569 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1570 if (tmp < 0)
1571 err++;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001572 if (comp->nbStep == 0)
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001573 ret = 1;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001574 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001575 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001576 continue; /* while */
1577 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001578
1579 /*
1580 * Fast check for ".".
1581 */
1582 if (comp->nbStep == 0) {
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001583 /*
1584 * For non-pattern like evaluation like XML Schema IDCs,
1585 * this will match if we are at the first level only,
1586 * otherwise on every level.
1587 */
1588 if ((nodeType == XML_ELEMENT_NODE) &&
1589 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1590 (stream->level == 0))) {
1591 ret = 1;
1592 }
1593 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001594 goto stream_next;
1595 }
William M. Brackfbb619f2005-06-06 13:49:18 +00001596 tmp = stream->level;
1597 for (i = 0; i < comp->nbStep; i++) {
1598 if (comp->steps[i].flags & XML_STREAM_STEP_DESC) {
1599 tmp = -2;
1600 break;
1601 }
1602 }
1603 if (comp->nbStep < tmp) {
1604 stream->level++;
1605 goto stream_next;
1606 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001607
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001608 /*
1609 * Check evolution of existing states
1610 */
1611 m = stream->nbState;
1612 for (i = 0;i < m;i++) {
1613 match = 0;
1614 step = stream->states[2 * i];
1615 /* dead states */
1616 if (step < 0) continue;
1617 /* skip new states just added */
1618 if (stream->states[(2 * i) + 1] > stream->level)
1619 continue;
1620 /* skip continuations */
1621 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1622 if ((stream->states[(2 * i) + 1] < stream->level) && (!desc))
1623 continue;
1624
1625 /* discard old states */
1626 /* something needed about old level discarded */
1627
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001628 /*
1629 * Check for correct node-type.
1630 */
1631 if ((comp->steps[step].flags & XML_STREAM_STEP_ATTR) &&
1632 (nodeType != XML_ATTRIBUTE_NODE))
1633 continue;
1634
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001635 if (comp->dict) {
1636 if (comp->steps[step].name == NULL) {
1637 if (comp->steps[step].ns == NULL)
1638 match = 1;
1639 else
1640 match = (comp->steps[step].ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001641 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001642 match = ((comp->steps[step].name == name) &&
1643 (comp->steps[step].ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001644 }
1645 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001646 if (comp->steps[step].name == NULL) {
1647 if (comp->steps[step].ns == NULL)
1648 match = 1;
1649 else
1650 match = xmlStrEqual(comp->steps[step].ns, ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001651 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001652 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
1653 (xmlStrEqual(comp->steps[step].ns, ns)));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001654 }
1655 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001656 if (match) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001657 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
1658 if (desc) {
1659 if (final) {
1660 ret = 1;
1661 } else {
1662 /* descending match create a new state */
1663 xmlStreamCtxtAddState(stream, step + 1,
1664 stream->level + 1);
1665 }
1666 } else {
1667 if (final) {
1668 ret = 1;
1669 } else {
1670 xmlStreamCtxtAddState(stream, step + 1,
1671 stream->level + 1);
1672 }
1673 }
1674 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001675 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001676
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001677 /*
1678 * Check creating a new state.
1679 */
1680 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001681
1682 /*
1683 * Check the start only if this is a "desc" evaluation
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001684 * or if we are at the first level of evaluation.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001685 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001686 desc = comp->steps[0].flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001687 if ( ((comp->steps[0].flags & XML_STREAM_STEP_ROOT) == 0) &&
1688 ( ((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1689 ( (desc || (stream->level == 1)) )
1690 )
1691 ) {
1692
1693/*
1694#ifdef SUPPORT_IDC
1695
1696
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001697 if ((desc || (stream->level == 1)) &&
1698 (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT))) {
1699
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001700 *
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001701 * Workaround for missing "self::node()" on "@foo".
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001702 *
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001703 if (comp->steps[0].flags & XML_STREAM_STEP_ATTR) {
1704 xmlStreamCtxtAddState(stream, 0, stream->level);
1705 goto stream_next;
1706 }
1707#else
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001708
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001709 if (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT)) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001710#endif
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001711 */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001712 match = 0;
1713 if (comp->dict) {
1714 if (comp->steps[0].name == NULL) {
1715 if (comp->steps[0].ns == NULL)
1716 match = 1;
1717 else
1718 match = (comp->steps[0].ns == ns);
1719 } else {
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001720 if (stream->flags & XML_PATTERN_NOTPATTERN) {
1721 /*
1722 * Workaround for missing "self::node() on "foo".
1723 */
1724 if (!desc) {
1725 xmlStreamCtxtAddState(stream, 0, stream->level);
1726 goto stream_next;
1727 } else {
1728 match = ((comp->steps[0].name == name) &&
1729 (comp->steps[0].ns == ns));
1730 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001731 } else {
1732 match = ((comp->steps[0].name == name) &&
1733 (comp->steps[0].ns == ns));
1734 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001735 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001736 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001737 if (comp->steps[0].name == NULL) {
1738 if (comp->steps[0].ns == NULL)
1739 match = 1;
1740 else
1741 match = xmlStrEqual(comp->steps[0].ns, ns);
1742 } else {
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001743 if (stream->flags & XML_PATTERN_NOTPATTERN) {
1744 /*
1745 * Workaround for missing "self::node() on "foo".
1746 */
1747 if (!desc) {
1748 xmlStreamCtxtAddState(stream, 0, stream->level);
1749 goto stream_next;
1750 } else {
1751 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1752 (xmlStrEqual(comp->steps[0].ns, ns)));
1753 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001754 } else {
1755 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1756 (xmlStrEqual(comp->steps[0].ns, ns)));
1757 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001758 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001759 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001760 if (match) {
1761 if (comp->steps[0].flags & XML_STREAM_STEP_FINAL)
1762 ret = 1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001763 else
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001764 xmlStreamCtxtAddState(stream, 1, stream->level);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001765 }
1766 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001767stream_next:
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001768 stream = stream->next;
1769 } /* while stream != NULL */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001770
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001771 if (err > 0)
1772 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001773#ifdef DEBUG_STREAMING
1774 xmlDebugStreamCtxt(orig, ret);
1775#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001776 return(ret);
1777}
1778
1779/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001780 * xmlStreamPush:
1781 * @stream: the stream context
1782 * @name: the current name
1783 * @ns: the namespace name
1784 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001785 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1786 * indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001787 * to come from the dictionary.
1788 * Both @name and @ns being NULL means the / i.e. the root of the document.
1789 * This can also act as a reset.
1790 *
1791 * Returns: -1 in case of error, 1 if the current state in the stream is a
1792 * match and 0 otherwise.
1793 */
1794int
1795xmlStreamPush(xmlStreamCtxtPtr stream,
1796 const xmlChar *name, const xmlChar *ns) {
1797 return (xmlStreamPushInternal(stream, name, ns, XML_ELEMENT_NODE));
1798}
1799
1800/**
1801* xmlStreamPushAttr:
1802* @stream: the stream context
1803* @name: the current name
1804* @ns: the namespace name
1805*
William M. Brackfbb619f2005-06-06 13:49:18 +00001806* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
1807* indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001808* to come from the dictionary.
1809* Both @name and @ns being NULL means the / i.e. the root of the document.
1810* This can also act as a reset.
1811*
1812* Returns: -1 in case of error, 1 if the current state in the stream is a
1813* match and 0 otherwise.
1814*/
1815int
1816xmlStreamPushAttr(xmlStreamCtxtPtr stream,
1817 const xmlChar *name, const xmlChar *ns) {
1818 return (xmlStreamPushInternal(stream, name, ns, XML_ATTRIBUTE_NODE));
1819}
1820
1821/**
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001822 * xmlStreamPop:
1823 * @stream: the stream context
1824 *
1825 * push one level from the stream.
1826 *
1827 * Returns: -1 in case of error, 0 otherwise.
1828 */
1829int
1830xmlStreamPop(xmlStreamCtxtPtr stream) {
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001831 int i, m;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001832 int ret;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001833
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001834 if (stream == NULL)
1835 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001836 ret = 0;
1837 while (stream != NULL) {
1838 stream->level--;
1839 if (stream->level < 0)
1840 ret = -1;
1841
1842 /*
1843 * Check evolution of existing states
1844 */
1845 m = stream->nbState;
1846 for (i = 0;i < m;i++) {
1847 if (stream->states[(2 * i)] < 0) break;
1848 /* discard obsoleted states */
1849 if (stream->states[(2 * i) + 1] > stream->level)
1850 stream->states[(2 * i)] = -1;
1851 }
1852 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001853 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001854 return(0);
1855}
1856
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001857/************************************************************************
1858 * *
1859 * The public interfaces *
1860 * *
1861 ************************************************************************/
1862
1863/**
1864 * xmlPatterncompile:
1865 * @pattern: the pattern to compile
William M. Brackfbb619f2005-06-06 13:49:18 +00001866 * @dict: an optional dictionary for interned strings
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001867 * @flags: compilation flags, undefined yet
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001868 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001869 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001870 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001871 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001872 * Returns the compiled form of the pattern or NULL in case of error
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001873 */
1874xmlPatternPtr
Daniel Veillard427174f2003-12-10 10:42:59 +00001875xmlPatterncompile(const xmlChar *pattern, xmlDict *dict,
1876 int flags ATTRIBUTE_UNUSED,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001877 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001878 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001879 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001880 const xmlChar *or, *start;
1881 xmlChar *tmp = NULL;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001882 int type = 0;
1883 int streamable = 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001884
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001885 if (pattern == NULL)
1886 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001887
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001888 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001889 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001890 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001891 tmp = NULL;
1892 while ((*or != 0) && (*or != '|')) or++;
1893 if (*or == 0)
1894 ctxt = xmlNewPatParserContext(start, dict, namespaces);
1895 else {
1896 tmp = xmlStrndup(start, or - start);
1897 if (tmp != NULL) {
1898 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
1899 }
1900 or++;
1901 }
1902 if (ctxt == NULL) goto error;
1903 cur = xmlNewPattern();
1904 if (cur == NULL) goto error;
1905 if (ret == NULL)
1906 ret = cur;
1907 else {
1908 cur->next = ret->next;
1909 ret->next = cur;
1910 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001911 cur->flags = flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001912 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001913
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001914 xmlCompilePathPattern(ctxt);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001915 if (ctxt->error != 0)
1916 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001917 xmlFreePatParserContext(ctxt);
William M. Brackfbb619f2005-06-06 13:49:18 +00001918 ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001919
1920
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001921 if (streamable) {
1922 if (type == 0) {
1923 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
1924 } else if (type == PAT_FROM_ROOT) {
1925 if (cur->flags & PAT_FROM_CUR)
1926 streamable = 0;
1927 } else if (type == PAT_FROM_CUR) {
1928 if (cur->flags & PAT_FROM_ROOT)
1929 streamable = 0;
1930 }
1931 }
1932 if (streamable)
1933 xmlStreamCompile(cur);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001934 if (xmlReversePattern(cur) < 0)
1935 goto error;
William M. Brackfbb619f2005-06-06 13:49:18 +00001936 if (tmp != NULL)
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001937 xmlFree(tmp);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001938 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001939 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001940 if (streamable == 0) {
1941 cur = ret;
1942 while (cur != NULL) {
1943 if (cur->stream != NULL) {
1944 xmlFreeStreamComp(cur->stream);
1945 cur->stream = NULL;
1946 }
1947 cur = cur->next;
1948 }
1949 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001950
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001951 return(ret);
1952error:
1953 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
1954 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001955 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001956 return(NULL);
1957}
1958
1959/**
1960 * xmlPatternMatch:
1961 * @comp: the precompiled pattern
1962 * @node: a node
1963 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001964 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001965 *
1966 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1967 */
1968int
1969xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
1970{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001971 int ret = 0;
1972
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001973 if ((comp == NULL) || (node == NULL))
1974 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001975
1976 while (comp != NULL) {
1977 ret = xmlPatMatch(comp, node);
1978 if (ret != 0)
1979 return(ret);
1980 comp = comp->next;
1981 }
1982 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001983}
1984
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001985/**
1986 * xmlPatternGetStreamCtxt:
1987 * @comp: the precompiled pattern
1988 *
1989 * Get a streaming context for that pattern
1990 * Use xmlFreeStreamCtxt to free the context.
1991 *
1992 * Returns a pointer to the context or NULL in case of failure
1993 */
1994xmlStreamCtxtPtr
1995xmlPatternGetStreamCtxt(xmlPatternPtr comp)
1996{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001997 xmlStreamCtxtPtr ret = NULL, cur;
1998
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001999 if ((comp == NULL) || (comp->stream == NULL))
2000 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002001
2002 while (comp != NULL) {
2003 if (comp->stream == NULL)
2004 goto failed;
2005 cur = xmlNewStreamCtxt(comp->stream);
2006 if (cur == NULL)
2007 goto failed;
2008 if (ret == NULL)
2009 ret = cur;
2010 else {
2011 cur->next = ret->next;
2012 ret->next = cur;
2013 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002014 cur->flags = comp->flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002015 comp = comp->next;
2016 }
2017 return(ret);
2018failed:
2019 xmlFreeStreamCtxt(ret);
2020 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002021}
2022
Daniel Veillard56de87e2005-02-16 00:22:29 +00002023/**
2024 * xmlPatternStreamable:
2025 * @comp: the precompiled pattern
2026 *
2027 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2028 * should work.
2029 *
2030 * Returns 1 if streamable, 0 if not and -1 in case of error.
2031 */
2032int
2033xmlPatternStreamable(xmlPatternPtr comp) {
2034 if (comp == NULL)
2035 return(-1);
2036 while (comp != NULL) {
2037 if (comp->stream == NULL)
2038 return(0);
2039 comp = comp->next;
2040 }
2041 return(1);
2042}
2043
2044/**
2045 * xmlPatternMaxDepth:
2046 * @comp: the precompiled pattern
2047 *
2048 * Check the maximum depth reachable by a pattern
2049 *
2050 * Returns -2 if no limit (using //), otherwise the depth,
2051 * and -1 in case of error
2052 */
2053int
2054xmlPatternMaxDepth(xmlPatternPtr comp) {
2055 int ret = 0, i;
2056 if (comp == NULL)
2057 return(-1);
2058 while (comp != NULL) {
2059 if (comp->stream == NULL)
2060 return(-1);
2061 for (i = 0;i < comp->stream->nbStep;i++)
2062 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2063 return(-2);
2064 if (comp->stream->nbStep > ret)
2065 ret = comp->stream->nbStep;
2066 comp = comp->next;
2067 }
2068 return(ret);
2069
2070}
2071
2072/**
2073 * xmlPatternFromRoot:
2074 * @comp: the precompiled pattern
2075 *
2076 * Check if the pattern must be looked at from the root.
2077 *
2078 * Returns 1 if true, 0 if false and -1 in case of error
2079 */
2080int
2081xmlPatternFromRoot(xmlPatternPtr comp) {
2082 if (comp == NULL)
2083 return(-1);
2084 while (comp != NULL) {
2085 if (comp->stream == NULL)
2086 return(-1);
2087 if (comp->flags & PAT_FROM_ROOT)
2088 return(1);
2089 comp = comp->next;
2090 }
2091 return(0);
2092
2093}
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002094
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002095#define bottom_pattern
2096#include "elfgcchack.h"
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002097#endif /* LIBXML_PATTERN_ENABLED */