blob: 664522470ef55d43cce03bd708427afb709e6d7f [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 Veillardf9d16912005-01-30 22:36:30 +000022 * - handling of disjunction "pattern1 | pattern2" mean needed to build
23 * and check a list internally
Daniel Veillardd4301ab2005-02-03 22:24:10 +000024 * - get rid of the "compile" starting with lowercase
25 * - get rid of the Strdup/Strndup in case of dictionary
Daniel Veillardf9d16912005-01-30 22:36:30 +000026 */
27
Daniel Veillardb3de70c2003-12-02 22:32:15 +000028#define IN_LIBXML
29#include "libxml.h"
30
31#include <string.h>
32#include <libxml/xmlmemory.h>
33#include <libxml/tree.h>
34#include <libxml/hash.h>
35#include <libxml/dict.h>
36#include <libxml/xmlerror.h>
37#include <libxml/parserInternals.h>
38#include <libxml/pattern.h>
39
Daniel Veillardd4301ab2005-02-03 22:24:10 +000040#ifdef LIBXML_PATTERN_ENABLED
Daniel Veillardb3de70c2003-12-02 22:32:15 +000041
Daniel Veillardd4301ab2005-02-03 22:24:10 +000042/* #define DEBUG_STREAMING */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000043
Daniel Veillardb3de70c2003-12-02 22:32:15 +000044#define ERROR(a, b, c, d)
45#define ERROR5(a, b, c, d, e)
46
Daniel Veillard2fc6df92005-01-30 18:42:55 +000047#define XML_STREAM_STEP_DESC 1
48#define XML_STREAM_STEP_FINAL 2
49#define XML_STREAM_STEP_ROOT 4
50
51typedef struct _xmlStreamStep xmlStreamStep;
52typedef xmlStreamStep *xmlStreamStepPtr;
53struct _xmlStreamStep {
54 int flags; /* properties of that step */
55 const xmlChar *name; /* first string value if NULL accept all */
56 const xmlChar *ns; /* second string value */
57};
58
59typedef struct _xmlStreamComp xmlStreamComp;
60typedef xmlStreamComp *xmlStreamCompPtr;
61struct _xmlStreamComp {
62 xmlDict *dict; /* the dictionnary if any */
63 int nbStep; /* number of steps in the automata */
64 int maxStep; /* allocated number of steps */
65 xmlStreamStepPtr steps; /* the array of steps */
66};
67
68struct _xmlStreamCtxt {
69 xmlStreamCompPtr comp; /* the compiled stream */
70 int nbState; /* number of state in the automata */
71 int maxState; /* allocated number of state */
72 int level; /* how deep are we ? */
73 int *states; /* the array of step indexes */
74};
75
76static void xmlFreeStreamComp(xmlStreamCompPtr comp);
77
Daniel Veillardb3de70c2003-12-02 22:32:15 +000078/*
79 * Types are private:
80 */
81
82typedef enum {
83 XML_OP_END=0,
84 XML_OP_ROOT,
85 XML_OP_ELEM,
86 XML_OP_CHILD,
87 XML_OP_ATTR,
88 XML_OP_PARENT,
89 XML_OP_ANCESTOR,
90 XML_OP_NS,
91 XML_OP_ALL
92} xmlPatOp;
93
94
Daniel Veillardd4301ab2005-02-03 22:24:10 +000095typedef struct _xmlStepState xmlStepState;
96typedef xmlStepState *xmlStepStatePtr;
97struct _xmlStepState {
98 int step;
99 xmlNodePtr node;
100};
101
102typedef struct _xmlStepStates xmlStepStates;
103typedef xmlStepStates *xmlStepStatesPtr;
104struct _xmlStepStates {
105 int nbstates;
106 int maxstates;
107 xmlStepStatePtr states;
108};
109
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000110typedef struct _xmlStepOp xmlStepOp;
111typedef xmlStepOp *xmlStepOpPtr;
112struct _xmlStepOp {
113 xmlPatOp op;
114 const xmlChar *value;
115 const xmlChar *value2;
116};
117
118struct _xmlPattern {
119 void *data; /* the associated template */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000120 xmlDictPtr dict; /* the optional dictionnary */
121 struct _xmlPattern *next; /* siblings */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000122 const xmlChar *pattern; /* the pattern */
123
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000124 int nbStep;
125 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000126 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000127 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000128};
129
130typedef struct _xmlPatParserContext xmlPatParserContext;
131typedef xmlPatParserContext *xmlPatParserContextPtr;
132struct _xmlPatParserContext {
133 const xmlChar *cur; /* the current char being parsed */
134 const xmlChar *base; /* the full expression */
135 int error; /* error code */
136 xmlDictPtr dict; /* the dictionnary if any */
137 xmlPatternPtr comp; /* the result */
138 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000139 const xmlChar **namespaces; /* the namespaces definitions */
140 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000141};
142
143/************************************************************************
144 * *
145 * Type functions *
146 * *
147 ************************************************************************/
148
149/**
150 * xmlNewPattern:
151 *
152 * Create a new XSLT Pattern
153 *
154 * Returns the newly allocated xmlPatternPtr or NULL in case of error
155 */
156static xmlPatternPtr
157xmlNewPattern(void) {
158 xmlPatternPtr cur;
159
160 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
161 if (cur == NULL) {
162 ERROR(NULL, NULL, NULL,
163 "xmlNewPattern : malloc failed\n");
164 return(NULL);
165 }
166 memset(cur, 0, sizeof(xmlPattern));
167 cur->maxStep = 10;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000168 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
169 if (cur->steps == NULL) {
170 xmlFree(cur);
171 ERROR(NULL, NULL, NULL,
172 "xmlNewPattern : malloc failed\n");
173 return(NULL);
174 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000175 return(cur);
176}
177
178/**
179 * xmlFreePattern:
180 * @comp: an XSLT comp
181 *
182 * Free up the memory allocated by @comp
183 */
184void
185xmlFreePattern(xmlPatternPtr comp) {
186 xmlStepOpPtr op;
187 int i;
188
189 if (comp == NULL)
190 return;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000191 if (comp->stream != NULL)
192 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000193 if (comp->pattern != NULL)
194 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000195 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000196 if (comp->dict == NULL) {
197 for (i = 0;i < comp->nbStep;i++) {
198 op = &comp->steps[i];
199 if (op->value != NULL)
200 xmlFree((xmlChar *) op->value);
201 if (op->value2 != NULL)
202 xmlFree((xmlChar *) op->value2);
203 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000204 }
205 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000206 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000207 if (comp->dict != NULL)
208 xmlDictFree(comp->dict);
209
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000210 memset(comp, -1, sizeof(xmlPattern));
211 xmlFree(comp);
212}
213
214/**
215 * xmlFreePatternList:
216 * @comp: an XSLT comp list
217 *
218 * Free up the memory allocated by all the elements of @comp
219 */
220void
221xmlFreePatternList(xmlPatternPtr comp) {
222 xmlPatternPtr cur;
223
224 while (comp != NULL) {
225 cur = comp;
226 comp = comp->next;
227 xmlFreePattern(cur);
228 }
229}
230
231/**
232 * xmlNewPatParserContext:
233 * @pattern: the pattern context
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000234 * @dict: the inherited dictionnary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000235 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
236 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000237 *
238 * Create a new XML pattern parser context
239 *
240 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
241 */
242static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000243xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
244 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000245 xmlPatParserContextPtr cur;
246
247 if (pattern == NULL)
248 return(NULL);
249
250 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
251 if (cur == NULL) {
252 ERROR(NULL, NULL, NULL,
253 "xmlNewPatParserContext : malloc failed\n");
254 return(NULL);
255 }
256 memset(cur, 0, sizeof(xmlPatParserContext));
257 cur->dict = dict;
258 cur->cur = pattern;
259 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000260 if (namespaces != NULL) {
261 int i;
262 for (i = 0;namespaces[2 * i] != NULL;i++);
263 cur->nb_namespaces = i;
264 } else {
265 cur->nb_namespaces = 0;
266 }
267 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000268 return(cur);
269}
270
271/**
272 * xmlFreePatParserContext:
273 * @ctxt: an XSLT parser context
274 *
275 * Free up the memory allocated by @ctxt
276 */
277static void
278xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
279 if (ctxt == NULL)
280 return;
281 memset(ctxt, -1, sizeof(xmlPatParserContext));
282 xmlFree(ctxt);
283}
284
285/**
286 * xmlPatternAdd:
287 * @comp: the compiled match expression
288 * @op: an op
289 * @value: the first value
290 * @value2: the second value
291 *
292 * Add an step to an XSLT Compiled Match
293 *
294 * Returns -1 in case of failure, 0 otherwise.
295 */
296static int
297xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
298 xmlPatternPtr comp,
299 xmlPatOp op, xmlChar * value, xmlChar * value2)
300{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000301 if (comp->nbStep >= comp->maxStep) {
302 xmlStepOpPtr temp;
303 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
304 sizeof(xmlStepOp));
305 if (temp == NULL) {
306 ERROR(ctxt, NULL, NULL,
307 "xmlPatternAdd: realloc failed\n");
308 return (-1);
309 }
310 comp->steps = temp;
311 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000312 }
313 comp->steps[comp->nbStep].op = op;
314 comp->steps[comp->nbStep].value = value;
315 comp->steps[comp->nbStep].value2 = value2;
316 comp->nbStep++;
317 return (0);
318}
319
320#if 0
321/**
322 * xsltSwapTopPattern:
323 * @comp: the compiled match expression
324 *
325 * reverse the two top steps.
326 */
327static void
328xsltSwapTopPattern(xmlPatternPtr comp) {
329 int i;
330 int j = comp->nbStep - 1;
331
332 if (j > 0) {
333 register const xmlChar *tmp;
334 register xmlPatOp op;
335 i = j - 1;
336 tmp = comp->steps[i].value;
337 comp->steps[i].value = comp->steps[j].value;
338 comp->steps[j].value = tmp;
339 tmp = comp->steps[i].value2;
340 comp->steps[i].value2 = comp->steps[j].value2;
341 comp->steps[j].value2 = tmp;
342 op = comp->steps[i].op;
343 comp->steps[i].op = comp->steps[j].op;
344 comp->steps[j].op = op;
345 }
346}
347#endif
348
349/**
350 * xmlReversePattern:
351 * @comp: the compiled match expression
352 *
353 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000354 *
355 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000356 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000357static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000358xmlReversePattern(xmlPatternPtr comp) {
359 int i = 0;
360 int j = comp->nbStep - 1;
361
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000362 if (comp->nbStep >= comp->maxStep) {
363 xmlStepOpPtr temp;
364 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
365 sizeof(xmlStepOp));
366 if (temp == NULL) {
367 ERROR(ctxt, NULL, NULL,
368 "xmlReversePattern: realloc failed\n");
369 return (-1);
370 }
371 comp->steps = temp;
372 comp->maxStep *= 2;
373 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000374 while (j > i) {
375 register const xmlChar *tmp;
376 register xmlPatOp op;
377 tmp = comp->steps[i].value;
378 comp->steps[i].value = comp->steps[j].value;
379 comp->steps[j].value = tmp;
380 tmp = comp->steps[i].value2;
381 comp->steps[i].value2 = comp->steps[j].value2;
382 comp->steps[j].value2 = tmp;
383 op = comp->steps[i].op;
384 comp->steps[i].op = comp->steps[j].op;
385 comp->steps[j].op = op;
386 j--;
387 i++;
388 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000389 comp->steps[comp->nbStep].value = NULL;
390 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000391 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000392 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000393}
394
395/************************************************************************
396 * *
397 * The interpreter for the precompiled patterns *
398 * *
399 ************************************************************************/
400
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000401static int
402xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
403 if ((states->states == NULL) || (states->maxstates <= 0)) {
404 states->maxstates = 4;
405 states->nbstates = 0;
406 states->states = xmlMalloc(4 * sizeof(xmlStepState));
407 }
408 else if (states->maxstates <= states->nbstates) {
409 xmlStepState *tmp;
410
411 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
412 2 * states->maxstates * sizeof(xmlStepState));
413 if (tmp == NULL)
414 return(-1);
415 states->states = tmp;
416 states->maxstates *= 2;
417 }
418 states->states[states->nbstates].step = step;
419 states->states[states->nbstates++].node = node;
420#if 0
421 fprintf(stderr, "Push: %d, %s\n", step, node->name);
422#endif
423 return(0);
424}
425
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000426/**
427 * xmlPatMatch:
428 * @comp: the precompiled pattern
429 * @node: a node
430 *
431 * Test wether the node matches the pattern
432 *
433 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
434 */
435static int
436xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
437 int i;
438 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000439 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000440
441 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000442 i = 0;
443restart:
444 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000445 step = &comp->steps[i];
446 switch (step->op) {
447 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000448 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000449 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000450 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000451 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000452 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000453 if ((node->type == XML_DOCUMENT_NODE) ||
454#ifdef LIBXML_DOCB_ENABLED
455 (node->type == XML_DOCB_DOCUMENT_NODE) ||
456#endif
457 (node->type == XML_HTML_DOCUMENT_NODE))
458 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000459 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000460 case XML_OP_ELEM:
461 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000462 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000463 if (step->value == NULL)
464 continue;
465 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000466 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000467 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000468 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000469
470 /* Namespace test */
471 if (node->ns == NULL) {
472 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000473 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000474 } else if (node->ns->href != NULL) {
475 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000476 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000477 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000478 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000479 }
480 continue;
481 case XML_OP_CHILD: {
482 xmlNodePtr lst;
483
484 if ((node->type != XML_ELEMENT_NODE) &&
485 (node->type != XML_DOCUMENT_NODE) &&
486#ifdef LIBXML_DOCB_ENABLED
487 (node->type != XML_DOCB_DOCUMENT_NODE) &&
488#endif
489 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000490 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000491
492 lst = node->children;
493
494 if (step->value != NULL) {
495 while (lst != NULL) {
496 if ((lst->type == XML_ELEMENT_NODE) &&
497 (step->value[0] == lst->name[0]) &&
498 (xmlStrEqual(step->value, lst->name)))
499 break;
500 lst = lst->next;
501 }
502 if (lst != NULL)
503 continue;
504 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000505 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000506 }
507 case XML_OP_ATTR:
508 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000509 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000510 if (step->value != NULL) {
511 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000512 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000513 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000514 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000515 }
516 /* Namespace test */
517 if (node->ns == NULL) {
518 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000519 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000520 } else if (step->value2 != NULL) {
521 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000522 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000523 }
524 continue;
525 case XML_OP_PARENT:
526 if ((node->type == XML_DOCUMENT_NODE) ||
527 (node->type == XML_HTML_DOCUMENT_NODE) ||
528#ifdef LIBXML_DOCB_ENABLED
529 (node->type == XML_DOCB_DOCUMENT_NODE) ||
530#endif
531 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000532 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000533 node = node->parent;
534 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000535 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000536 if (step->value == NULL)
537 continue;
538 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000539 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000540 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000541 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000542 /* Namespace test */
543 if (node->ns == NULL) {
544 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000545 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000546 } else if (node->ns->href != NULL) {
547 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000548 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000549 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000550 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000551 }
552 continue;
553 case XML_OP_ANCESTOR:
554 /* TODO: implement coalescing of ANCESTOR/NODE ops */
555 if (step->value == NULL) {
556 i++;
557 step = &comp->steps[i];
558 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000559 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000560 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000561 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000562 if (step->value == NULL)
563 return(-1);
564 }
565 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000566 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000567 if ((node->type == XML_DOCUMENT_NODE) ||
568 (node->type == XML_HTML_DOCUMENT_NODE) ||
569#ifdef LIBXML_DOCB_ENABLED
570 (node->type == XML_DOCB_DOCUMENT_NODE) ||
571#endif
572 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000573 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000574 node = node->parent;
575 while (node != NULL) {
576 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000577 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000578 if ((node->type == XML_ELEMENT_NODE) &&
579 (step->value[0] == node->name[0]) &&
580 (xmlStrEqual(step->value, node->name))) {
581 /* Namespace test */
582 if (node->ns == NULL) {
583 if (step->value2 == NULL)
584 break;
585 } else if (node->ns->href != NULL) {
586 if ((step->value2 != NULL) &&
587 (xmlStrEqual(step->value2, node->ns->href)))
588 break;
589 }
590 }
591 node = node->parent;
592 }
593 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000594 goto rollback;
595 /*
596 * prepare a potential rollback from here
597 * for ancestors of that node.
598 */
599 if (step->op == XML_OP_ANCESTOR)
600 xmlPatPushState(&states, i, node);
601 else
602 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000603 continue;
604 case XML_OP_NS:
605 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000606 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000607 if (node->ns == NULL) {
608 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000609 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000610 } else if (node->ns->href != NULL) {
611 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000612 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000613 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000614 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000615 }
616 break;
617 case XML_OP_ALL:
618 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000619 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000620 break;
621 }
622 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000623found:
624 if (states.states != NULL) {
625 /* Free the rollback states */
626 xmlFree(states.states);
627 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000628 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000629rollback:
630 /* got an error try to rollback */
631 if (states.states == NULL)
632 return(0);
633 if (states.nbstates <= 0) {
634 xmlFree(states.states);
635 return(0);
636 }
637 states.nbstates--;
638 i = states.states[states.nbstates].step;
639 node = states.states[states.nbstates].node;
640#if 0
641 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
642#endif
643 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000644}
645
646/************************************************************************
647 * *
648 * Dedicated parser for templates *
649 * *
650 ************************************************************************/
651
652#define TODO \
653 xmlGenericError(xmlGenericErrorContext, \
654 "Unimplemented block at %s:%d\n", \
655 __FILE__, __LINE__);
656#define CUR (*ctxt->cur)
657#define SKIP(val) ctxt->cur += (val)
658#define NXT(val) ctxt->cur[(val)]
659#define CUR_PTR ctxt->cur
660
661#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000662 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000663
664#define CURRENT (*ctxt->cur)
665#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
666
667
668#define PUSH(op, val, val2) \
669 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
670
671#define XSLT_ERROR(X) \
672 { xsltError(ctxt, __FILE__, __LINE__, X); \
673 ctxt->error = (X); return; }
674
675#define XSLT_ERROR0(X) \
676 { xsltError(ctxt, __FILE__, __LINE__, X); \
677 ctxt->error = (X); return(0); }
678
679#if 0
680/**
681 * xmlPatScanLiteral:
682 * @ctxt: the XPath Parser context
683 *
684 * Parse an XPath Litteral:
685 *
686 * [29] Literal ::= '"' [^"]* '"'
687 * | "'" [^']* "'"
688 *
689 * Returns the Literal parsed or NULL
690 */
691
692static xmlChar *
693xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
694 const xmlChar *q, *cur;
695 xmlChar *ret = NULL;
696 int val, len;
697
698 SKIP_BLANKS;
699 if (CUR == '"') {
700 NEXT;
701 cur = q = CUR_PTR;
702 val = xmlStringCurrentChar(NULL, cur, &len);
703 while ((IS_CHAR(val)) && (val != '"')) {
704 cur += len;
705 val = xmlStringCurrentChar(NULL, cur, &len);
706 }
707 if (!IS_CHAR(val)) {
708 ctxt->error = 1;
709 return(NULL);
710 } else {
711 ret = xmlStrndup(q, cur - q);
712 }
713 cur += len;
714 CUR_PTR = cur;
715 } else if (CUR == '\'') {
716 NEXT;
717 cur = q = CUR_PTR;
718 val = xmlStringCurrentChar(NULL, cur, &len);
719 while ((IS_CHAR(val)) && (val != '\'')) {
720 cur += len;
721 val = xmlStringCurrentChar(NULL, cur, &len);
722 }
723 if (!IS_CHAR(val)) {
724 ctxt->error = 1;
725 return(NULL);
726 } else {
727 ret = xmlStrndup(q, cur - q);
728 }
729 cur += len;
730 CUR_PTR = cur;
731 } else {
732 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
733 ctxt->error = 1;
734 return(NULL);
735 }
736 return(ret);
737}
738#endif
739
740/**
741 * xmlPatScanName:
742 * @ctxt: the XPath Parser context
743 *
744 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
745 * CombiningChar | Extender
746 *
747 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
748 *
749 * [6] Names ::= Name (S Name)*
750 *
751 * Returns the Name parsed or NULL
752 */
753
754static xmlChar *
755xmlPatScanName(xmlPatParserContextPtr ctxt) {
756 const xmlChar *q, *cur;
757 xmlChar *ret = NULL;
758 int val, len;
759
760 SKIP_BLANKS;
761
762 cur = q = CUR_PTR;
763 val = xmlStringCurrentChar(NULL, cur, &len);
764 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
765 return(NULL);
766
767 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
768 (val == '.') || (val == '-') ||
769 (val == '_') ||
770 (IS_COMBINING(val)) ||
771 (IS_EXTENDER(val))) {
772 cur += len;
773 val = xmlStringCurrentChar(NULL, cur, &len);
774 }
775 ret = xmlStrndup(q, cur - q);
776 CUR_PTR = cur;
777 return(ret);
778}
779
780/**
781 * xmlPatScanNCName:
782 * @ctxt: the XPath Parser context
783 *
784 * Parses a non qualified name
785 *
786 * Returns the Name parsed or NULL
787 */
788
789static xmlChar *
790xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
791 const xmlChar *q, *cur;
792 xmlChar *ret = NULL;
793 int val, len;
794
795 SKIP_BLANKS;
796
797 cur = q = CUR_PTR;
798 val = xmlStringCurrentChar(NULL, cur, &len);
799 if (!IS_LETTER(val) && (val != '_'))
800 return(NULL);
801
802 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
803 (val == '.') || (val == '-') ||
804 (val == '_') ||
805 (IS_COMBINING(val)) ||
806 (IS_EXTENDER(val))) {
807 cur += len;
808 val = xmlStringCurrentChar(NULL, cur, &len);
809 }
810 ret = xmlStrndup(q, cur - q);
811 CUR_PTR = cur;
812 return(ret);
813}
814
815#if 0
816/**
817 * xmlPatScanQName:
818 * @ctxt: the XPath Parser context
819 * @prefix: the place to store the prefix
820 *
821 * Parse a qualified name
822 *
823 * Returns the Name parsed or NULL
824 */
825
826static xmlChar *
827xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
828 xmlChar *ret = NULL;
829
830 *prefix = NULL;
831 ret = xmlPatScanNCName(ctxt);
832 if (CUR == ':') {
833 *prefix = ret;
834 NEXT;
835 ret = xmlPatScanNCName(ctxt);
836 }
837 return(ret);
838}
839#endif
840
841/**
842 * xmlCompileStepPattern:
843 * @ctxt: the compilation context
844 *
845 * Compile the Step Pattern and generates a precompiled
846 * form suitable for fast matching.
847 *
848 * [3] Step ::= '.' | NameTest
849 * [4] NameTest ::= QName | '*' | NCName ':' '*'
850 */
851
852static void
853xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
854 xmlChar *token = NULL;
855 xmlChar *name = NULL;
856 const xmlChar *URI = NULL;
857 xmlChar *URL = NULL;
858
859 SKIP_BLANKS;
860 if (CUR == '.') {
861 NEXT;
862 PUSH(XML_OP_ELEM, NULL, NULL);
863 return;
864 }
865 name = xmlPatScanNCName(ctxt);
866 if (name == NULL) {
867 if (CUR == '*') {
868 NEXT;
869 PUSH(XML_OP_ALL, NULL, NULL);
870 return;
871 } else {
872 ERROR(NULL, NULL, NULL,
873 "xmlCompileStepPattern : Name expected\n");
874 ctxt->error = 1;
875 return;
876 }
877 }
878 SKIP_BLANKS;
879 if (CUR == ':') {
880 NEXT;
881 if (CUR != ':') {
882 xmlChar *prefix = name;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000883 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000884
885 /*
886 * This is a namespace match
887 */
888 token = xmlPatScanName(ctxt);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000889 for (i = 0;i < ctxt->nb_namespaces;i++) {
890 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Daniel Veillard0996a162005-02-05 14:00:10 +0000891 URL = xmlStrdup(ctxt->namespaces[2 * i]);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000892 break;
893 }
894 }
895 if (i >= ctxt->nb_namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000896 ERROR5(NULL, NULL, NULL,
897 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
898 prefix);
899 ctxt->error = 1;
900 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000901 }
902 xmlFree(prefix);
903 if (token == NULL) {
904 if (CUR == '*') {
905 NEXT;
906 PUSH(XML_OP_NS, URL, NULL);
907 } else {
908 ERROR(NULL, NULL, NULL,
909 "xmlCompileStepPattern : Name expected\n");
910 ctxt->error = 1;
911 goto error;
912 }
913 } else {
914 PUSH(XML_OP_ELEM, token, URL);
915 }
916 } else {
917 NEXT;
918 if (xmlStrEqual(token, (const xmlChar *) "child")) {
919 xmlFree(token);
920 token = xmlPatScanName(ctxt);
921 if (token == NULL) {
922 if (CUR == '*') {
923 NEXT;
924 PUSH(XML_OP_ALL, token, NULL);
925 return;
926 } else {
927 ERROR(NULL, NULL, NULL,
928 "xmlCompileStepPattern : QName expected\n");
929 ctxt->error = 1;
930 goto error;
931 }
932 }
933 TODO
934 /* URI = xsltGetQNameURI(ctxt->elem, &token); */
935 if (token == NULL) {
936 ctxt->error = 1;
937 goto error;
938 } else {
939 name = xmlStrdup(token);
940 if (URI != NULL)
941 URL = xmlStrdup(URI);
942 }
943 PUSH(XML_OP_CHILD, name, URL);
944 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
945 xmlFree(token);
946 token = xmlPatScanName(ctxt);
947 if (token == NULL) {
948 ERROR(NULL, NULL, NULL,
949 "xmlCompileStepPattern : QName expected\n");
950 ctxt->error = 1;
951 goto error;
952 }
953 TODO
954 /* URI = xsltGetQNameURI(ctxt->elem, &token); */
955 if (token == NULL) {
956 ctxt->error = 1;
957 goto error;
958 } else {
959 name = xmlStrdup(token);
960 if (URI != NULL)
961 URL = xmlStrdup(URI);
962 }
963 PUSH(XML_OP_ATTR, name, URL);
964 } else {
965 ERROR(NULL, NULL, NULL,
966 "xmlCompileStepPattern : 'child' or 'attribute' expected\n");
967 ctxt->error = 1;
968 goto error;
969 }
970 xmlFree(token);
971 }
972 } else if (CUR == '*') {
973 NEXT;
974 PUSH(XML_OP_ALL, token, NULL);
975 } else {
976 if (name == NULL) {
977 ctxt->error = 1;
978 goto error;
979 }
980 PUSH(XML_OP_ELEM, name, NULL);
981 }
982 return;
983error:
984 if (token != NULL)
985 xmlFree(token);
986 if (name != NULL)
987 xmlFree(name);
988}
989
990/**
991 * xmlCompilePathPattern:
992 * @ctxt: the compilation context
993 *
994 * Compile the Path Pattern and generates a precompiled
995 * form suitable for fast matching.
996 *
997 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
998 */
999static void
1000xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1001 SKIP_BLANKS;
1002 if ((CUR == '/') && (NXT(1) == '/')) {
1003 /*
1004 * since we reverse the query
1005 * a leading // can be safely ignored
1006 */
1007 NEXT;
1008 NEXT;
1009 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1010 /*
1011 * a leading .// can be safely ignored
1012 */
1013 NEXT;
1014 NEXT;
1015 NEXT;
1016 }
1017 if (CUR == '@') {
1018 TODO
1019 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001020 if (CUR == '/') {
1021 PUSH(XML_OP_ROOT, NULL, NULL);
1022 NEXT;
1023 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001024 xmlCompileStepPattern(ctxt);
1025 SKIP_BLANKS;
1026 while (CUR == '/') {
1027 if ((CUR == '/') && (NXT(1) == '/')) {
1028 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1029 NEXT;
1030 NEXT;
1031 SKIP_BLANKS;
1032 xmlCompileStepPattern(ctxt);
1033 } else {
1034 PUSH(XML_OP_PARENT, NULL, NULL);
1035 NEXT;
1036 SKIP_BLANKS;
1037 if ((CUR != 0) || (CUR == '|')) {
1038 xmlCompileStepPattern(ctxt);
1039 }
1040 }
1041 }
1042 }
1043error:
1044 return;
1045}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001046
1047/************************************************************************
1048 * *
1049 * The streaming code *
1050 * *
1051 ************************************************************************/
1052
1053#ifdef DEBUG_STREAMING
1054static void
1055xmlDebugStreamComp(xmlStreamCompPtr stream) {
1056 int i;
1057
1058 if (stream == NULL) {
1059 printf("Stream: NULL\n");
1060 return;
1061 }
1062 printf("Stream: %d steps\n", stream->nbStep);
1063 for (i = 0;i < stream->nbStep;i++) {
1064 if (stream->steps[i].ns != NULL) {
1065 printf("{%s}", stream->steps[i].ns);
1066 }
1067 if (stream->steps[i].name == NULL) {
1068 printf("* ");
1069 } else {
1070 printf("%s ", stream->steps[i].name);
1071 }
1072 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1073 printf("root ");
1074 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1075 printf("// ");
1076 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1077 printf("final ");
1078 printf("\n");
1079 }
1080}
1081static void
1082xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1083 int i;
1084
1085 if (ctxt == NULL) {
1086 printf("Stream: NULL\n");
1087 return;
1088 }
1089 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1090 if (match)
1091 printf("matches\n");
1092 else
1093 printf("\n");
1094 for (i = 0;i < ctxt->nbState;i++) {
1095 if (ctxt->states[2 * i] < 0)
1096 printf(" %d: free\n", i);
1097 else {
1098 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1099 ctxt->states[(2 * i) + 1]);
1100 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1101 XML_STREAM_STEP_DESC)
1102 printf(" //\n");
1103 else
1104 printf("\n");
1105 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001106 }
1107}
1108#endif
1109/**
1110 * xmlNewStreamComp:
1111 * @size: the number of expected steps
1112 *
1113 * build a new compiled pattern for streaming
1114 *
1115 * Returns the new structure or NULL in case of error.
1116 */
1117static xmlStreamCompPtr
1118xmlNewStreamComp(int size) {
1119 xmlStreamCompPtr cur;
1120
1121 if (size < 4)
1122 size = 4;
1123
1124 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1125 if (cur == NULL) {
1126 ERROR(NULL, NULL, NULL,
1127 "xmlNewStreamComp: malloc failed\n");
1128 return(NULL);
1129 }
1130 memset(cur, 0, sizeof(xmlStreamComp));
1131 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1132 if (cur->steps == NULL) {
1133 xmlFree(cur);
1134 ERROR(NULL, NULL, NULL,
1135 "xmlNewStreamComp: malloc failed\n");
1136 return(NULL);
1137 }
1138 cur->nbStep = 0;
1139 cur->maxStep = size;
1140 return(cur);
1141}
1142
1143/**
1144 * xmlFreeStreamComp:
1145 * @comp: the compiled pattern for streaming
1146 *
1147 * Free the compiled pattern for streaming
1148 */
1149static void
1150xmlFreeStreamComp(xmlStreamCompPtr comp) {
1151 if (comp != NULL) {
1152 if (comp->steps != NULL)
1153 xmlFree(comp->steps);
1154 if (comp->dict != NULL)
1155 xmlDictFree(comp->dict);
1156 xmlFree(comp);
1157 }
1158}
1159
1160/**
1161 * xmlStreamCompAddStep:
1162 * @comp: the compiled pattern for streaming
1163 * @name: the first string, the name, or NULL for *
1164 * @ns: the second step, the namespace name
1165 * @flags: the flags for that step
1166 *
1167 * Add a new step to the compiled pattern
1168 *
1169 * Returns -1 in case of error or the step index if successful
1170 */
1171static int
1172xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1173 const xmlChar *ns, int flags) {
1174 xmlStreamStepPtr cur;
1175
1176 if (comp->nbStep >= comp->maxStep) {
1177 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1178 comp->maxStep * 2 * sizeof(xmlStreamStep));
1179 if (cur == NULL) {
1180 ERROR(NULL, NULL, NULL,
1181 "xmlNewStreamComp: malloc failed\n");
1182 return(-1);
1183 }
1184 comp->steps = cur;
1185 comp->maxStep *= 2;
1186 }
1187 cur = &comp->steps[comp->nbStep++];
1188 cur->flags = flags;
1189 cur->name = name;
1190 cur->ns = ns;
1191 return(comp->nbStep - 1);
1192}
1193
1194/**
1195 * xmlStreamCompile:
1196 * @comp: the precompiled pattern
1197 *
1198 * Tries to stream compile a pattern
1199 *
1200 * Returns -1 in case of failure and 0 in case of success.
1201 */
1202static int
1203xmlStreamCompile(xmlPatternPtr comp) {
1204 xmlStreamCompPtr stream;
1205 int i, s = 0, root = 0, desc = 0;
1206
1207 if ((comp == NULL) || (comp->steps == NULL))
1208 return(-1);
1209 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1210 if (stream == NULL)
1211 return(-1);
1212 if (comp->dict != NULL) {
1213 stream->dict = comp->dict;
1214 xmlDictReference(stream->dict);
1215 }
1216 for (i = 0;i < comp->nbStep;i++) {
1217 switch (comp->steps[i].op) {
1218 case XML_OP_END:
1219 break;
1220 case XML_OP_ROOT:
1221 if (i != 0)
1222 goto error;
1223 root = 1;
1224 break;
1225 case XML_OP_CHILD:
1226 case XML_OP_ATTR:
1227 case XML_OP_NS:
1228 goto error;
1229 case XML_OP_ELEM:
1230 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1231 comp->steps[i].value2, desc);
1232 desc = 0;
1233 if (s < 0)
1234 goto error;
1235 break;
1236 case XML_OP_ALL:
1237 s = xmlStreamCompAddStep(stream, NULL, NULL, desc);
1238 desc = 0;
1239 if (s < 0)
1240 goto error;
1241 break;
1242 case XML_OP_PARENT:
1243 break;
1244 case XML_OP_ANCESTOR:
1245 desc = XML_STREAM_STEP_DESC;
1246 break;
1247 }
1248 }
1249 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1250 if (root)
1251 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1252#ifdef DEBUG_STREAMING
1253 xmlDebugStreamComp(stream);
1254#endif
1255 comp->stream = stream;
1256 return(0);
1257error:
1258 xmlFreeStreamComp(stream);
1259 return(0);
1260}
1261
1262/**
1263 * xmlNewStreamCtxt:
1264 * @size: the number of expected states
1265 *
1266 * build a new stream context
1267 *
1268 * Returns the new structure or NULL in case of error.
1269 */
1270static xmlStreamCtxtPtr
1271xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1272 xmlStreamCtxtPtr cur;
1273
1274 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1275 if (cur == NULL) {
1276 ERROR(NULL, NULL, NULL,
1277 "xmlNewStreamCtxt: malloc failed\n");
1278 return(NULL);
1279 }
1280 memset(cur, 0, sizeof(xmlStreamCtxt));
1281 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1282 if (cur->states == NULL) {
1283 xmlFree(cur);
1284 ERROR(NULL, NULL, NULL,
1285 "xmlNewStreamCtxt: malloc failed\n");
1286 return(NULL);
1287 }
1288 cur->nbState = 0;
1289 cur->maxState = 4;
1290 cur->level = 0;
1291 cur->comp = stream;
1292 return(cur);
1293}
1294
1295/**
1296 * xmlFreeStreamCtxt:
1297 * @stream: the stream context
1298 *
1299 * Free the stream context
1300 */
1301void
1302xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
1303 if (stream != NULL) {
1304 if (stream->states != NULL)
1305 xmlFree(stream->states);
1306 xmlFree(stream);
1307 }
1308}
1309
1310/**
1311 * xmlStreamCtxtAddState:
1312 * @comp: the stream context
1313 * @idx: the step index for that streaming state
1314 *
1315 * Add a new state to the stream context
1316 *
1317 * Returns -1 in case of error or the state index if successful
1318 */
1319static int
1320xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1321 int i;
1322 for (i = 0;i < comp->nbState;i++) {
1323 if (comp->states[2 * i] < 0) {
1324 comp->states[2 * i] = idx;
1325 comp->states[2 * i + 1] = level;
1326 return(i);
1327 }
1328 }
1329 if (comp->nbState >= comp->maxState) {
1330 int *cur;
1331
1332 cur = (int *) xmlRealloc(comp->states,
1333 comp->maxState * 4 * sizeof(int));
1334 if (cur == NULL) {
1335 ERROR(NULL, NULL, NULL,
1336 "xmlNewStreamCtxt: malloc failed\n");
1337 return(-1);
1338 }
1339 comp->states = cur;
1340 comp->maxState *= 2;
1341 }
1342 comp->states[2 * comp->nbState] = idx;
1343 comp->states[2 * comp->nbState++ + 1] = level;
1344 return(comp->nbState - 1);
1345}
1346
1347/**
1348 * xmlStreamPush:
1349 * @stream: the stream context
1350 * @name: the current name
1351 * @ns: the namespace name
1352 *
1353 * push new data onto the stream. NOTE: if the call xmlPatterncompile()
1354 * indicated a dictionnary, then strings for name and ns will be expected
1355 * to come from the dictionary.
1356 * Both @name and @ns being NULL means the / i.e. the root of the document.
1357 * This can also act as a reset.
1358 *
1359 * Returns: -1 in case of error, 1 if the current state in the stream is a
1360 * match and 0 otherwise.
1361 */
1362int
1363xmlStreamPush(xmlStreamCtxtPtr stream,
1364 const xmlChar *name, const xmlChar *ns) {
Daniel Veillard16ef8002005-01-31 00:27:50 +00001365 int ret = 0, tmp, i, m, match, step, desc, final;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001366 xmlStreamCompPtr comp;
1367
1368 if ((stream == NULL) || (stream->nbState < 0))
1369 return(-1);
1370 comp = stream->comp;
1371 if ((name == NULL) && (ns == NULL)) {
1372 stream->nbState = 0;
1373 stream->level = 0;
1374 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1375 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1376 if (tmp < 0)
1377 return(-1);
1378 if (comp->steps[tmp].flags & XML_STREAM_STEP_FINAL)
1379 return(1);
1380 }
1381 return(0);
1382 }
1383 /*
1384 * Check evolution of existing states
1385 */
1386 m = stream->nbState;
1387 for (i = 0;i < m;i++) {
1388 match = 0;
1389 step = stream->states[2 * i];
1390 /* dead states */
1391 if (step < 0) continue;
1392 /* skip new states just added */
1393 if (stream->states[(2 * i) + 1] > stream->level) continue;
Daniel Veillard16ef8002005-01-31 00:27:50 +00001394 /* skip continuations */
1395 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1396 if ((stream->states[(2 * i) + 1] < stream->level) && (!desc))continue;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001397
1398 /* discard old states */
1399 /* something needed about old level discarded */
1400
1401 if (comp->dict) {
1402 if (comp->steps[step].name == NULL) {
1403 if (comp->steps[step].ns == NULL)
1404 match = 1;
1405 else
1406 match = (comp->steps[step].ns == ns);
1407 } else {
1408 match = ((comp->steps[step].name == name) &&
1409 (comp->steps[step].ns == ns));
1410 }
1411 } else {
1412 if (comp->steps[step].name == NULL) {
1413 if (comp->steps[step].ns == NULL)
1414 match = 1;
1415 else
1416 match = xmlStrEqual(comp->steps[step].ns, ns);
1417 } else {
1418 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
1419 (xmlStrEqual(comp->steps[step].ns, ns)));
1420 }
1421 }
1422 if (match) {
Daniel Veillard16ef8002005-01-31 00:27:50 +00001423 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
1424 if (desc) {
1425 if (final) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001426 ret = 1;
1427 } else {
1428 /* descending match create a new state */
1429 xmlStreamCtxtAddState(stream, step + 1, stream->level + 1);
1430 }
1431 } else {
Daniel Veillard16ef8002005-01-31 00:27:50 +00001432 if (final) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001433 ret = 1;
Daniel Veillard16ef8002005-01-31 00:27:50 +00001434#if 0
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001435 stream->states[2 * i] = -1;
Daniel Veillard16ef8002005-01-31 00:27:50 +00001436#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001437 } else {
Daniel Veillard16ef8002005-01-31 00:27:50 +00001438#if 0
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001439 stream->states[2 * i] = step + 1;
1440 stream->states[2 * i + 1] = stream->level + 1;
Daniel Veillard16ef8002005-01-31 00:27:50 +00001441#endif
1442 xmlStreamCtxtAddState(stream, step + 1, stream->level + 1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001443 }
1444 }
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001445#if 0
Daniel Veillard16ef8002005-01-31 00:27:50 +00001446 } else if (!desc) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001447 /* didn't match, discard */
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001448 stream->states[2 * i] = -1;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001449#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001450 }
1451 }
1452
1453 /*
1454 * Check creating a new state.
1455 */
1456 stream->level++;
1457 if (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT)) {
1458 match = 0;
1459 if (comp->dict) {
1460 if (comp->steps[0].name == NULL) {
1461 if (comp->steps[0].ns == NULL)
1462 match = 1;
1463 else
1464 match = (comp->steps[0].ns == ns);
1465 } else {
1466 match = ((comp->steps[0].name == name) &&
1467 (comp->steps[0].ns == ns));
1468 }
1469 } else {
1470 if (comp->steps[0].name == NULL) {
1471 if (comp->steps[0].ns == NULL)
1472 match = 1;
1473 else
1474 match = xmlStrEqual(comp->steps[0].ns, ns);
1475 } else {
1476 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1477 (xmlStrEqual(comp->steps[0].ns, ns)));
1478 }
1479 }
1480 if (match) {
1481 if (comp->steps[0].flags & XML_STREAM_STEP_FINAL)
1482 ret = 1;
1483 else
1484 xmlStreamCtxtAddState(stream, 1, stream->level);
1485 }
1486 }
1487
1488#ifdef DEBUG_STREAMING
1489 xmlDebugStreamCtxt(stream, ret);
1490#endif
1491 return(ret);
1492}
1493
1494/**
1495 * xmlStreamPop:
1496 * @stream: the stream context
1497 *
1498 * push one level from the stream.
1499 *
1500 * Returns: -1 in case of error, 0 otherwise.
1501 */
1502int
1503xmlStreamPop(xmlStreamCtxtPtr stream) {
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001504 int i, m;
1505
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001506 if (stream == NULL)
1507 return(-1);
1508 stream->level--;
1509 if (stream->level < 0)
1510 return(-1);
1511
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001512 /*
1513 * Check evolution of existing states
1514 */
1515 m = stream->nbState;
1516 for (i = 0;i < m;i++) {
1517 if (stream->states[(2 * i)] < 0) break;
1518 /* discard obsoleted states */
1519 if (stream->states[(2 * i) + 1] > stream->level)
1520 stream->states[(2 * i)] = -1;
1521 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001522 return(0);
1523}
1524
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001525/************************************************************************
1526 * *
1527 * The public interfaces *
1528 * *
1529 ************************************************************************/
1530
1531/**
1532 * xmlPatterncompile:
1533 * @pattern: the pattern to compile
1534 * @dict: an optional dictionnary for interned strings
1535 * @flags: compilation flags, undefined yet
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001536 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001537 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001538 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001539 *
1540 * Returns the compiled for of the pattern or NULL in case of error
1541 */
1542xmlPatternPtr
Daniel Veillard427174f2003-12-10 10:42:59 +00001543xmlPatterncompile(const xmlChar *pattern, xmlDict *dict,
1544 int flags ATTRIBUTE_UNUSED,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001545 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001546 xmlPatternPtr ret = NULL;
1547 xmlPatParserContextPtr ctxt = NULL;
1548
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001549 ctxt = xmlNewPatParserContext(pattern, dict, namespaces);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001550 if (ctxt == NULL) goto error;
1551 ret = xmlNewPattern();
1552 if (ret == NULL) goto error;
1553 ctxt->comp = ret;
1554
1555 xmlCompilePathPattern(ctxt);
1556 xmlFreePatParserContext(ctxt);
1557
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001558 xmlStreamCompile(ret);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +00001559 if (xmlReversePattern(ret) < 0)
1560 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001561 return(ret);
1562error:
1563 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
1564 if (ret != NULL) xmlFreePattern(ret);
1565 return(NULL);
1566}
1567
1568/**
1569 * xmlPatternMatch:
1570 * @comp: the precompiled pattern
1571 * @node: a node
1572 *
1573 * Test wether the node matches the pattern
1574 *
1575 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1576 */
1577int
1578xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
1579{
1580 if ((comp == NULL) || (node == NULL))
1581 return(-1);
1582 return(xmlPatMatch(comp, node));
1583}
1584
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001585/**
1586 * xmlPatternGetStreamCtxt:
1587 * @comp: the precompiled pattern
1588 *
1589 * Get a streaming context for that pattern
1590 * Use xmlFreeStreamCtxt to free the context.
1591 *
1592 * Returns a pointer to the context or NULL in case of failure
1593 */
1594xmlStreamCtxtPtr
1595xmlPatternGetStreamCtxt(xmlPatternPtr comp)
1596{
1597 if ((comp == NULL) || (comp->stream == NULL))
1598 return(NULL);
1599 return(xmlNewStreamCtxt(comp->stream));
1600}
1601
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001602#endif /* LIBXML_PATTERN_ENABLED */