blob: e9975f76dc3a79b28f84ddf537c3b4f601e3a9be [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 .
22 * - xmlPatterncompile support of namespaces arguments, I'm not sure
23 * it's implemented and definitely not tested
24 * - handling of disjunction "pattern1 | pattern2" mean needed to build
25 * and check a list internally
Daniel Veillardd4301ab2005-02-03 22:24:10 +000026 * - get rid of the "compile" starting with lowercase
27 * - get rid of the Strdup/Strndup in case of dictionary
Daniel Veillardf9d16912005-01-30 22:36:30 +000028 */
29
Daniel Veillardb3de70c2003-12-02 22:32:15 +000030#define IN_LIBXML
31#include "libxml.h"
32
33#include <string.h>
34#include <libxml/xmlmemory.h>
35#include <libxml/tree.h>
36#include <libxml/hash.h>
37#include <libxml/dict.h>
38#include <libxml/xmlerror.h>
39#include <libxml/parserInternals.h>
40#include <libxml/pattern.h>
41
Daniel Veillardd4301ab2005-02-03 22:24:10 +000042#ifdef LIBXML_PATTERN_ENABLED
Daniel Veillardb3de70c2003-12-02 22:32:15 +000043
Daniel Veillardd4301ab2005-02-03 22:24:10 +000044/* #define DEBUG_STREAMING */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000045
Daniel Veillardb3de70c2003-12-02 22:32:15 +000046#define ERROR(a, b, c, d)
47#define ERROR5(a, b, c, d, e)
48
Daniel Veillard2fc6df92005-01-30 18:42:55 +000049#define XML_STREAM_STEP_DESC 1
50#define XML_STREAM_STEP_FINAL 2
51#define XML_STREAM_STEP_ROOT 4
52
53typedef 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 {
64 xmlDict *dict; /* the dictionnary if any */
65 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 {
71 xmlStreamCompPtr comp; /* the compiled stream */
72 int nbState; /* number of state in the automata */
73 int maxState; /* allocated number of state */
74 int level; /* how deep are we ? */
75 int *states; /* the array of step indexes */
76};
77
78static void xmlFreeStreamComp(xmlStreamCompPtr comp);
79
Daniel Veillardb3de70c2003-12-02 22:32:15 +000080/*
81 * Types are private:
82 */
83
84typedef enum {
85 XML_OP_END=0,
86 XML_OP_ROOT,
87 XML_OP_ELEM,
88 XML_OP_CHILD,
89 XML_OP_ATTR,
90 XML_OP_PARENT,
91 XML_OP_ANCESTOR,
92 XML_OP_NS,
93 XML_OP_ALL
94} xmlPatOp;
95
96
Daniel Veillardd4301ab2005-02-03 22:24:10 +000097typedef struct _xmlStepState xmlStepState;
98typedef xmlStepState *xmlStepStatePtr;
99struct _xmlStepState {
100 int step;
101 xmlNodePtr node;
102};
103
104typedef struct _xmlStepStates xmlStepStates;
105typedef xmlStepStates *xmlStepStatesPtr;
106struct _xmlStepStates {
107 int nbstates;
108 int maxstates;
109 xmlStepStatePtr states;
110};
111
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000112typedef struct _xmlStepOp xmlStepOp;
113typedef xmlStepOp *xmlStepOpPtr;
114struct _xmlStepOp {
115 xmlPatOp op;
116 const xmlChar *value;
117 const xmlChar *value2;
118};
119
120struct _xmlPattern {
121 void *data; /* the associated template */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000122 xmlDictPtr dict; /* the optional dictionnary */
123 struct _xmlPattern *next; /* siblings */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000124 const xmlChar *pattern; /* the pattern */
125
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000126 int nbStep;
127 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000128 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000129 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000130};
131
132typedef struct _xmlPatParserContext xmlPatParserContext;
133typedef xmlPatParserContext *xmlPatParserContextPtr;
134struct _xmlPatParserContext {
135 const xmlChar *cur; /* the current char being parsed */
136 const xmlChar *base; /* the full expression */
137 int error; /* error code */
138 xmlDictPtr dict; /* the dictionnary if any */
139 xmlPatternPtr comp; /* the result */
140 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000141 const xmlChar **namespaces; /* the namespaces definitions */
142 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000143};
144
145/************************************************************************
146 * *
147 * Type functions *
148 * *
149 ************************************************************************/
150
151/**
152 * xmlNewPattern:
153 *
154 * Create a new XSLT Pattern
155 *
156 * Returns the newly allocated xmlPatternPtr or NULL in case of error
157 */
158static xmlPatternPtr
159xmlNewPattern(void) {
160 xmlPatternPtr cur;
161
162 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
163 if (cur == NULL) {
164 ERROR(NULL, NULL, NULL,
165 "xmlNewPattern : malloc failed\n");
166 return(NULL);
167 }
168 memset(cur, 0, sizeof(xmlPattern));
169 cur->maxStep = 10;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000170 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
171 if (cur->steps == NULL) {
172 xmlFree(cur);
173 ERROR(NULL, NULL, NULL,
174 "xmlNewPattern : malloc failed\n");
175 return(NULL);
176 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000177 return(cur);
178}
179
180/**
181 * xmlFreePattern:
182 * @comp: an XSLT comp
183 *
184 * Free up the memory allocated by @comp
185 */
186void
187xmlFreePattern(xmlPatternPtr comp) {
188 xmlStepOpPtr op;
189 int i;
190
191 if (comp == NULL)
192 return;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000193 if (comp->stream != NULL)
194 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000195 if (comp->pattern != NULL)
196 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000197 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000198 if (comp->dict == NULL) {
199 for (i = 0;i < comp->nbStep;i++) {
200 op = &comp->steps[i];
201 if (op->value != NULL)
202 xmlFree((xmlChar *) op->value);
203 if (op->value2 != NULL)
204 xmlFree((xmlChar *) op->value2);
205 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000206 }
207 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000208 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000209 if (comp->dict != NULL)
210 xmlDictFree(comp->dict);
211
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000212 memset(comp, -1, sizeof(xmlPattern));
213 xmlFree(comp);
214}
215
216/**
217 * xmlFreePatternList:
218 * @comp: an XSLT comp list
219 *
220 * Free up the memory allocated by all the elements of @comp
221 */
222void
223xmlFreePatternList(xmlPatternPtr comp) {
224 xmlPatternPtr cur;
225
226 while (comp != NULL) {
227 cur = comp;
228 comp = comp->next;
229 xmlFreePattern(cur);
230 }
231}
232
233/**
234 * xmlNewPatParserContext:
235 * @pattern: the pattern context
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000236 * @dict: the inherited dictionnary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000237 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
238 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000239 *
240 * Create a new XML pattern parser context
241 *
242 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
243 */
244static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000245xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
246 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000247 xmlPatParserContextPtr cur;
248
249 if (pattern == NULL)
250 return(NULL);
251
252 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
253 if (cur == NULL) {
254 ERROR(NULL, NULL, NULL,
255 "xmlNewPatParserContext : malloc failed\n");
256 return(NULL);
257 }
258 memset(cur, 0, sizeof(xmlPatParserContext));
259 cur->dict = dict;
260 cur->cur = pattern;
261 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000262 if (namespaces != NULL) {
263 int i;
264 for (i = 0;namespaces[2 * i] != NULL;i++);
265 cur->nb_namespaces = i;
266 } else {
267 cur->nb_namespaces = 0;
268 }
269 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000270 return(cur);
271}
272
273/**
274 * xmlFreePatParserContext:
275 * @ctxt: an XSLT parser context
276 *
277 * Free up the memory allocated by @ctxt
278 */
279static void
280xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
281 if (ctxt == NULL)
282 return;
283 memset(ctxt, -1, sizeof(xmlPatParserContext));
284 xmlFree(ctxt);
285}
286
287/**
288 * xmlPatternAdd:
289 * @comp: the compiled match expression
290 * @op: an op
291 * @value: the first value
292 * @value2: the second value
293 *
294 * Add an step to an XSLT Compiled Match
295 *
296 * Returns -1 in case of failure, 0 otherwise.
297 */
298static int
299xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
300 xmlPatternPtr comp,
301 xmlPatOp op, xmlChar * value, xmlChar * value2)
302{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000303 if (comp->nbStep >= comp->maxStep) {
304 xmlStepOpPtr temp;
305 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
306 sizeof(xmlStepOp));
307 if (temp == NULL) {
308 ERROR(ctxt, NULL, NULL,
309 "xmlPatternAdd: realloc failed\n");
310 return (-1);
311 }
312 comp->steps = temp;
313 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000314 }
315 comp->steps[comp->nbStep].op = op;
316 comp->steps[comp->nbStep].value = value;
317 comp->steps[comp->nbStep].value2 = value2;
318 comp->nbStep++;
319 return (0);
320}
321
322#if 0
323/**
324 * xsltSwapTopPattern:
325 * @comp: the compiled match expression
326 *
327 * reverse the two top steps.
328 */
329static void
330xsltSwapTopPattern(xmlPatternPtr comp) {
331 int i;
332 int j = comp->nbStep - 1;
333
334 if (j > 0) {
335 register const xmlChar *tmp;
336 register xmlPatOp op;
337 i = j - 1;
338 tmp = comp->steps[i].value;
339 comp->steps[i].value = comp->steps[j].value;
340 comp->steps[j].value = tmp;
341 tmp = comp->steps[i].value2;
342 comp->steps[i].value2 = comp->steps[j].value2;
343 comp->steps[j].value2 = tmp;
344 op = comp->steps[i].op;
345 comp->steps[i].op = comp->steps[j].op;
346 comp->steps[j].op = op;
347 }
348}
349#endif
350
351/**
352 * xmlReversePattern:
353 * @comp: the compiled match expression
354 *
355 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000356 *
357 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000358 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000359static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000360xmlReversePattern(xmlPatternPtr comp) {
361 int i = 0;
362 int j = comp->nbStep - 1;
363
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000364 if (comp->nbStep >= comp->maxStep) {
365 xmlStepOpPtr temp;
366 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
367 sizeof(xmlStepOp));
368 if (temp == NULL) {
369 ERROR(ctxt, NULL, NULL,
370 "xmlReversePattern: realloc failed\n");
371 return (-1);
372 }
373 comp->steps = temp;
374 comp->maxStep *= 2;
375 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000376 while (j > i) {
377 register const xmlChar *tmp;
378 register xmlPatOp op;
379 tmp = comp->steps[i].value;
380 comp->steps[i].value = comp->steps[j].value;
381 comp->steps[j].value = tmp;
382 tmp = comp->steps[i].value2;
383 comp->steps[i].value2 = comp->steps[j].value2;
384 comp->steps[j].value2 = tmp;
385 op = comp->steps[i].op;
386 comp->steps[i].op = comp->steps[j].op;
387 comp->steps[j].op = op;
388 j--;
389 i++;
390 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000391 comp->steps[comp->nbStep].value = NULL;
392 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000393 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000394 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000395}
396
397/************************************************************************
398 * *
399 * The interpreter for the precompiled patterns *
400 * *
401 ************************************************************************/
402
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000403static int
404xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
405 if ((states->states == NULL) || (states->maxstates <= 0)) {
406 states->maxstates = 4;
407 states->nbstates = 0;
408 states->states = xmlMalloc(4 * sizeof(xmlStepState));
409 }
410 else if (states->maxstates <= states->nbstates) {
411 xmlStepState *tmp;
412
413 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
414 2 * states->maxstates * sizeof(xmlStepState));
415 if (tmp == NULL)
416 return(-1);
417 states->states = tmp;
418 states->maxstates *= 2;
419 }
420 states->states[states->nbstates].step = step;
421 states->states[states->nbstates++].node = node;
422#if 0
423 fprintf(stderr, "Push: %d, %s\n", step, node->name);
424#endif
425 return(0);
426}
427
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000428/**
429 * xmlPatMatch:
430 * @comp: the precompiled pattern
431 * @node: a node
432 *
433 * Test wether the node matches the pattern
434 *
435 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
436 */
437static int
438xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
439 int i;
440 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000441 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000442
443 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000444 i = 0;
445restart:
446 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000447 step = &comp->steps[i];
448 switch (step->op) {
449 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000450 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000451 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000452 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000453 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000454 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000455 if ((node->type == XML_DOCUMENT_NODE) ||
456#ifdef LIBXML_DOCB_ENABLED
457 (node->type == XML_DOCB_DOCUMENT_NODE) ||
458#endif
459 (node->type == XML_HTML_DOCUMENT_NODE))
460 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000461 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000462 case XML_OP_ELEM:
463 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000464 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000465 if (step->value == NULL)
466 continue;
467 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000468 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000469 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000470 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000471
472 /* Namespace test */
473 if (node->ns == NULL) {
474 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000475 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000476 } else if (node->ns->href != NULL) {
477 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000478 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000479 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000480 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000481 }
482 continue;
483 case XML_OP_CHILD: {
484 xmlNodePtr lst;
485
486 if ((node->type != XML_ELEMENT_NODE) &&
487 (node->type != XML_DOCUMENT_NODE) &&
488#ifdef LIBXML_DOCB_ENABLED
489 (node->type != XML_DOCB_DOCUMENT_NODE) &&
490#endif
491 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000492 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000493
494 lst = node->children;
495
496 if (step->value != NULL) {
497 while (lst != NULL) {
498 if ((lst->type == XML_ELEMENT_NODE) &&
499 (step->value[0] == lst->name[0]) &&
500 (xmlStrEqual(step->value, lst->name)))
501 break;
502 lst = lst->next;
503 }
504 if (lst != NULL)
505 continue;
506 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000507 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000508 }
509 case XML_OP_ATTR:
510 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000511 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000512 if (step->value != NULL) {
513 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000514 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000515 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000516 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000517 }
518 /* Namespace test */
519 if (node->ns == NULL) {
520 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000521 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000522 } else if (step->value2 != NULL) {
523 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000524 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000525 }
526 continue;
527 case XML_OP_PARENT:
528 if ((node->type == XML_DOCUMENT_NODE) ||
529 (node->type == XML_HTML_DOCUMENT_NODE) ||
530#ifdef LIBXML_DOCB_ENABLED
531 (node->type == XML_DOCB_DOCUMENT_NODE) ||
532#endif
533 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000534 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000535 node = node->parent;
536 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000537 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000538 if (step->value == NULL)
539 continue;
540 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000541 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000542 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000543 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000544 /* Namespace test */
545 if (node->ns == NULL) {
546 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000547 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000548 } else if (node->ns->href != NULL) {
549 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000550 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000551 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000552 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000553 }
554 continue;
555 case XML_OP_ANCESTOR:
556 /* TODO: implement coalescing of ANCESTOR/NODE ops */
557 if (step->value == NULL) {
558 i++;
559 step = &comp->steps[i];
560 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000561 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000562 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000563 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000564 if (step->value == NULL)
565 return(-1);
566 }
567 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000568 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000569 if ((node->type == XML_DOCUMENT_NODE) ||
570 (node->type == XML_HTML_DOCUMENT_NODE) ||
571#ifdef LIBXML_DOCB_ENABLED
572 (node->type == XML_DOCB_DOCUMENT_NODE) ||
573#endif
574 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000575 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000576 node = node->parent;
577 while (node != NULL) {
578 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000579 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000580 if ((node->type == XML_ELEMENT_NODE) &&
581 (step->value[0] == node->name[0]) &&
582 (xmlStrEqual(step->value, node->name))) {
583 /* Namespace test */
584 if (node->ns == NULL) {
585 if (step->value2 == NULL)
586 break;
587 } else if (node->ns->href != NULL) {
588 if ((step->value2 != NULL) &&
589 (xmlStrEqual(step->value2, node->ns->href)))
590 break;
591 }
592 }
593 node = node->parent;
594 }
595 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000596 goto rollback;
597 /*
598 * prepare a potential rollback from here
599 * for ancestors of that node.
600 */
601 if (step->op == XML_OP_ANCESTOR)
602 xmlPatPushState(&states, i, node);
603 else
604 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000605 continue;
606 case XML_OP_NS:
607 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000608 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000609 if (node->ns == NULL) {
610 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000611 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000612 } else if (node->ns->href != NULL) {
613 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000614 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000615 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000616 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000617 }
618 break;
619 case XML_OP_ALL:
620 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000621 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000622 break;
623 }
624 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000625found:
626 if (states.states != NULL) {
627 /* Free the rollback states */
628 xmlFree(states.states);
629 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000630 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000631rollback:
632 /* got an error try to rollback */
633 if (states.states == NULL)
634 return(0);
635 if (states.nbstates <= 0) {
636 xmlFree(states.states);
637 return(0);
638 }
639 states.nbstates--;
640 i = states.states[states.nbstates].step;
641 node = states.states[states.nbstates].node;
642#if 0
643 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
644#endif
645 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000646}
647
648/************************************************************************
649 * *
650 * Dedicated parser for templates *
651 * *
652 ************************************************************************/
653
654#define TODO \
655 xmlGenericError(xmlGenericErrorContext, \
656 "Unimplemented block at %s:%d\n", \
657 __FILE__, __LINE__);
658#define CUR (*ctxt->cur)
659#define SKIP(val) ctxt->cur += (val)
660#define NXT(val) ctxt->cur[(val)]
661#define CUR_PTR ctxt->cur
662
663#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000664 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000665
666#define CURRENT (*ctxt->cur)
667#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
668
669
670#define PUSH(op, val, val2) \
671 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
672
673#define XSLT_ERROR(X) \
674 { xsltError(ctxt, __FILE__, __LINE__, X); \
675 ctxt->error = (X); return; }
676
677#define XSLT_ERROR0(X) \
678 { xsltError(ctxt, __FILE__, __LINE__, X); \
679 ctxt->error = (X); return(0); }
680
681#if 0
682/**
683 * xmlPatScanLiteral:
684 * @ctxt: the XPath Parser context
685 *
686 * Parse an XPath Litteral:
687 *
688 * [29] Literal ::= '"' [^"]* '"'
689 * | "'" [^']* "'"
690 *
691 * Returns the Literal parsed or NULL
692 */
693
694static xmlChar *
695xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
696 const xmlChar *q, *cur;
697 xmlChar *ret = NULL;
698 int val, len;
699
700 SKIP_BLANKS;
701 if (CUR == '"') {
702 NEXT;
703 cur = q = CUR_PTR;
704 val = xmlStringCurrentChar(NULL, cur, &len);
705 while ((IS_CHAR(val)) && (val != '"')) {
706 cur += len;
707 val = xmlStringCurrentChar(NULL, cur, &len);
708 }
709 if (!IS_CHAR(val)) {
710 ctxt->error = 1;
711 return(NULL);
712 } else {
713 ret = xmlStrndup(q, cur - q);
714 }
715 cur += len;
716 CUR_PTR = cur;
717 } else if (CUR == '\'') {
718 NEXT;
719 cur = q = CUR_PTR;
720 val = xmlStringCurrentChar(NULL, cur, &len);
721 while ((IS_CHAR(val)) && (val != '\'')) {
722 cur += len;
723 val = xmlStringCurrentChar(NULL, cur, &len);
724 }
725 if (!IS_CHAR(val)) {
726 ctxt->error = 1;
727 return(NULL);
728 } else {
729 ret = xmlStrndup(q, cur - q);
730 }
731 cur += len;
732 CUR_PTR = cur;
733 } else {
734 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
735 ctxt->error = 1;
736 return(NULL);
737 }
738 return(ret);
739}
740#endif
741
742/**
743 * xmlPatScanName:
744 * @ctxt: the XPath Parser context
745 *
746 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
747 * CombiningChar | Extender
748 *
749 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
750 *
751 * [6] Names ::= Name (S Name)*
752 *
753 * Returns the Name parsed or NULL
754 */
755
756static xmlChar *
757xmlPatScanName(xmlPatParserContextPtr ctxt) {
758 const xmlChar *q, *cur;
759 xmlChar *ret = NULL;
760 int val, len;
761
762 SKIP_BLANKS;
763
764 cur = q = CUR_PTR;
765 val = xmlStringCurrentChar(NULL, cur, &len);
766 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
767 return(NULL);
768
769 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
770 (val == '.') || (val == '-') ||
771 (val == '_') ||
772 (IS_COMBINING(val)) ||
773 (IS_EXTENDER(val))) {
774 cur += len;
775 val = xmlStringCurrentChar(NULL, cur, &len);
776 }
777 ret = xmlStrndup(q, cur - q);
778 CUR_PTR = cur;
779 return(ret);
780}
781
782/**
783 * xmlPatScanNCName:
784 * @ctxt: the XPath Parser context
785 *
786 * Parses a non qualified name
787 *
788 * Returns the Name parsed or NULL
789 */
790
791static xmlChar *
792xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
793 const xmlChar *q, *cur;
794 xmlChar *ret = NULL;
795 int val, len;
796
797 SKIP_BLANKS;
798
799 cur = q = CUR_PTR;
800 val = xmlStringCurrentChar(NULL, cur, &len);
801 if (!IS_LETTER(val) && (val != '_'))
802 return(NULL);
803
804 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
805 (val == '.') || (val == '-') ||
806 (val == '_') ||
807 (IS_COMBINING(val)) ||
808 (IS_EXTENDER(val))) {
809 cur += len;
810 val = xmlStringCurrentChar(NULL, cur, &len);
811 }
812 ret = xmlStrndup(q, cur - q);
813 CUR_PTR = cur;
814 return(ret);
815}
816
817#if 0
818/**
819 * xmlPatScanQName:
820 * @ctxt: the XPath Parser context
821 * @prefix: the place to store the prefix
822 *
823 * Parse a qualified name
824 *
825 * Returns the Name parsed or NULL
826 */
827
828static xmlChar *
829xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
830 xmlChar *ret = NULL;
831
832 *prefix = NULL;
833 ret = xmlPatScanNCName(ctxt);
834 if (CUR == ':') {
835 *prefix = ret;
836 NEXT;
837 ret = xmlPatScanNCName(ctxt);
838 }
839 return(ret);
840}
841#endif
842
843/**
844 * xmlCompileStepPattern:
845 * @ctxt: the compilation context
846 *
847 * Compile the Step Pattern and generates a precompiled
848 * form suitable for fast matching.
849 *
850 * [3] Step ::= '.' | NameTest
851 * [4] NameTest ::= QName | '*' | NCName ':' '*'
852 */
853
854static void
855xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
856 xmlChar *token = NULL;
857 xmlChar *name = NULL;
858 const xmlChar *URI = NULL;
859 xmlChar *URL = NULL;
860
861 SKIP_BLANKS;
862 if (CUR == '.') {
863 NEXT;
864 PUSH(XML_OP_ELEM, NULL, NULL);
865 return;
866 }
867 name = xmlPatScanNCName(ctxt);
868 if (name == NULL) {
869 if (CUR == '*') {
870 NEXT;
871 PUSH(XML_OP_ALL, NULL, NULL);
872 return;
873 } else {
874 ERROR(NULL, NULL, NULL,
875 "xmlCompileStepPattern : Name expected\n");
876 ctxt->error = 1;
877 return;
878 }
879 }
880 SKIP_BLANKS;
881 if (CUR == ':') {
882 NEXT;
883 if (CUR != ':') {
884 xmlChar *prefix = name;
885 xmlNsPtr ns;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000886 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000887
888 /*
889 * This is a namespace match
890 */
891 token = xmlPatScanName(ctxt);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000892 for (i = 0;i < ctxt->nb_namespaces;i++) {
893 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
894 URL = xmlStrdup(ctxt->namespaces[2 * i + 1]);
895 break;
896 }
897 }
898 if (i >= ctxt->nb_namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000899 ERROR5(NULL, NULL, NULL,
900 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
901 prefix);
902 ctxt->error = 1;
903 goto error;
904 } else {
905 URL = xmlStrdup(ns->href);
906 }
907 xmlFree(prefix);
908 if (token == NULL) {
909 if (CUR == '*') {
910 NEXT;
911 PUSH(XML_OP_NS, URL, NULL);
912 } else {
913 ERROR(NULL, NULL, NULL,
914 "xmlCompileStepPattern : Name expected\n");
915 ctxt->error = 1;
916 goto error;
917 }
918 } else {
919 PUSH(XML_OP_ELEM, token, URL);
920 }
921 } else {
922 NEXT;
923 if (xmlStrEqual(token, (const xmlChar *) "child")) {
924 xmlFree(token);
925 token = xmlPatScanName(ctxt);
926 if (token == NULL) {
927 if (CUR == '*') {
928 NEXT;
929 PUSH(XML_OP_ALL, token, NULL);
930 return;
931 } else {
932 ERROR(NULL, NULL, NULL,
933 "xmlCompileStepPattern : QName expected\n");
934 ctxt->error = 1;
935 goto error;
936 }
937 }
938 TODO
939 /* URI = xsltGetQNameURI(ctxt->elem, &token); */
940 if (token == NULL) {
941 ctxt->error = 1;
942 goto error;
943 } else {
944 name = xmlStrdup(token);
945 if (URI != NULL)
946 URL = xmlStrdup(URI);
947 }
948 PUSH(XML_OP_CHILD, name, URL);
949 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
950 xmlFree(token);
951 token = xmlPatScanName(ctxt);
952 if (token == NULL) {
953 ERROR(NULL, NULL, NULL,
954 "xmlCompileStepPattern : QName expected\n");
955 ctxt->error = 1;
956 goto error;
957 }
958 TODO
959 /* URI = xsltGetQNameURI(ctxt->elem, &token); */
960 if (token == NULL) {
961 ctxt->error = 1;
962 goto error;
963 } else {
964 name = xmlStrdup(token);
965 if (URI != NULL)
966 URL = xmlStrdup(URI);
967 }
968 PUSH(XML_OP_ATTR, name, URL);
969 } else {
970 ERROR(NULL, NULL, NULL,
971 "xmlCompileStepPattern : 'child' or 'attribute' expected\n");
972 ctxt->error = 1;
973 goto error;
974 }
975 xmlFree(token);
976 }
977 } else if (CUR == '*') {
978 NEXT;
979 PUSH(XML_OP_ALL, token, NULL);
980 } else {
981 if (name == NULL) {
982 ctxt->error = 1;
983 goto error;
984 }
985 PUSH(XML_OP_ELEM, name, NULL);
986 }
987 return;
988error:
989 if (token != NULL)
990 xmlFree(token);
991 if (name != NULL)
992 xmlFree(name);
993}
994
995/**
996 * xmlCompilePathPattern:
997 * @ctxt: the compilation context
998 *
999 * Compile the Path Pattern and generates a precompiled
1000 * form suitable for fast matching.
1001 *
1002 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1003 */
1004static void
1005xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1006 SKIP_BLANKS;
1007 if ((CUR == '/') && (NXT(1) == '/')) {
1008 /*
1009 * since we reverse the query
1010 * a leading // can be safely ignored
1011 */
1012 NEXT;
1013 NEXT;
1014 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1015 /*
1016 * a leading .// can be safely ignored
1017 */
1018 NEXT;
1019 NEXT;
1020 NEXT;
1021 }
1022 if (CUR == '@') {
1023 TODO
1024 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001025 if (CUR == '/') {
1026 PUSH(XML_OP_ROOT, NULL, NULL);
1027 NEXT;
1028 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001029 xmlCompileStepPattern(ctxt);
1030 SKIP_BLANKS;
1031 while (CUR == '/') {
1032 if ((CUR == '/') && (NXT(1) == '/')) {
1033 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1034 NEXT;
1035 NEXT;
1036 SKIP_BLANKS;
1037 xmlCompileStepPattern(ctxt);
1038 } else {
1039 PUSH(XML_OP_PARENT, NULL, NULL);
1040 NEXT;
1041 SKIP_BLANKS;
1042 if ((CUR != 0) || (CUR == '|')) {
1043 xmlCompileStepPattern(ctxt);
1044 }
1045 }
1046 }
1047 }
1048error:
1049 return;
1050}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001051
1052/************************************************************************
1053 * *
1054 * The streaming code *
1055 * *
1056 ************************************************************************/
1057
1058#ifdef DEBUG_STREAMING
1059static void
1060xmlDebugStreamComp(xmlStreamCompPtr stream) {
1061 int i;
1062
1063 if (stream == NULL) {
1064 printf("Stream: NULL\n");
1065 return;
1066 }
1067 printf("Stream: %d steps\n", stream->nbStep);
1068 for (i = 0;i < stream->nbStep;i++) {
1069 if (stream->steps[i].ns != NULL) {
1070 printf("{%s}", stream->steps[i].ns);
1071 }
1072 if (stream->steps[i].name == NULL) {
1073 printf("* ");
1074 } else {
1075 printf("%s ", stream->steps[i].name);
1076 }
1077 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1078 printf("root ");
1079 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1080 printf("// ");
1081 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1082 printf("final ");
1083 printf("\n");
1084 }
1085}
1086static void
1087xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1088 int i;
1089
1090 if (ctxt == NULL) {
1091 printf("Stream: NULL\n");
1092 return;
1093 }
1094 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1095 if (match)
1096 printf("matches\n");
1097 else
1098 printf("\n");
1099 for (i = 0;i < ctxt->nbState;i++) {
1100 if (ctxt->states[2 * i] < 0)
1101 printf(" %d: free\n", i);
1102 else {
1103 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1104 ctxt->states[(2 * i) + 1]);
1105 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1106 XML_STREAM_STEP_DESC)
1107 printf(" //\n");
1108 else
1109 printf("\n");
1110 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001111 }
1112}
1113#endif
1114/**
1115 * xmlNewStreamComp:
1116 * @size: the number of expected steps
1117 *
1118 * build a new compiled pattern for streaming
1119 *
1120 * Returns the new structure or NULL in case of error.
1121 */
1122static xmlStreamCompPtr
1123xmlNewStreamComp(int size) {
1124 xmlStreamCompPtr cur;
1125
1126 if (size < 4)
1127 size = 4;
1128
1129 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1130 if (cur == NULL) {
1131 ERROR(NULL, NULL, NULL,
1132 "xmlNewStreamComp: malloc failed\n");
1133 return(NULL);
1134 }
1135 memset(cur, 0, sizeof(xmlStreamComp));
1136 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1137 if (cur->steps == NULL) {
1138 xmlFree(cur);
1139 ERROR(NULL, NULL, NULL,
1140 "xmlNewStreamComp: malloc failed\n");
1141 return(NULL);
1142 }
1143 cur->nbStep = 0;
1144 cur->maxStep = size;
1145 return(cur);
1146}
1147
1148/**
1149 * xmlFreeStreamComp:
1150 * @comp: the compiled pattern for streaming
1151 *
1152 * Free the compiled pattern for streaming
1153 */
1154static void
1155xmlFreeStreamComp(xmlStreamCompPtr comp) {
1156 if (comp != NULL) {
1157 if (comp->steps != NULL)
1158 xmlFree(comp->steps);
1159 if (comp->dict != NULL)
1160 xmlDictFree(comp->dict);
1161 xmlFree(comp);
1162 }
1163}
1164
1165/**
1166 * xmlStreamCompAddStep:
1167 * @comp: the compiled pattern for streaming
1168 * @name: the first string, the name, or NULL for *
1169 * @ns: the second step, the namespace name
1170 * @flags: the flags for that step
1171 *
1172 * Add a new step to the compiled pattern
1173 *
1174 * Returns -1 in case of error or the step index if successful
1175 */
1176static int
1177xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1178 const xmlChar *ns, int flags) {
1179 xmlStreamStepPtr cur;
1180
1181 if (comp->nbStep >= comp->maxStep) {
1182 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1183 comp->maxStep * 2 * sizeof(xmlStreamStep));
1184 if (cur == NULL) {
1185 ERROR(NULL, NULL, NULL,
1186 "xmlNewStreamComp: malloc failed\n");
1187 return(-1);
1188 }
1189 comp->steps = cur;
1190 comp->maxStep *= 2;
1191 }
1192 cur = &comp->steps[comp->nbStep++];
1193 cur->flags = flags;
1194 cur->name = name;
1195 cur->ns = ns;
1196 return(comp->nbStep - 1);
1197}
1198
1199/**
1200 * xmlStreamCompile:
1201 * @comp: the precompiled pattern
1202 *
1203 * Tries to stream compile a pattern
1204 *
1205 * Returns -1 in case of failure and 0 in case of success.
1206 */
1207static int
1208xmlStreamCompile(xmlPatternPtr comp) {
1209 xmlStreamCompPtr stream;
1210 int i, s = 0, root = 0, desc = 0;
1211
1212 if ((comp == NULL) || (comp->steps == NULL))
1213 return(-1);
1214 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1215 if (stream == NULL)
1216 return(-1);
1217 if (comp->dict != NULL) {
1218 stream->dict = comp->dict;
1219 xmlDictReference(stream->dict);
1220 }
1221 for (i = 0;i < comp->nbStep;i++) {
1222 switch (comp->steps[i].op) {
1223 case XML_OP_END:
1224 break;
1225 case XML_OP_ROOT:
1226 if (i != 0)
1227 goto error;
1228 root = 1;
1229 break;
1230 case XML_OP_CHILD:
1231 case XML_OP_ATTR:
1232 case XML_OP_NS:
1233 goto error;
1234 case XML_OP_ELEM:
1235 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1236 comp->steps[i].value2, desc);
1237 desc = 0;
1238 if (s < 0)
1239 goto error;
1240 break;
1241 case XML_OP_ALL:
1242 s = xmlStreamCompAddStep(stream, NULL, NULL, desc);
1243 desc = 0;
1244 if (s < 0)
1245 goto error;
1246 break;
1247 case XML_OP_PARENT:
1248 break;
1249 case XML_OP_ANCESTOR:
1250 desc = XML_STREAM_STEP_DESC;
1251 break;
1252 }
1253 }
1254 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1255 if (root)
1256 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1257#ifdef DEBUG_STREAMING
1258 xmlDebugStreamComp(stream);
1259#endif
1260 comp->stream = stream;
1261 return(0);
1262error:
1263 xmlFreeStreamComp(stream);
1264 return(0);
1265}
1266
1267/**
1268 * xmlNewStreamCtxt:
1269 * @size: the number of expected states
1270 *
1271 * build a new stream context
1272 *
1273 * Returns the new structure or NULL in case of error.
1274 */
1275static xmlStreamCtxtPtr
1276xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1277 xmlStreamCtxtPtr cur;
1278
1279 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1280 if (cur == NULL) {
1281 ERROR(NULL, NULL, NULL,
1282 "xmlNewStreamCtxt: malloc failed\n");
1283 return(NULL);
1284 }
1285 memset(cur, 0, sizeof(xmlStreamCtxt));
1286 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1287 if (cur->states == NULL) {
1288 xmlFree(cur);
1289 ERROR(NULL, NULL, NULL,
1290 "xmlNewStreamCtxt: malloc failed\n");
1291 return(NULL);
1292 }
1293 cur->nbState = 0;
1294 cur->maxState = 4;
1295 cur->level = 0;
1296 cur->comp = stream;
1297 return(cur);
1298}
1299
1300/**
1301 * xmlFreeStreamCtxt:
1302 * @stream: the stream context
1303 *
1304 * Free the stream context
1305 */
1306void
1307xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
1308 if (stream != NULL) {
1309 if (stream->states != NULL)
1310 xmlFree(stream->states);
1311 xmlFree(stream);
1312 }
1313}
1314
1315/**
1316 * xmlStreamCtxtAddState:
1317 * @comp: the stream context
1318 * @idx: the step index for that streaming state
1319 *
1320 * Add a new state to the stream context
1321 *
1322 * Returns -1 in case of error or the state index if successful
1323 */
1324static int
1325xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1326 int i;
1327 for (i = 0;i < comp->nbState;i++) {
1328 if (comp->states[2 * i] < 0) {
1329 comp->states[2 * i] = idx;
1330 comp->states[2 * i + 1] = level;
1331 return(i);
1332 }
1333 }
1334 if (comp->nbState >= comp->maxState) {
1335 int *cur;
1336
1337 cur = (int *) xmlRealloc(comp->states,
1338 comp->maxState * 4 * sizeof(int));
1339 if (cur == NULL) {
1340 ERROR(NULL, NULL, NULL,
1341 "xmlNewStreamCtxt: malloc failed\n");
1342 return(-1);
1343 }
1344 comp->states = cur;
1345 comp->maxState *= 2;
1346 }
1347 comp->states[2 * comp->nbState] = idx;
1348 comp->states[2 * comp->nbState++ + 1] = level;
1349 return(comp->nbState - 1);
1350}
1351
1352/**
1353 * xmlStreamPush:
1354 * @stream: the stream context
1355 * @name: the current name
1356 * @ns: the namespace name
1357 *
1358 * push new data onto the stream. NOTE: if the call xmlPatterncompile()
1359 * indicated a dictionnary, then strings for name and ns will be expected
1360 * to come from the dictionary.
1361 * Both @name and @ns being NULL means the / i.e. the root of the document.
1362 * This can also act as a reset.
1363 *
1364 * Returns: -1 in case of error, 1 if the current state in the stream is a
1365 * match and 0 otherwise.
1366 */
1367int
1368xmlStreamPush(xmlStreamCtxtPtr stream,
1369 const xmlChar *name, const xmlChar *ns) {
Daniel Veillard16ef8002005-01-31 00:27:50 +00001370 int ret = 0, tmp, i, m, match, step, desc, final;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001371 xmlStreamCompPtr comp;
1372
1373 if ((stream == NULL) || (stream->nbState < 0))
1374 return(-1);
1375 comp = stream->comp;
1376 if ((name == NULL) && (ns == NULL)) {
1377 stream->nbState = 0;
1378 stream->level = 0;
1379 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1380 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1381 if (tmp < 0)
1382 return(-1);
1383 if (comp->steps[tmp].flags & XML_STREAM_STEP_FINAL)
1384 return(1);
1385 }
1386 return(0);
1387 }
1388 /*
1389 * Check evolution of existing states
1390 */
1391 m = stream->nbState;
1392 for (i = 0;i < m;i++) {
1393 match = 0;
1394 step = stream->states[2 * i];
1395 /* dead states */
1396 if (step < 0) continue;
1397 /* skip new states just added */
1398 if (stream->states[(2 * i) + 1] > stream->level) continue;
Daniel Veillard16ef8002005-01-31 00:27:50 +00001399 /* skip continuations */
1400 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1401 if ((stream->states[(2 * i) + 1] < stream->level) && (!desc))continue;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001402
1403 /* discard old states */
1404 /* something needed about old level discarded */
1405
1406 if (comp->dict) {
1407 if (comp->steps[step].name == NULL) {
1408 if (comp->steps[step].ns == NULL)
1409 match = 1;
1410 else
1411 match = (comp->steps[step].ns == ns);
1412 } else {
1413 match = ((comp->steps[step].name == name) &&
1414 (comp->steps[step].ns == ns));
1415 }
1416 } else {
1417 if (comp->steps[step].name == NULL) {
1418 if (comp->steps[step].ns == NULL)
1419 match = 1;
1420 else
1421 match = xmlStrEqual(comp->steps[step].ns, ns);
1422 } else {
1423 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
1424 (xmlStrEqual(comp->steps[step].ns, ns)));
1425 }
1426 }
1427 if (match) {
Daniel Veillard16ef8002005-01-31 00:27:50 +00001428 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
1429 if (desc) {
1430 if (final) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001431 ret = 1;
1432 } else {
1433 /* descending match create a new state */
1434 xmlStreamCtxtAddState(stream, step + 1, stream->level + 1);
1435 }
1436 } else {
Daniel Veillard16ef8002005-01-31 00:27:50 +00001437 if (final) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001438 ret = 1;
Daniel Veillard16ef8002005-01-31 00:27:50 +00001439#if 0
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001440 stream->states[2 * i] = -1;
Daniel Veillard16ef8002005-01-31 00:27:50 +00001441#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001442 } else {
Daniel Veillard16ef8002005-01-31 00:27:50 +00001443#if 0
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001444 stream->states[2 * i] = step + 1;
1445 stream->states[2 * i + 1] = stream->level + 1;
Daniel Veillard16ef8002005-01-31 00:27:50 +00001446#endif
1447 xmlStreamCtxtAddState(stream, step + 1, stream->level + 1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001448 }
1449 }
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001450#if 0
Daniel Veillard16ef8002005-01-31 00:27:50 +00001451 } else if (!desc) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001452 /* didn't match, discard */
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001453 stream->states[2 * i] = -1;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001454#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001455 }
1456 }
1457
1458 /*
1459 * Check creating a new state.
1460 */
1461 stream->level++;
1462 if (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT)) {
1463 match = 0;
1464 if (comp->dict) {
1465 if (comp->steps[0].name == NULL) {
1466 if (comp->steps[0].ns == NULL)
1467 match = 1;
1468 else
1469 match = (comp->steps[0].ns == ns);
1470 } else {
1471 match = ((comp->steps[0].name == name) &&
1472 (comp->steps[0].ns == ns));
1473 }
1474 } else {
1475 if (comp->steps[0].name == NULL) {
1476 if (comp->steps[0].ns == NULL)
1477 match = 1;
1478 else
1479 match = xmlStrEqual(comp->steps[0].ns, ns);
1480 } else {
1481 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1482 (xmlStrEqual(comp->steps[0].ns, ns)));
1483 }
1484 }
1485 if (match) {
1486 if (comp->steps[0].flags & XML_STREAM_STEP_FINAL)
1487 ret = 1;
1488 else
1489 xmlStreamCtxtAddState(stream, 1, stream->level);
1490 }
1491 }
1492
1493#ifdef DEBUG_STREAMING
1494 xmlDebugStreamCtxt(stream, ret);
1495#endif
1496 return(ret);
1497}
1498
1499/**
1500 * xmlStreamPop:
1501 * @stream: the stream context
1502 *
1503 * push one level from the stream.
1504 *
1505 * Returns: -1 in case of error, 0 otherwise.
1506 */
1507int
1508xmlStreamPop(xmlStreamCtxtPtr stream) {
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001509 int i, m;
1510
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001511 if (stream == NULL)
1512 return(-1);
1513 stream->level--;
1514 if (stream->level < 0)
1515 return(-1);
1516
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001517 /*
1518 * Check evolution of existing states
1519 */
1520 m = stream->nbState;
1521 for (i = 0;i < m;i++) {
1522 if (stream->states[(2 * i)] < 0) break;
1523 /* discard obsoleted states */
1524 if (stream->states[(2 * i) + 1] > stream->level)
1525 stream->states[(2 * i)] = -1;
1526 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001527 return(0);
1528}
1529
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001530/************************************************************************
1531 * *
1532 * The public interfaces *
1533 * *
1534 ************************************************************************/
1535
1536/**
1537 * xmlPatterncompile:
1538 * @pattern: the pattern to compile
1539 * @dict: an optional dictionnary for interned strings
1540 * @flags: compilation flags, undefined yet
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001541 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001542 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001543 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001544 *
1545 * Returns the compiled for of the pattern or NULL in case of error
1546 */
1547xmlPatternPtr
Daniel Veillard427174f2003-12-10 10:42:59 +00001548xmlPatterncompile(const xmlChar *pattern, xmlDict *dict,
1549 int flags ATTRIBUTE_UNUSED,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001550 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001551 xmlPatternPtr ret = NULL;
1552 xmlPatParserContextPtr ctxt = NULL;
1553
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001554 ctxt = xmlNewPatParserContext(pattern, dict, namespaces);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001555 if (ctxt == NULL) goto error;
1556 ret = xmlNewPattern();
1557 if (ret == NULL) goto error;
1558 ctxt->comp = ret;
1559
1560 xmlCompilePathPattern(ctxt);
1561 xmlFreePatParserContext(ctxt);
1562
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001563 xmlStreamCompile(ret);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +00001564 if (xmlReversePattern(ret) < 0)
1565 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001566 return(ret);
1567error:
1568 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
1569 if (ret != NULL) xmlFreePattern(ret);
1570 return(NULL);
1571}
1572
1573/**
1574 * xmlPatternMatch:
1575 * @comp: the precompiled pattern
1576 * @node: a node
1577 *
1578 * Test wether the node matches the pattern
1579 *
1580 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1581 */
1582int
1583xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
1584{
1585 if ((comp == NULL) || (node == NULL))
1586 return(-1);
1587 return(xmlPatMatch(comp, node));
1588}
1589
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001590/**
1591 * xmlPatternGetStreamCtxt:
1592 * @comp: the precompiled pattern
1593 *
1594 * Get a streaming context for that pattern
1595 * Use xmlFreeStreamCtxt to free the context.
1596 *
1597 * Returns a pointer to the context or NULL in case of failure
1598 */
1599xmlStreamCtxtPtr
1600xmlPatternGetStreamCtxt(xmlPatternPtr comp)
1601{
1602 if ((comp == NULL) || (comp->stream == NULL))
1603 return(NULL);
1604 return(xmlNewStreamCtxt(comp->stream));
1605}
1606
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001607#endif /* LIBXML_PATTERN_ENABLED */