blob: c0ad59c1947e79961bb41ba64d86697b69924392 [file] [log] [blame]
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001/*
2 * pattern.c: Implemetation of selectors for nodes
3 *
4 * Reference:
5 * http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
6 * to some extent
7 * http://www.w3.org/TR/1999/REC-xml-19991116
8 *
9 * See Copyright for the status of this software.
10 *
11 * daniel@veillard.com
12 */
13
Daniel Veillardf9d16912005-01-30 22:36:30 +000014/*
15 * TODO:
16 * - compilation flags to check for specific syntaxes
17 * using flags of xmlPatterncompile()
18 * - making clear how pattern starting with / or . need to be handled,
19 * currently push(NULL, NULL) means a reset of the streaming context
20 * and indicating we are on / (the document node), probably need
21 * something similar for .
Daniel Veillardd4301ab2005-02-03 22:24:10 +000022 * - get rid of the "compile" starting with lowercase
23 * - get rid of the Strdup/Strndup in case of dictionary
Daniel Veillardf9d16912005-01-30 22:36:30 +000024 */
25
Daniel Veillardb3de70c2003-12-02 22:32:15 +000026#define IN_LIBXML
27#include "libxml.h"
28
29#include <string.h>
30#include <libxml/xmlmemory.h>
31#include <libxml/tree.h>
32#include <libxml/hash.h>
33#include <libxml/dict.h>
34#include <libxml/xmlerror.h>
35#include <libxml/parserInternals.h>
36#include <libxml/pattern.h>
37
Daniel Veillardd4301ab2005-02-03 22:24:10 +000038#ifdef LIBXML_PATTERN_ENABLED
Daniel Veillardb3de70c2003-12-02 22:32:15 +000039
Daniel Veillardd4301ab2005-02-03 22:24:10 +000040/* #define DEBUG_STREAMING */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000041
Daniel Veillardb3de70c2003-12-02 22:32:15 +000042#define ERROR(a, b, c, d)
43#define ERROR5(a, b, c, d, e)
44
Daniel Veillard2fc6df92005-01-30 18:42:55 +000045#define XML_STREAM_STEP_DESC 1
46#define XML_STREAM_STEP_FINAL 2
47#define XML_STREAM_STEP_ROOT 4
48
49typedef struct _xmlStreamStep xmlStreamStep;
50typedef xmlStreamStep *xmlStreamStepPtr;
51struct _xmlStreamStep {
52 int flags; /* properties of that step */
53 const xmlChar *name; /* first string value if NULL accept all */
54 const xmlChar *ns; /* second string value */
55};
56
57typedef struct _xmlStreamComp xmlStreamComp;
58typedef xmlStreamComp *xmlStreamCompPtr;
59struct _xmlStreamComp {
60 xmlDict *dict; /* the dictionnary if any */
61 int nbStep; /* number of steps in the automata */
62 int maxStep; /* allocated number of steps */
63 xmlStreamStepPtr steps; /* the array of steps */
64};
65
66struct _xmlStreamCtxt {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +000067 struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000068 xmlStreamCompPtr comp; /* the compiled stream */
69 int nbState; /* number of state in the automata */
70 int maxState; /* allocated number of state */
71 int level; /* how deep are we ? */
72 int *states; /* the array of step indexes */
73};
74
75static void xmlFreeStreamComp(xmlStreamCompPtr comp);
76
Daniel Veillardb3de70c2003-12-02 22:32:15 +000077/*
78 * Types are private:
79 */
80
81typedef enum {
82 XML_OP_END=0,
83 XML_OP_ROOT,
84 XML_OP_ELEM,
85 XML_OP_CHILD,
86 XML_OP_ATTR,
87 XML_OP_PARENT,
88 XML_OP_ANCESTOR,
89 XML_OP_NS,
90 XML_OP_ALL
91} xmlPatOp;
92
93
Daniel Veillardd4301ab2005-02-03 22:24:10 +000094typedef struct _xmlStepState xmlStepState;
95typedef xmlStepState *xmlStepStatePtr;
96struct _xmlStepState {
97 int step;
98 xmlNodePtr node;
99};
100
101typedef struct _xmlStepStates xmlStepStates;
102typedef xmlStepStates *xmlStepStatesPtr;
103struct _xmlStepStates {
104 int nbstates;
105 int maxstates;
106 xmlStepStatePtr states;
107};
108
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000109typedef struct _xmlStepOp xmlStepOp;
110typedef xmlStepOp *xmlStepOpPtr;
111struct _xmlStepOp {
112 xmlPatOp op;
113 const xmlChar *value;
114 const xmlChar *value2;
115};
116
117struct _xmlPattern {
118 void *data; /* the associated template */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000119 xmlDictPtr dict; /* the optional dictionnary */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000120 struct _xmlPattern *next; /* next pattern if | is used */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000121 const xmlChar *pattern; /* the pattern */
122
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000123 int nbStep;
124 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000125 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000126 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000127};
128
129typedef struct _xmlPatParserContext xmlPatParserContext;
130typedef xmlPatParserContext *xmlPatParserContextPtr;
131struct _xmlPatParserContext {
132 const xmlChar *cur; /* the current char being parsed */
133 const xmlChar *base; /* the full expression */
134 int error; /* error code */
135 xmlDictPtr dict; /* the dictionnary if any */
136 xmlPatternPtr comp; /* the result */
137 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000138 const xmlChar **namespaces; /* the namespaces definitions */
139 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000140};
141
142/************************************************************************
143 * *
144 * Type functions *
145 * *
146 ************************************************************************/
147
148/**
149 * xmlNewPattern:
150 *
151 * Create a new XSLT Pattern
152 *
153 * Returns the newly allocated xmlPatternPtr or NULL in case of error
154 */
155static xmlPatternPtr
156xmlNewPattern(void) {
157 xmlPatternPtr cur;
158
159 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
160 if (cur == NULL) {
161 ERROR(NULL, NULL, NULL,
162 "xmlNewPattern : malloc failed\n");
163 return(NULL);
164 }
165 memset(cur, 0, sizeof(xmlPattern));
166 cur->maxStep = 10;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000167 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
168 if (cur->steps == NULL) {
169 xmlFree(cur);
170 ERROR(NULL, NULL, NULL,
171 "xmlNewPattern : malloc failed\n");
172 return(NULL);
173 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000174 return(cur);
175}
176
177/**
178 * xmlFreePattern:
179 * @comp: an XSLT comp
180 *
181 * Free up the memory allocated by @comp
182 */
183void
184xmlFreePattern(xmlPatternPtr comp) {
185 xmlStepOpPtr op;
186 int i;
187
188 if (comp == NULL)
189 return;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000190 if (comp->next != NULL)
191 xmlFreePattern(comp->next);
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000192 if (comp->stream != NULL)
193 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000194 if (comp->pattern != NULL)
195 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000196 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000197 if (comp->dict == NULL) {
198 for (i = 0;i < comp->nbStep;i++) {
199 op = &comp->steps[i];
200 if (op->value != NULL)
201 xmlFree((xmlChar *) op->value);
202 if (op->value2 != NULL)
203 xmlFree((xmlChar *) op->value2);
204 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000205 }
206 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000207 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000208 if (comp->dict != NULL)
209 xmlDictFree(comp->dict);
210
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000211 memset(comp, -1, sizeof(xmlPattern));
212 xmlFree(comp);
213}
214
215/**
216 * xmlFreePatternList:
217 * @comp: an XSLT comp list
218 *
219 * Free up the memory allocated by all the elements of @comp
220 */
221void
222xmlFreePatternList(xmlPatternPtr comp) {
223 xmlPatternPtr cur;
224
225 while (comp != NULL) {
226 cur = comp;
227 comp = comp->next;
228 xmlFreePattern(cur);
229 }
230}
231
232/**
233 * xmlNewPatParserContext:
234 * @pattern: the pattern context
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000235 * @dict: the inherited dictionnary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000236 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
237 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000238 *
239 * Create a new XML pattern parser context
240 *
241 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
242 */
243static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000244xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
245 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000246 xmlPatParserContextPtr cur;
247
248 if (pattern == NULL)
249 return(NULL);
250
251 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
252 if (cur == NULL) {
253 ERROR(NULL, NULL, NULL,
254 "xmlNewPatParserContext : malloc failed\n");
255 return(NULL);
256 }
257 memset(cur, 0, sizeof(xmlPatParserContext));
258 cur->dict = dict;
259 cur->cur = pattern;
260 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000261 if (namespaces != NULL) {
262 int i;
263 for (i = 0;namespaces[2 * i] != NULL;i++);
264 cur->nb_namespaces = i;
265 } else {
266 cur->nb_namespaces = 0;
267 }
268 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000269 return(cur);
270}
271
272/**
273 * xmlFreePatParserContext:
274 * @ctxt: an XSLT parser context
275 *
276 * Free up the memory allocated by @ctxt
277 */
278static void
279xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
280 if (ctxt == NULL)
281 return;
282 memset(ctxt, -1, sizeof(xmlPatParserContext));
283 xmlFree(ctxt);
284}
285
286/**
287 * xmlPatternAdd:
288 * @comp: the compiled match expression
289 * @op: an op
290 * @value: the first value
291 * @value2: the second value
292 *
293 * Add an step to an XSLT Compiled Match
294 *
295 * Returns -1 in case of failure, 0 otherwise.
296 */
297static int
298xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
299 xmlPatternPtr comp,
300 xmlPatOp op, xmlChar * value, xmlChar * value2)
301{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000302 if (comp->nbStep >= comp->maxStep) {
303 xmlStepOpPtr temp;
304 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
305 sizeof(xmlStepOp));
306 if (temp == NULL) {
307 ERROR(ctxt, NULL, NULL,
308 "xmlPatternAdd: realloc failed\n");
309 return (-1);
310 }
311 comp->steps = temp;
312 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000313 }
314 comp->steps[comp->nbStep].op = op;
315 comp->steps[comp->nbStep].value = value;
316 comp->steps[comp->nbStep].value2 = value2;
317 comp->nbStep++;
318 return (0);
319}
320
321#if 0
322/**
323 * xsltSwapTopPattern:
324 * @comp: the compiled match expression
325 *
326 * reverse the two top steps.
327 */
328static void
329xsltSwapTopPattern(xmlPatternPtr comp) {
330 int i;
331 int j = comp->nbStep - 1;
332
333 if (j > 0) {
334 register const xmlChar *tmp;
335 register xmlPatOp op;
336 i = j - 1;
337 tmp = comp->steps[i].value;
338 comp->steps[i].value = comp->steps[j].value;
339 comp->steps[j].value = tmp;
340 tmp = comp->steps[i].value2;
341 comp->steps[i].value2 = comp->steps[j].value2;
342 comp->steps[j].value2 = tmp;
343 op = comp->steps[i].op;
344 comp->steps[i].op = comp->steps[j].op;
345 comp->steps[j].op = op;
346 }
347}
348#endif
349
350/**
351 * xmlReversePattern:
352 * @comp: the compiled match expression
353 *
354 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000355 *
356 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000357 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000358static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000359xmlReversePattern(xmlPatternPtr comp) {
360 int i = 0;
361 int j = comp->nbStep - 1;
362
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000363 if (comp->nbStep >= comp->maxStep) {
364 xmlStepOpPtr temp;
365 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
366 sizeof(xmlStepOp));
367 if (temp == NULL) {
368 ERROR(ctxt, NULL, NULL,
369 "xmlReversePattern: realloc failed\n");
370 return (-1);
371 }
372 comp->steps = temp;
373 comp->maxStep *= 2;
374 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000375 while (j > i) {
376 register const xmlChar *tmp;
377 register xmlPatOp op;
378 tmp = comp->steps[i].value;
379 comp->steps[i].value = comp->steps[j].value;
380 comp->steps[j].value = tmp;
381 tmp = comp->steps[i].value2;
382 comp->steps[i].value2 = comp->steps[j].value2;
383 comp->steps[j].value2 = tmp;
384 op = comp->steps[i].op;
385 comp->steps[i].op = comp->steps[j].op;
386 comp->steps[j].op = op;
387 j--;
388 i++;
389 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000390 comp->steps[comp->nbStep].value = NULL;
391 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000392 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000393 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000394}
395
396/************************************************************************
397 * *
398 * The interpreter for the precompiled patterns *
399 * *
400 ************************************************************************/
401
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000402static int
403xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
404 if ((states->states == NULL) || (states->maxstates <= 0)) {
405 states->maxstates = 4;
406 states->nbstates = 0;
407 states->states = xmlMalloc(4 * sizeof(xmlStepState));
408 }
409 else if (states->maxstates <= states->nbstates) {
410 xmlStepState *tmp;
411
412 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
413 2 * states->maxstates * sizeof(xmlStepState));
414 if (tmp == NULL)
415 return(-1);
416 states->states = tmp;
417 states->maxstates *= 2;
418 }
419 states->states[states->nbstates].step = step;
420 states->states[states->nbstates++].node = node;
421#if 0
422 fprintf(stderr, "Push: %d, %s\n", step, node->name);
423#endif
424 return(0);
425}
426
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000427/**
428 * xmlPatMatch:
429 * @comp: the precompiled pattern
430 * @node: a node
431 *
432 * Test wether the node matches the pattern
433 *
434 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
435 */
436static int
437xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
438 int i;
439 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000440 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000441
442 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000443 i = 0;
444restart:
445 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000446 step = &comp->steps[i];
447 switch (step->op) {
448 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000449 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000450 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000451 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000452 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000453 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000454 if ((node->type == XML_DOCUMENT_NODE) ||
455#ifdef LIBXML_DOCB_ENABLED
456 (node->type == XML_DOCB_DOCUMENT_NODE) ||
457#endif
458 (node->type == XML_HTML_DOCUMENT_NODE))
459 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000460 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000461 case XML_OP_ELEM:
462 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000463 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000464 if (step->value == NULL)
465 continue;
466 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000467 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000468 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000469 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000470
471 /* Namespace test */
472 if (node->ns == NULL) {
473 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000474 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000475 } else if (node->ns->href != NULL) {
476 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000477 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000478 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000479 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000480 }
481 continue;
482 case XML_OP_CHILD: {
483 xmlNodePtr lst;
484
485 if ((node->type != XML_ELEMENT_NODE) &&
486 (node->type != XML_DOCUMENT_NODE) &&
487#ifdef LIBXML_DOCB_ENABLED
488 (node->type != XML_DOCB_DOCUMENT_NODE) &&
489#endif
490 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000491 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000492
493 lst = node->children;
494
495 if (step->value != NULL) {
496 while (lst != NULL) {
497 if ((lst->type == XML_ELEMENT_NODE) &&
498 (step->value[0] == lst->name[0]) &&
499 (xmlStrEqual(step->value, lst->name)))
500 break;
501 lst = lst->next;
502 }
503 if (lst != NULL)
504 continue;
505 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000506 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000507 }
508 case XML_OP_ATTR:
509 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000510 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000511 if (step->value != NULL) {
512 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000513 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000514 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000515 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000516 }
517 /* Namespace test */
518 if (node->ns == NULL) {
519 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000520 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000521 } else if (step->value2 != NULL) {
522 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000523 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000524 }
525 continue;
526 case XML_OP_PARENT:
527 if ((node->type == XML_DOCUMENT_NODE) ||
528 (node->type == XML_HTML_DOCUMENT_NODE) ||
529#ifdef LIBXML_DOCB_ENABLED
530 (node->type == XML_DOCB_DOCUMENT_NODE) ||
531#endif
532 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000533 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000534 node = node->parent;
535 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000536 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000537 if (step->value == NULL)
538 continue;
539 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000540 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000541 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000542 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000543 /* Namespace test */
544 if (node->ns == NULL) {
545 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000546 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000547 } else if (node->ns->href != NULL) {
548 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000549 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000550 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000551 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000552 }
553 continue;
554 case XML_OP_ANCESTOR:
555 /* TODO: implement coalescing of ANCESTOR/NODE ops */
556 if (step->value == NULL) {
557 i++;
558 step = &comp->steps[i];
559 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000560 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000561 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000562 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000563 if (step->value == NULL)
564 return(-1);
565 }
566 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000567 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000568 if ((node->type == XML_DOCUMENT_NODE) ||
569 (node->type == XML_HTML_DOCUMENT_NODE) ||
570#ifdef LIBXML_DOCB_ENABLED
571 (node->type == XML_DOCB_DOCUMENT_NODE) ||
572#endif
573 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000574 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000575 node = node->parent;
576 while (node != NULL) {
577 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000578 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000579 if ((node->type == XML_ELEMENT_NODE) &&
580 (step->value[0] == node->name[0]) &&
581 (xmlStrEqual(step->value, node->name))) {
582 /* Namespace test */
583 if (node->ns == NULL) {
584 if (step->value2 == NULL)
585 break;
586 } else if (node->ns->href != NULL) {
587 if ((step->value2 != NULL) &&
588 (xmlStrEqual(step->value2, node->ns->href)))
589 break;
590 }
591 }
592 node = node->parent;
593 }
594 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000595 goto rollback;
596 /*
597 * prepare a potential rollback from here
598 * for ancestors of that node.
599 */
600 if (step->op == XML_OP_ANCESTOR)
601 xmlPatPushState(&states, i, node);
602 else
603 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000604 continue;
605 case XML_OP_NS:
606 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000607 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000608 if (node->ns == NULL) {
609 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000610 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000611 } else if (node->ns->href != NULL) {
612 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000613 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000614 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000615 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000616 }
617 break;
618 case XML_OP_ALL:
619 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000620 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000621 break;
622 }
623 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000624found:
625 if (states.states != NULL) {
626 /* Free the rollback states */
627 xmlFree(states.states);
628 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000629 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000630rollback:
631 /* got an error try to rollback */
632 if (states.states == NULL)
633 return(0);
634 if (states.nbstates <= 0) {
635 xmlFree(states.states);
636 return(0);
637 }
638 states.nbstates--;
639 i = states.states[states.nbstates].step;
640 node = states.states[states.nbstates].node;
641#if 0
642 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
643#endif
644 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000645}
646
647/************************************************************************
648 * *
649 * Dedicated parser for templates *
650 * *
651 ************************************************************************/
652
653#define TODO \
654 xmlGenericError(xmlGenericErrorContext, \
655 "Unimplemented block at %s:%d\n", \
656 __FILE__, __LINE__);
657#define CUR (*ctxt->cur)
658#define SKIP(val) ctxt->cur += (val)
659#define NXT(val) ctxt->cur[(val)]
660#define CUR_PTR ctxt->cur
661
662#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000663 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000664
665#define CURRENT (*ctxt->cur)
666#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
667
668
669#define PUSH(op, val, val2) \
670 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
671
672#define XSLT_ERROR(X) \
673 { xsltError(ctxt, __FILE__, __LINE__, X); \
674 ctxt->error = (X); return; }
675
676#define XSLT_ERROR0(X) \
677 { xsltError(ctxt, __FILE__, __LINE__, X); \
678 ctxt->error = (X); return(0); }
679
680#if 0
681/**
682 * xmlPatScanLiteral:
683 * @ctxt: the XPath Parser context
684 *
685 * Parse an XPath Litteral:
686 *
687 * [29] Literal ::= '"' [^"]* '"'
688 * | "'" [^']* "'"
689 *
690 * Returns the Literal parsed or NULL
691 */
692
693static xmlChar *
694xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
695 const xmlChar *q, *cur;
696 xmlChar *ret = NULL;
697 int val, len;
698
699 SKIP_BLANKS;
700 if (CUR == '"') {
701 NEXT;
702 cur = q = CUR_PTR;
703 val = xmlStringCurrentChar(NULL, cur, &len);
704 while ((IS_CHAR(val)) && (val != '"')) {
705 cur += len;
706 val = xmlStringCurrentChar(NULL, cur, &len);
707 }
708 if (!IS_CHAR(val)) {
709 ctxt->error = 1;
710 return(NULL);
711 } else {
712 ret = xmlStrndup(q, cur - q);
713 }
714 cur += len;
715 CUR_PTR = cur;
716 } else if (CUR == '\'') {
717 NEXT;
718 cur = q = CUR_PTR;
719 val = xmlStringCurrentChar(NULL, cur, &len);
720 while ((IS_CHAR(val)) && (val != '\'')) {
721 cur += len;
722 val = xmlStringCurrentChar(NULL, cur, &len);
723 }
724 if (!IS_CHAR(val)) {
725 ctxt->error = 1;
726 return(NULL);
727 } else {
728 ret = xmlStrndup(q, cur - q);
729 }
730 cur += len;
731 CUR_PTR = cur;
732 } else {
733 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
734 ctxt->error = 1;
735 return(NULL);
736 }
737 return(ret);
738}
739#endif
740
741/**
742 * xmlPatScanName:
743 * @ctxt: the XPath Parser context
744 *
745 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
746 * CombiningChar | Extender
747 *
748 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
749 *
750 * [6] Names ::= Name (S Name)*
751 *
752 * Returns the Name parsed or NULL
753 */
754
755static xmlChar *
756xmlPatScanName(xmlPatParserContextPtr ctxt) {
757 const xmlChar *q, *cur;
758 xmlChar *ret = NULL;
759 int val, len;
760
761 SKIP_BLANKS;
762
763 cur = q = CUR_PTR;
764 val = xmlStringCurrentChar(NULL, cur, &len);
765 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
766 return(NULL);
767
768 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
769 (val == '.') || (val == '-') ||
770 (val == '_') ||
771 (IS_COMBINING(val)) ||
772 (IS_EXTENDER(val))) {
773 cur += len;
774 val = xmlStringCurrentChar(NULL, cur, &len);
775 }
776 ret = xmlStrndup(q, cur - q);
777 CUR_PTR = cur;
778 return(ret);
779}
780
781/**
782 * xmlPatScanNCName:
783 * @ctxt: the XPath Parser context
784 *
785 * Parses a non qualified name
786 *
787 * Returns the Name parsed or NULL
788 */
789
790static xmlChar *
791xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
792 const xmlChar *q, *cur;
793 xmlChar *ret = NULL;
794 int val, len;
795
796 SKIP_BLANKS;
797
798 cur = q = CUR_PTR;
799 val = xmlStringCurrentChar(NULL, cur, &len);
800 if (!IS_LETTER(val) && (val != '_'))
801 return(NULL);
802
803 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
804 (val == '.') || (val == '-') ||
805 (val == '_') ||
806 (IS_COMBINING(val)) ||
807 (IS_EXTENDER(val))) {
808 cur += len;
809 val = xmlStringCurrentChar(NULL, cur, &len);
810 }
811 ret = xmlStrndup(q, cur - q);
812 CUR_PTR = cur;
813 return(ret);
814}
815
816#if 0
817/**
818 * xmlPatScanQName:
819 * @ctxt: the XPath Parser context
820 * @prefix: the place to store the prefix
821 *
822 * Parse a qualified name
823 *
824 * Returns the Name parsed or NULL
825 */
826
827static xmlChar *
828xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
829 xmlChar *ret = NULL;
830
831 *prefix = NULL;
832 ret = xmlPatScanNCName(ctxt);
833 if (CUR == ':') {
834 *prefix = ret;
835 NEXT;
836 ret = xmlPatScanNCName(ctxt);
837 }
838 return(ret);
839}
840#endif
841
842/**
843 * xmlCompileStepPattern:
844 * @ctxt: the compilation context
845 *
846 * Compile the Step Pattern and generates a precompiled
847 * form suitable for fast matching.
848 *
849 * [3] Step ::= '.' | NameTest
850 * [4] NameTest ::= QName | '*' | NCName ':' '*'
851 */
852
853static void
854xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
855 xmlChar *token = NULL;
856 xmlChar *name = NULL;
857 const xmlChar *URI = NULL;
858 xmlChar *URL = NULL;
859
860 SKIP_BLANKS;
861 if (CUR == '.') {
862 NEXT;
863 PUSH(XML_OP_ELEM, NULL, NULL);
864 return;
865 }
866 name = xmlPatScanNCName(ctxt);
867 if (name == NULL) {
868 if (CUR == '*') {
869 NEXT;
870 PUSH(XML_OP_ALL, NULL, NULL);
871 return;
872 } else {
873 ERROR(NULL, NULL, NULL,
874 "xmlCompileStepPattern : Name expected\n");
875 ctxt->error = 1;
876 return;
877 }
878 }
879 SKIP_BLANKS;
880 if (CUR == ':') {
881 NEXT;
882 if (CUR != ':') {
883 xmlChar *prefix = name;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000884 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000885
886 /*
887 * This is a namespace match
888 */
889 token = xmlPatScanName(ctxt);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000890 for (i = 0;i < ctxt->nb_namespaces;i++) {
891 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Daniel Veillard0996a162005-02-05 14:00:10 +0000892 URL = xmlStrdup(ctxt->namespaces[2 * i]);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000893 break;
894 }
895 }
896 if (i >= ctxt->nb_namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000897 ERROR5(NULL, NULL, NULL,
898 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
899 prefix);
900 ctxt->error = 1;
901 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000902 }
903 xmlFree(prefix);
904 if (token == NULL) {
905 if (CUR == '*') {
906 NEXT;
907 PUSH(XML_OP_NS, URL, NULL);
908 } else {
909 ERROR(NULL, NULL, NULL,
910 "xmlCompileStepPattern : Name expected\n");
911 ctxt->error = 1;
912 goto error;
913 }
914 } else {
915 PUSH(XML_OP_ELEM, token, URL);
916 }
917 } else {
918 NEXT;
919 if (xmlStrEqual(token, (const xmlChar *) "child")) {
920 xmlFree(token);
921 token = xmlPatScanName(ctxt);
922 if (token == NULL) {
923 if (CUR == '*') {
924 NEXT;
925 PUSH(XML_OP_ALL, token, NULL);
926 return;
927 } else {
928 ERROR(NULL, NULL, NULL,
929 "xmlCompileStepPattern : QName expected\n");
930 ctxt->error = 1;
931 goto error;
932 }
933 }
934 TODO
935 /* URI = xsltGetQNameURI(ctxt->elem, &token); */
936 if (token == NULL) {
937 ctxt->error = 1;
938 goto error;
939 } else {
940 name = xmlStrdup(token);
941 if (URI != NULL)
942 URL = xmlStrdup(URI);
943 }
944 PUSH(XML_OP_CHILD, name, URL);
945 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
946 xmlFree(token);
947 token = xmlPatScanName(ctxt);
948 if (token == NULL) {
949 ERROR(NULL, NULL, NULL,
950 "xmlCompileStepPattern : QName expected\n");
951 ctxt->error = 1;
952 goto error;
953 }
954 TODO
955 /* URI = xsltGetQNameURI(ctxt->elem, &token); */
956 if (token == NULL) {
957 ctxt->error = 1;
958 goto error;
959 } else {
960 name = xmlStrdup(token);
961 if (URI != NULL)
962 URL = xmlStrdup(URI);
963 }
964 PUSH(XML_OP_ATTR, name, URL);
965 } else {
966 ERROR(NULL, NULL, NULL,
967 "xmlCompileStepPattern : 'child' or 'attribute' expected\n");
968 ctxt->error = 1;
969 goto error;
970 }
971 xmlFree(token);
972 }
973 } else if (CUR == '*') {
974 NEXT;
975 PUSH(XML_OP_ALL, token, NULL);
976 } else {
977 if (name == NULL) {
978 ctxt->error = 1;
979 goto error;
980 }
981 PUSH(XML_OP_ELEM, name, NULL);
982 }
983 return;
984error:
985 if (token != NULL)
986 xmlFree(token);
987 if (name != NULL)
988 xmlFree(name);
989}
990
991/**
992 * xmlCompilePathPattern:
993 * @ctxt: the compilation context
994 *
995 * Compile the Path Pattern and generates a precompiled
996 * form suitable for fast matching.
997 *
998 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
999 */
1000static void
1001xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1002 SKIP_BLANKS;
1003 if ((CUR == '/') && (NXT(1) == '/')) {
1004 /*
1005 * since we reverse the query
1006 * a leading // can be safely ignored
1007 */
1008 NEXT;
1009 NEXT;
1010 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1011 /*
1012 * a leading .// can be safely ignored
1013 */
1014 NEXT;
1015 NEXT;
1016 NEXT;
1017 }
1018 if (CUR == '@') {
1019 TODO
1020 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001021 if (CUR == '/') {
1022 PUSH(XML_OP_ROOT, NULL, NULL);
1023 NEXT;
1024 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001025 xmlCompileStepPattern(ctxt);
1026 SKIP_BLANKS;
1027 while (CUR == '/') {
1028 if ((CUR == '/') && (NXT(1) == '/')) {
1029 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1030 NEXT;
1031 NEXT;
1032 SKIP_BLANKS;
1033 xmlCompileStepPattern(ctxt);
1034 } else {
1035 PUSH(XML_OP_PARENT, NULL, NULL);
1036 NEXT;
1037 SKIP_BLANKS;
1038 if ((CUR != 0) || (CUR == '|')) {
1039 xmlCompileStepPattern(ctxt);
1040 }
1041 }
1042 }
1043 }
1044error:
1045 return;
1046}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001047
1048/************************************************************************
1049 * *
1050 * The streaming code *
1051 * *
1052 ************************************************************************/
1053
1054#ifdef DEBUG_STREAMING
1055static void
1056xmlDebugStreamComp(xmlStreamCompPtr stream) {
1057 int i;
1058
1059 if (stream == NULL) {
1060 printf("Stream: NULL\n");
1061 return;
1062 }
1063 printf("Stream: %d steps\n", stream->nbStep);
1064 for (i = 0;i < stream->nbStep;i++) {
1065 if (stream->steps[i].ns != NULL) {
1066 printf("{%s}", stream->steps[i].ns);
1067 }
1068 if (stream->steps[i].name == NULL) {
1069 printf("* ");
1070 } else {
1071 printf("%s ", stream->steps[i].name);
1072 }
1073 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1074 printf("root ");
1075 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1076 printf("// ");
1077 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1078 printf("final ");
1079 printf("\n");
1080 }
1081}
1082static void
1083xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1084 int i;
1085
1086 if (ctxt == NULL) {
1087 printf("Stream: NULL\n");
1088 return;
1089 }
1090 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1091 if (match)
1092 printf("matches\n");
1093 else
1094 printf("\n");
1095 for (i = 0;i < ctxt->nbState;i++) {
1096 if (ctxt->states[2 * i] < 0)
1097 printf(" %d: free\n", i);
1098 else {
1099 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1100 ctxt->states[(2 * i) + 1]);
1101 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1102 XML_STREAM_STEP_DESC)
1103 printf(" //\n");
1104 else
1105 printf("\n");
1106 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001107 }
1108}
1109#endif
1110/**
1111 * xmlNewStreamComp:
1112 * @size: the number of expected steps
1113 *
1114 * build a new compiled pattern for streaming
1115 *
1116 * Returns the new structure or NULL in case of error.
1117 */
1118static xmlStreamCompPtr
1119xmlNewStreamComp(int size) {
1120 xmlStreamCompPtr cur;
1121
1122 if (size < 4)
1123 size = 4;
1124
1125 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1126 if (cur == NULL) {
1127 ERROR(NULL, NULL, NULL,
1128 "xmlNewStreamComp: malloc failed\n");
1129 return(NULL);
1130 }
1131 memset(cur, 0, sizeof(xmlStreamComp));
1132 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1133 if (cur->steps == NULL) {
1134 xmlFree(cur);
1135 ERROR(NULL, NULL, NULL,
1136 "xmlNewStreamComp: malloc failed\n");
1137 return(NULL);
1138 }
1139 cur->nbStep = 0;
1140 cur->maxStep = size;
1141 return(cur);
1142}
1143
1144/**
1145 * xmlFreeStreamComp:
1146 * @comp: the compiled pattern for streaming
1147 *
1148 * Free the compiled pattern for streaming
1149 */
1150static void
1151xmlFreeStreamComp(xmlStreamCompPtr comp) {
1152 if (comp != NULL) {
1153 if (comp->steps != NULL)
1154 xmlFree(comp->steps);
1155 if (comp->dict != NULL)
1156 xmlDictFree(comp->dict);
1157 xmlFree(comp);
1158 }
1159}
1160
1161/**
1162 * xmlStreamCompAddStep:
1163 * @comp: the compiled pattern for streaming
1164 * @name: the first string, the name, or NULL for *
1165 * @ns: the second step, the namespace name
1166 * @flags: the flags for that step
1167 *
1168 * Add a new step to the compiled pattern
1169 *
1170 * Returns -1 in case of error or the step index if successful
1171 */
1172static int
1173xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1174 const xmlChar *ns, int flags) {
1175 xmlStreamStepPtr cur;
1176
1177 if (comp->nbStep >= comp->maxStep) {
1178 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1179 comp->maxStep * 2 * sizeof(xmlStreamStep));
1180 if (cur == NULL) {
1181 ERROR(NULL, NULL, NULL,
1182 "xmlNewStreamComp: malloc failed\n");
1183 return(-1);
1184 }
1185 comp->steps = cur;
1186 comp->maxStep *= 2;
1187 }
1188 cur = &comp->steps[comp->nbStep++];
1189 cur->flags = flags;
1190 cur->name = name;
1191 cur->ns = ns;
1192 return(comp->nbStep - 1);
1193}
1194
1195/**
1196 * xmlStreamCompile:
1197 * @comp: the precompiled pattern
1198 *
1199 * Tries to stream compile a pattern
1200 *
1201 * Returns -1 in case of failure and 0 in case of success.
1202 */
1203static int
1204xmlStreamCompile(xmlPatternPtr comp) {
1205 xmlStreamCompPtr stream;
1206 int i, s = 0, root = 0, desc = 0;
1207
1208 if ((comp == NULL) || (comp->steps == NULL))
1209 return(-1);
1210 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1211 if (stream == NULL)
1212 return(-1);
1213 if (comp->dict != NULL) {
1214 stream->dict = comp->dict;
1215 xmlDictReference(stream->dict);
1216 }
1217 for (i = 0;i < comp->nbStep;i++) {
1218 switch (comp->steps[i].op) {
1219 case XML_OP_END:
1220 break;
1221 case XML_OP_ROOT:
1222 if (i != 0)
1223 goto error;
1224 root = 1;
1225 break;
1226 case XML_OP_CHILD:
1227 case XML_OP_ATTR:
1228 case XML_OP_NS:
1229 goto error;
1230 case XML_OP_ELEM:
1231 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1232 comp->steps[i].value2, desc);
1233 desc = 0;
1234 if (s < 0)
1235 goto error;
1236 break;
1237 case XML_OP_ALL:
1238 s = xmlStreamCompAddStep(stream, NULL, NULL, desc);
1239 desc = 0;
1240 if (s < 0)
1241 goto error;
1242 break;
1243 case XML_OP_PARENT:
1244 break;
1245 case XML_OP_ANCESTOR:
1246 desc = XML_STREAM_STEP_DESC;
1247 break;
1248 }
1249 }
1250 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1251 if (root)
1252 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1253#ifdef DEBUG_STREAMING
1254 xmlDebugStreamComp(stream);
1255#endif
1256 comp->stream = stream;
1257 return(0);
1258error:
1259 xmlFreeStreamComp(stream);
1260 return(0);
1261}
1262
1263/**
1264 * xmlNewStreamCtxt:
1265 * @size: the number of expected states
1266 *
1267 * build a new stream context
1268 *
1269 * Returns the new structure or NULL in case of error.
1270 */
1271static xmlStreamCtxtPtr
1272xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1273 xmlStreamCtxtPtr cur;
1274
1275 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1276 if (cur == NULL) {
1277 ERROR(NULL, NULL, NULL,
1278 "xmlNewStreamCtxt: malloc failed\n");
1279 return(NULL);
1280 }
1281 memset(cur, 0, sizeof(xmlStreamCtxt));
1282 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1283 if (cur->states == NULL) {
1284 xmlFree(cur);
1285 ERROR(NULL, NULL, NULL,
1286 "xmlNewStreamCtxt: malloc failed\n");
1287 return(NULL);
1288 }
1289 cur->nbState = 0;
1290 cur->maxState = 4;
1291 cur->level = 0;
1292 cur->comp = stream;
1293 return(cur);
1294}
1295
1296/**
1297 * xmlFreeStreamCtxt:
1298 * @stream: the stream context
1299 *
1300 * Free the stream context
1301 */
1302void
1303xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001304 xmlStreamCtxtPtr next;
1305
1306 while (stream != NULL) {
1307 next = stream->next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001308 if (stream->states != NULL)
1309 xmlFree(stream->states);
1310 xmlFree(stream);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001311 stream = next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001312 }
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 Veillardf1f08cf2005-02-05 16:35:04 +00001370 int ret = 0, err = 0, tmp, i, m, match, step, desc, final;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001371 xmlStreamCompPtr comp;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001372#ifdef DEBUG_STREAMING
1373 xmlStreamCtxtPtr orig = stream;
1374#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001375
1376 if ((stream == NULL) || (stream->nbState < 0))
1377 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001378
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001379 while (stream != NULL) {
1380 comp = stream->comp;
1381 if ((name == NULL) && (ns == NULL)) {
1382 stream->nbState = 0;
1383 stream->level = 0;
1384 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1385 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1386 if (tmp < 0)
1387 err++;
1388 if (comp->steps[tmp].flags & XML_STREAM_STEP_FINAL)
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001389 ret = 1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001390 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001391 continue; /* while */
1392 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001393 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001394 continue; /* while */
1395 }
1396 /*
1397 * Check evolution of existing states
1398 */
1399 m = stream->nbState;
1400 for (i = 0;i < m;i++) {
1401 match = 0;
1402 step = stream->states[2 * i];
1403 /* dead states */
1404 if (step < 0) continue;
1405 /* skip new states just added */
1406 if (stream->states[(2 * i) + 1] > stream->level)
1407 continue;
1408 /* skip continuations */
1409 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1410 if ((stream->states[(2 * i) + 1] < stream->level) && (!desc))
1411 continue;
1412
1413 /* discard old states */
1414 /* something needed about old level discarded */
1415
1416 if (comp->dict) {
1417 if (comp->steps[step].name == NULL) {
1418 if (comp->steps[step].ns == NULL)
1419 match = 1;
1420 else
1421 match = (comp->steps[step].ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001422 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001423 match = ((comp->steps[step].name == name) &&
1424 (comp->steps[step].ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001425 }
1426 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001427 if (comp->steps[step].name == NULL) {
1428 if (comp->steps[step].ns == NULL)
1429 match = 1;
1430 else
1431 match = xmlStrEqual(comp->steps[step].ns, ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001432 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001433 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
1434 (xmlStrEqual(comp->steps[step].ns, ns)));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001435 }
1436 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001437 if (match) {
1438 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
1439 if (desc) {
1440 if (final) {
1441 ret = 1;
1442 } else {
1443 /* descending match create a new state */
1444 xmlStreamCtxtAddState(stream, step + 1,
1445 stream->level + 1);
1446 }
1447 } else {
1448 if (final) {
1449 ret = 1;
1450 } else {
1451 xmlStreamCtxtAddState(stream, step + 1,
1452 stream->level + 1);
1453 }
1454 }
1455 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001456 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001457
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001458 /*
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 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001474 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001475 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 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001484 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001485 if (match) {
1486 if (comp->steps[0].flags & XML_STREAM_STEP_FINAL)
1487 ret = 1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001488 else
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001489 xmlStreamCtxtAddState(stream, 1, stream->level);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001490 }
1491 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001492
1493 stream = stream->next;
1494 } /* while stream != NULL */
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001495
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001496 if (err > 0)
1497 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001498#ifdef DEBUG_STREAMING
1499 xmlDebugStreamCtxt(orig, ret);
1500#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001501 return(ret);
1502}
1503
1504/**
1505 * xmlStreamPop:
1506 * @stream: the stream context
1507 *
1508 * push one level from the stream.
1509 *
1510 * Returns: -1 in case of error, 0 otherwise.
1511 */
1512int
1513xmlStreamPop(xmlStreamCtxtPtr stream) {
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001514 int i, m;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001515 int ret;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001516
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001517 if (stream == NULL)
1518 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001519 ret = 0;
1520 while (stream != NULL) {
1521 stream->level--;
1522 if (stream->level < 0)
1523 ret = -1;
1524
1525 /*
1526 * Check evolution of existing states
1527 */
1528 m = stream->nbState;
1529 for (i = 0;i < m;i++) {
1530 if (stream->states[(2 * i)] < 0) break;
1531 /* discard obsoleted states */
1532 if (stream->states[(2 * i) + 1] > stream->level)
1533 stream->states[(2 * i)] = -1;
1534 }
1535 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001536 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001537 return(0);
1538}
1539
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001540/************************************************************************
1541 * *
1542 * The public interfaces *
1543 * *
1544 ************************************************************************/
1545
1546/**
1547 * xmlPatterncompile:
1548 * @pattern: the pattern to compile
1549 * @dict: an optional dictionnary for interned strings
1550 * @flags: compilation flags, undefined yet
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001551 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001552 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001553 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001554 *
1555 * Returns the compiled for of the pattern or NULL in case of error
1556 */
1557xmlPatternPtr
Daniel Veillard427174f2003-12-10 10:42:59 +00001558xmlPatterncompile(const xmlChar *pattern, xmlDict *dict,
1559 int flags ATTRIBUTE_UNUSED,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001560 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001561 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001562 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001563 const xmlChar *or, *start;
1564 xmlChar *tmp = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001565
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001566 if (pattern == NULL)
1567 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001568
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001569 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001570 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001571 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001572 tmp = NULL;
1573 while ((*or != 0) && (*or != '|')) or++;
1574 if (*or == 0)
1575 ctxt = xmlNewPatParserContext(start, dict, namespaces);
1576 else {
1577 tmp = xmlStrndup(start, or - start);
1578 if (tmp != NULL) {
1579 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
1580 }
1581 or++;
1582 }
1583 if (ctxt == NULL) goto error;
1584 cur = xmlNewPattern();
1585 if (cur == NULL) goto error;
1586 if (ret == NULL)
1587 ret = cur;
1588 else {
1589 cur->next = ret->next;
1590 ret->next = cur;
1591 }
1592 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001593
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001594 xmlCompilePathPattern(ctxt);
1595 xmlFreePatParserContext(ctxt);
1596
1597
1598 xmlStreamCompile(cur);
1599 if (xmlReversePattern(cur) < 0)
1600 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001601 if (tmp != NULL) {
1602 xmlFree(tmp);
1603 tmp = NULL;
1604 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001605 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001606 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001607 return(ret);
1608error:
1609 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
1610 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001611 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001612 return(NULL);
1613}
1614
1615/**
1616 * xmlPatternMatch:
1617 * @comp: the precompiled pattern
1618 * @node: a node
1619 *
1620 * Test wether the node matches the pattern
1621 *
1622 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1623 */
1624int
1625xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
1626{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001627 int ret = 0;
1628
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001629 if ((comp == NULL) || (node == NULL))
1630 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001631
1632 while (comp != NULL) {
1633 ret = xmlPatMatch(comp, node);
1634 if (ret != 0)
1635 return(ret);
1636 comp = comp->next;
1637 }
1638 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001639}
1640
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001641/**
1642 * xmlPatternGetStreamCtxt:
1643 * @comp: the precompiled pattern
1644 *
1645 * Get a streaming context for that pattern
1646 * Use xmlFreeStreamCtxt to free the context.
1647 *
1648 * Returns a pointer to the context or NULL in case of failure
1649 */
1650xmlStreamCtxtPtr
1651xmlPatternGetStreamCtxt(xmlPatternPtr comp)
1652{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001653 xmlStreamCtxtPtr ret = NULL, cur;
1654
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001655 if ((comp == NULL) || (comp->stream == NULL))
1656 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001657
1658 while (comp != NULL) {
1659 if (comp->stream == NULL)
1660 goto failed;
1661 cur = xmlNewStreamCtxt(comp->stream);
1662 if (cur == NULL)
1663 goto failed;
1664 if (ret == NULL)
1665 ret = cur;
1666 else {
1667 cur->next = ret->next;
1668 ret->next = cur;
1669 }
1670 comp = comp->next;
1671 }
1672 return(ret);
1673failed:
1674 xmlFreeStreamCtxt(ret);
1675 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001676}
1677
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001678#endif /* LIBXML_PATTERN_ENABLED */