blob: 22d5c31d5df7c1b491d88765129e1a8cd806db63 [file] [log] [blame]
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001/*
2 * pattern.c: Implemetation of selectors for nodes
3 *
4 * Reference:
5 * http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
6 * to some extent
7 * http://www.w3.org/TR/1999/REC-xml-19991116
8 *
9 * See Copyright for the status of this software.
10 *
11 * daniel@veillard.com
12 */
13
Daniel Veillardf9d16912005-01-30 22:36:30 +000014/*
15 * TODO:
16 * - compilation flags to check for specific syntaxes
17 * using flags of xmlPatterncompile()
18 * - making clear how pattern starting with / or . need to be handled,
19 * currently push(NULL, NULL) means a reset of the streaming context
20 * and indicating we are on / (the document node), probably need
21 * something similar for .
Daniel Veillardd4301ab2005-02-03 22:24:10 +000022 * - get rid of the "compile" starting with lowercase
23 * - get rid of the Strdup/Strndup in case of dictionary
Daniel Veillardf9d16912005-01-30 22:36:30 +000024 */
25
Daniel Veillardb3de70c2003-12-02 22:32:15 +000026#define IN_LIBXML
27#include "libxml.h"
28
29#include <string.h>
30#include <libxml/xmlmemory.h>
31#include <libxml/tree.h>
32#include <libxml/hash.h>
33#include <libxml/dict.h>
34#include <libxml/xmlerror.h>
35#include <libxml/parserInternals.h>
36#include <libxml/pattern.h>
37
Daniel Veillardd4301ab2005-02-03 22:24:10 +000038#ifdef LIBXML_PATTERN_ENABLED
Daniel Veillardb3de70c2003-12-02 22:32:15 +000039
Daniel Veillardd4301ab2005-02-03 22:24:10 +000040/* #define DEBUG_STREAMING */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +000041/* #define SUPPORT_IDC */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000042
Daniel Veillardb3de70c2003-12-02 22:32:15 +000043#define ERROR(a, b, c, d)
44#define ERROR5(a, b, c, d, e)
45
Daniel Veillard2fc6df92005-01-30 18:42:55 +000046#define XML_STREAM_STEP_DESC 1
47#define XML_STREAM_STEP_FINAL 2
48#define XML_STREAM_STEP_ROOT 4
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +000049#define XML_STREAM_STEP_ATTR 8
Daniel Veillard2fc6df92005-01-30 18:42:55 +000050
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 {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +000069 struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000070 xmlStreamCompPtr comp; /* the compiled stream */
71 int nbState; /* number of state in the automata */
72 int maxState; /* allocated number of state */
73 int level; /* how deep are we ? */
74 int *states; /* the array of step indexes */
75};
76
77static void xmlFreeStreamComp(xmlStreamCompPtr comp);
78
Daniel Veillardb3de70c2003-12-02 22:32:15 +000079/*
80 * Types are private:
81 */
82
83typedef enum {
84 XML_OP_END=0,
85 XML_OP_ROOT,
86 XML_OP_ELEM,
87 XML_OP_CHILD,
88 XML_OP_ATTR,
89 XML_OP_PARENT,
90 XML_OP_ANCESTOR,
91 XML_OP_NS,
92 XML_OP_ALL
93} xmlPatOp;
94
95
Daniel Veillardd4301ab2005-02-03 22:24:10 +000096typedef struct _xmlStepState xmlStepState;
97typedef xmlStepState *xmlStepStatePtr;
98struct _xmlStepState {
99 int step;
100 xmlNodePtr node;
101};
102
103typedef struct _xmlStepStates xmlStepStates;
104typedef xmlStepStates *xmlStepStatesPtr;
105struct _xmlStepStates {
106 int nbstates;
107 int maxstates;
108 xmlStepStatePtr states;
109};
110
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000111typedef struct _xmlStepOp xmlStepOp;
112typedef xmlStepOp *xmlStepOpPtr;
113struct _xmlStepOp {
114 xmlPatOp op;
115 const xmlChar *value;
116 const xmlChar *value2;
117};
118
Daniel Veillard56de87e2005-02-16 00:22:29 +0000119#define PAT_FROM_ROOT 1
120#define PAT_FROM_CUR 2
121
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000122struct _xmlPattern {
123 void *data; /* the associated template */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000124 xmlDictPtr dict; /* the optional dictionnary */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000125 struct _xmlPattern *next; /* next pattern if | is used */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000126 const xmlChar *pattern; /* the pattern */
127
Daniel Veillard56de87e2005-02-16 00:22:29 +0000128 int flags; /* flags */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000129 int nbStep;
130 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000131 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000132 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000133};
134
135typedef struct _xmlPatParserContext xmlPatParserContext;
136typedef xmlPatParserContext *xmlPatParserContextPtr;
137struct _xmlPatParserContext {
138 const xmlChar *cur; /* the current char being parsed */
139 const xmlChar *base; /* the full expression */
140 int error; /* error code */
141 xmlDictPtr dict; /* the dictionnary if any */
142 xmlPatternPtr comp; /* the result */
143 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000144 const xmlChar **namespaces; /* the namespaces definitions */
145 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000146};
147
148/************************************************************************
149 * *
150 * Type functions *
151 * *
152 ************************************************************************/
153
154/**
155 * xmlNewPattern:
156 *
157 * Create a new XSLT Pattern
158 *
159 * Returns the newly allocated xmlPatternPtr or NULL in case of error
160 */
161static xmlPatternPtr
162xmlNewPattern(void) {
163 xmlPatternPtr cur;
164
165 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
166 if (cur == NULL) {
167 ERROR(NULL, NULL, NULL,
168 "xmlNewPattern : malloc failed\n");
169 return(NULL);
170 }
171 memset(cur, 0, sizeof(xmlPattern));
172 cur->maxStep = 10;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000173 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
174 if (cur->steps == NULL) {
175 xmlFree(cur);
176 ERROR(NULL, NULL, NULL,
177 "xmlNewPattern : malloc failed\n");
178 return(NULL);
179 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000180 return(cur);
181}
182
183/**
184 * xmlFreePattern:
185 * @comp: an XSLT comp
186 *
187 * Free up the memory allocated by @comp
188 */
189void
190xmlFreePattern(xmlPatternPtr comp) {
191 xmlStepOpPtr op;
192 int i;
193
194 if (comp == NULL)
195 return;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000196 if (comp->next != NULL)
197 xmlFreePattern(comp->next);
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000198 if (comp->stream != NULL)
199 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000200 if (comp->pattern != NULL)
201 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000202 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000203 if (comp->dict == NULL) {
204 for (i = 0;i < comp->nbStep;i++) {
205 op = &comp->steps[i];
206 if (op->value != NULL)
207 xmlFree((xmlChar *) op->value);
208 if (op->value2 != NULL)
209 xmlFree((xmlChar *) op->value2);
210 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000211 }
212 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000213 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000214 if (comp->dict != NULL)
215 xmlDictFree(comp->dict);
216
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000217 memset(comp, -1, sizeof(xmlPattern));
218 xmlFree(comp);
219}
220
221/**
222 * xmlFreePatternList:
223 * @comp: an XSLT comp list
224 *
225 * Free up the memory allocated by all the elements of @comp
226 */
227void
228xmlFreePatternList(xmlPatternPtr comp) {
229 xmlPatternPtr cur;
230
231 while (comp != NULL) {
232 cur = comp;
233 comp = comp->next;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +0000234 cur->next = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000235 xmlFreePattern(cur);
236 }
237}
238
239/**
240 * xmlNewPatParserContext:
241 * @pattern: the pattern context
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000242 * @dict: the inherited dictionnary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000243 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
244 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000245 *
246 * Create a new XML pattern parser context
247 *
248 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
249 */
250static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000251xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
252 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000253 xmlPatParserContextPtr cur;
254
255 if (pattern == NULL)
256 return(NULL);
257
258 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
259 if (cur == NULL) {
260 ERROR(NULL, NULL, NULL,
261 "xmlNewPatParserContext : malloc failed\n");
262 return(NULL);
263 }
264 memset(cur, 0, sizeof(xmlPatParserContext));
265 cur->dict = dict;
266 cur->cur = pattern;
267 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000268 if (namespaces != NULL) {
269 int i;
270 for (i = 0;namespaces[2 * i] != NULL;i++);
271 cur->nb_namespaces = i;
272 } else {
273 cur->nb_namespaces = 0;
274 }
275 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000276 return(cur);
277}
278
279/**
280 * xmlFreePatParserContext:
281 * @ctxt: an XSLT parser context
282 *
283 * Free up the memory allocated by @ctxt
284 */
285static void
286xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
287 if (ctxt == NULL)
288 return;
289 memset(ctxt, -1, sizeof(xmlPatParserContext));
290 xmlFree(ctxt);
291}
292
293/**
294 * xmlPatternAdd:
295 * @comp: the compiled match expression
296 * @op: an op
297 * @value: the first value
298 * @value2: the second value
299 *
300 * Add an step to an XSLT Compiled Match
301 *
302 * Returns -1 in case of failure, 0 otherwise.
303 */
304static int
305xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
306 xmlPatternPtr comp,
307 xmlPatOp op, xmlChar * value, xmlChar * value2)
308{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000309 if (comp->nbStep >= comp->maxStep) {
310 xmlStepOpPtr temp;
311 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
312 sizeof(xmlStepOp));
313 if (temp == NULL) {
314 ERROR(ctxt, NULL, NULL,
315 "xmlPatternAdd: realloc failed\n");
316 return (-1);
317 }
318 comp->steps = temp;
319 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000320 }
321 comp->steps[comp->nbStep].op = op;
322 comp->steps[comp->nbStep].value = value;
323 comp->steps[comp->nbStep].value2 = value2;
324 comp->nbStep++;
325 return (0);
326}
327
328#if 0
329/**
330 * xsltSwapTopPattern:
331 * @comp: the compiled match expression
332 *
333 * reverse the two top steps.
334 */
335static void
336xsltSwapTopPattern(xmlPatternPtr comp) {
337 int i;
338 int j = comp->nbStep - 1;
339
340 if (j > 0) {
341 register const xmlChar *tmp;
342 register xmlPatOp op;
343 i = j - 1;
344 tmp = comp->steps[i].value;
345 comp->steps[i].value = comp->steps[j].value;
346 comp->steps[j].value = tmp;
347 tmp = comp->steps[i].value2;
348 comp->steps[i].value2 = comp->steps[j].value2;
349 comp->steps[j].value2 = tmp;
350 op = comp->steps[i].op;
351 comp->steps[i].op = comp->steps[j].op;
352 comp->steps[j].op = op;
353 }
354}
355#endif
356
357/**
358 * xmlReversePattern:
359 * @comp: the compiled match expression
360 *
361 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000362 *
363 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000364 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000365static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000366xmlReversePattern(xmlPatternPtr comp) {
Daniel Veillard56de87e2005-02-16 00:22:29 +0000367 int i, j;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000368
Daniel Veillard56de87e2005-02-16 00:22:29 +0000369 /*
370 * remove the leading // for //a or .//a
371 */
372 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
373 for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
374 comp->steps[i].value = comp->steps[j].value;
375 comp->steps[i].value2 = comp->steps[j].value2;
376 comp->steps[i].op = comp->steps[j].op;
377 }
378 comp->nbStep--;
379 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000380 if (comp->nbStep >= comp->maxStep) {
381 xmlStepOpPtr temp;
382 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
383 sizeof(xmlStepOp));
384 if (temp == NULL) {
385 ERROR(ctxt, NULL, NULL,
386 "xmlReversePattern: realloc failed\n");
387 return (-1);
388 }
389 comp->steps = temp;
390 comp->maxStep *= 2;
391 }
Daniel Veillard56de87e2005-02-16 00:22:29 +0000392 i = 0;
393 j = comp->nbStep - 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000394 while (j > i) {
395 register const xmlChar *tmp;
396 register xmlPatOp op;
397 tmp = comp->steps[i].value;
398 comp->steps[i].value = comp->steps[j].value;
399 comp->steps[j].value = tmp;
400 tmp = comp->steps[i].value2;
401 comp->steps[i].value2 = comp->steps[j].value2;
402 comp->steps[j].value2 = tmp;
403 op = comp->steps[i].op;
404 comp->steps[i].op = comp->steps[j].op;
405 comp->steps[j].op = op;
406 j--;
407 i++;
408 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000409 comp->steps[comp->nbStep].value = NULL;
410 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000411 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000412 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000413}
414
415/************************************************************************
416 * *
417 * The interpreter for the precompiled patterns *
418 * *
419 ************************************************************************/
420
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000421static int
422xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
423 if ((states->states == NULL) || (states->maxstates <= 0)) {
424 states->maxstates = 4;
425 states->nbstates = 0;
426 states->states = xmlMalloc(4 * sizeof(xmlStepState));
427 }
428 else if (states->maxstates <= states->nbstates) {
429 xmlStepState *tmp;
430
431 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
432 2 * states->maxstates * sizeof(xmlStepState));
433 if (tmp == NULL)
434 return(-1);
435 states->states = tmp;
436 states->maxstates *= 2;
437 }
438 states->states[states->nbstates].step = step;
439 states->states[states->nbstates++].node = node;
440#if 0
441 fprintf(stderr, "Push: %d, %s\n", step, node->name);
442#endif
443 return(0);
444}
445
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000446/**
447 * xmlPatMatch:
448 * @comp: the precompiled pattern
449 * @node: a node
450 *
451 * Test wether the node matches the pattern
452 *
453 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
454 */
455static int
456xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
457 int i;
458 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000459 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000460
461 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000462 i = 0;
463restart:
464 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000465 step = &comp->steps[i];
466 switch (step->op) {
467 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000468 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000469 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000470 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000471 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000472 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000473 if ((node->type == XML_DOCUMENT_NODE) ||
474#ifdef LIBXML_DOCB_ENABLED
475 (node->type == XML_DOCB_DOCUMENT_NODE) ||
476#endif
477 (node->type == XML_HTML_DOCUMENT_NODE))
478 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000479 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000480 case XML_OP_ELEM:
481 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000482 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000483 if (step->value == NULL)
484 continue;
485 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000486 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000487 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000488 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000489
490 /* Namespace test */
491 if (node->ns == NULL) {
492 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000493 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000494 } else if (node->ns->href != NULL) {
495 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000496 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000497 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000498 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000499 }
500 continue;
501 case XML_OP_CHILD: {
502 xmlNodePtr lst;
503
504 if ((node->type != XML_ELEMENT_NODE) &&
505 (node->type != XML_DOCUMENT_NODE) &&
506#ifdef LIBXML_DOCB_ENABLED
507 (node->type != XML_DOCB_DOCUMENT_NODE) &&
508#endif
509 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000510 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000511
512 lst = node->children;
513
514 if (step->value != NULL) {
515 while (lst != NULL) {
516 if ((lst->type == XML_ELEMENT_NODE) &&
517 (step->value[0] == lst->name[0]) &&
518 (xmlStrEqual(step->value, lst->name)))
519 break;
520 lst = lst->next;
521 }
522 if (lst != NULL)
523 continue;
524 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000525 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000526 }
527 case XML_OP_ATTR:
528 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000529 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000530 if (step->value != NULL) {
531 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000532 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000533 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000534 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000535 }
536 /* Namespace test */
537 if (node->ns == NULL) {
538 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000539 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000540 } else if (step->value2 != NULL) {
541 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000542 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000543 }
544 continue;
545 case XML_OP_PARENT:
546 if ((node->type == XML_DOCUMENT_NODE) ||
547 (node->type == XML_HTML_DOCUMENT_NODE) ||
548#ifdef LIBXML_DOCB_ENABLED
549 (node->type == XML_DOCB_DOCUMENT_NODE) ||
550#endif
551 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000552 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000553 node = node->parent;
554 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000555 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000556 if (step->value == NULL)
557 continue;
558 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000559 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000560 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000561 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000562 /* Namespace test */
563 if (node->ns == NULL) {
564 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000565 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000566 } else if (node->ns->href != NULL) {
567 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000568 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000569 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000570 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000571 }
572 continue;
573 case XML_OP_ANCESTOR:
574 /* TODO: implement coalescing of ANCESTOR/NODE ops */
575 if (step->value == NULL) {
576 i++;
577 step = &comp->steps[i];
578 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000579 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000580 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000581 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000582 if (step->value == NULL)
583 return(-1);
584 }
585 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000586 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000587 if ((node->type == XML_DOCUMENT_NODE) ||
588 (node->type == XML_HTML_DOCUMENT_NODE) ||
589#ifdef LIBXML_DOCB_ENABLED
590 (node->type == XML_DOCB_DOCUMENT_NODE) ||
591#endif
592 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000593 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000594 node = node->parent;
595 while (node != NULL) {
596 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000597 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000598 if ((node->type == XML_ELEMENT_NODE) &&
599 (step->value[0] == node->name[0]) &&
600 (xmlStrEqual(step->value, node->name))) {
601 /* Namespace test */
602 if (node->ns == NULL) {
603 if (step->value2 == NULL)
604 break;
605 } else if (node->ns->href != NULL) {
606 if ((step->value2 != NULL) &&
607 (xmlStrEqual(step->value2, node->ns->href)))
608 break;
609 }
610 }
611 node = node->parent;
612 }
613 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000614 goto rollback;
615 /*
616 * prepare a potential rollback from here
617 * for ancestors of that node.
618 */
619 if (step->op == XML_OP_ANCESTOR)
620 xmlPatPushState(&states, i, node);
621 else
622 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000623 continue;
624 case XML_OP_NS:
625 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000626 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000627 if (node->ns == NULL) {
628 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000629 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000630 } else if (node->ns->href != NULL) {
631 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000632 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000633 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000634 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000635 }
636 break;
637 case XML_OP_ALL:
638 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000639 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000640 break;
641 }
642 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000643found:
644 if (states.states != NULL) {
645 /* Free the rollback states */
646 xmlFree(states.states);
647 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000648 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000649rollback:
650 /* got an error try to rollback */
651 if (states.states == NULL)
652 return(0);
653 if (states.nbstates <= 0) {
654 xmlFree(states.states);
655 return(0);
656 }
657 states.nbstates--;
658 i = states.states[states.nbstates].step;
659 node = states.states[states.nbstates].node;
660#if 0
661 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
662#endif
663 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000664}
665
666/************************************************************************
667 * *
668 * Dedicated parser for templates *
669 * *
670 ************************************************************************/
671
672#define TODO \
673 xmlGenericError(xmlGenericErrorContext, \
674 "Unimplemented block at %s:%d\n", \
675 __FILE__, __LINE__);
676#define CUR (*ctxt->cur)
677#define SKIP(val) ctxt->cur += (val)
678#define NXT(val) ctxt->cur[(val)]
679#define CUR_PTR ctxt->cur
680
681#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000682 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000683
684#define CURRENT (*ctxt->cur)
685#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
686
687
688#define PUSH(op, val, val2) \
689 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
690
691#define XSLT_ERROR(X) \
692 { xsltError(ctxt, __FILE__, __LINE__, X); \
693 ctxt->error = (X); return; }
694
695#define XSLT_ERROR0(X) \
696 { xsltError(ctxt, __FILE__, __LINE__, X); \
697 ctxt->error = (X); return(0); }
698
699#if 0
700/**
701 * xmlPatScanLiteral:
702 * @ctxt: the XPath Parser context
703 *
704 * Parse an XPath Litteral:
705 *
706 * [29] Literal ::= '"' [^"]* '"'
707 * | "'" [^']* "'"
708 *
709 * Returns the Literal parsed or NULL
710 */
711
712static xmlChar *
713xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
714 const xmlChar *q, *cur;
715 xmlChar *ret = NULL;
716 int val, len;
717
718 SKIP_BLANKS;
719 if (CUR == '"') {
720 NEXT;
721 cur = q = CUR_PTR;
722 val = xmlStringCurrentChar(NULL, cur, &len);
723 while ((IS_CHAR(val)) && (val != '"')) {
724 cur += len;
725 val = xmlStringCurrentChar(NULL, cur, &len);
726 }
727 if (!IS_CHAR(val)) {
728 ctxt->error = 1;
729 return(NULL);
730 } else {
731 ret = xmlStrndup(q, cur - q);
732 }
733 cur += len;
734 CUR_PTR = cur;
735 } else if (CUR == '\'') {
736 NEXT;
737 cur = q = CUR_PTR;
738 val = xmlStringCurrentChar(NULL, cur, &len);
739 while ((IS_CHAR(val)) && (val != '\'')) {
740 cur += len;
741 val = xmlStringCurrentChar(NULL, cur, &len);
742 }
743 if (!IS_CHAR(val)) {
744 ctxt->error = 1;
745 return(NULL);
746 } else {
747 ret = xmlStrndup(q, cur - q);
748 }
749 cur += len;
750 CUR_PTR = cur;
751 } else {
752 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
753 ctxt->error = 1;
754 return(NULL);
755 }
756 return(ret);
757}
758#endif
759
760/**
761 * xmlPatScanName:
762 * @ctxt: the XPath Parser context
763 *
764 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
765 * CombiningChar | Extender
766 *
767 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
768 *
769 * [6] Names ::= Name (S Name)*
770 *
771 * Returns the Name parsed or NULL
772 */
773
774static xmlChar *
775xmlPatScanName(xmlPatParserContextPtr ctxt) {
776 const xmlChar *q, *cur;
777 xmlChar *ret = NULL;
778 int val, len;
779
780 SKIP_BLANKS;
781
782 cur = q = CUR_PTR;
783 val = xmlStringCurrentChar(NULL, cur, &len);
784 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
785 return(NULL);
786
787 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
788 (val == '.') || (val == '-') ||
789 (val == '_') ||
790 (IS_COMBINING(val)) ||
791 (IS_EXTENDER(val))) {
792 cur += len;
793 val = xmlStringCurrentChar(NULL, cur, &len);
794 }
795 ret = xmlStrndup(q, cur - q);
796 CUR_PTR = cur;
797 return(ret);
798}
799
800/**
801 * xmlPatScanNCName:
802 * @ctxt: the XPath Parser context
803 *
804 * Parses a non qualified name
805 *
806 * Returns the Name parsed or NULL
807 */
808
809static xmlChar *
810xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
811 const xmlChar *q, *cur;
812 xmlChar *ret = NULL;
813 int val, len;
814
815 SKIP_BLANKS;
816
817 cur = q = CUR_PTR;
818 val = xmlStringCurrentChar(NULL, cur, &len);
819 if (!IS_LETTER(val) && (val != '_'))
820 return(NULL);
821
822 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
823 (val == '.') || (val == '-') ||
824 (val == '_') ||
825 (IS_COMBINING(val)) ||
826 (IS_EXTENDER(val))) {
827 cur += len;
828 val = xmlStringCurrentChar(NULL, cur, &len);
829 }
830 ret = xmlStrndup(q, cur - q);
831 CUR_PTR = cur;
832 return(ret);
833}
834
835#if 0
836/**
837 * xmlPatScanQName:
838 * @ctxt: the XPath Parser context
839 * @prefix: the place to store the prefix
840 *
841 * Parse a qualified name
842 *
843 * Returns the Name parsed or NULL
844 */
845
846static xmlChar *
847xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
848 xmlChar *ret = NULL;
849
850 *prefix = NULL;
851 ret = xmlPatScanNCName(ctxt);
852 if (CUR == ':') {
853 *prefix = ret;
854 NEXT;
855 ret = xmlPatScanNCName(ctxt);
856 }
857 return(ret);
858}
859#endif
860
861/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000862 * xmlCompileAttributeTest:
863 * @ctxt: the compilation context
864 *
865 * Compile an attribute test.
866 */
867static void
868xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
869 xmlChar *token = NULL;
870 xmlChar *name = NULL;
871 xmlChar *URL = NULL;
872
873 name = xmlPatScanNCName(ctxt);
874 if (name == NULL) {
875 if (CUR == '*') {
876 PUSH(XML_OP_ATTR, NULL, NULL);
877 } else {
878 ERROR(NULL, NULL, NULL,
879 "xmlCompileAttributeTest : Name expected\n");
880 ctxt->error = 1;
881 }
882 return;
883 }
884 if (CUR == ':') {
885 int i;
886 xmlChar *prefix = name;
887
888 NEXT;
889 /*
890 * This is a namespace match
891 */
892 token = xmlPatScanName(ctxt);
893 for (i = 0;i < ctxt->nb_namespaces;i++) {
894 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
895 URL = xmlStrdup(ctxt->namespaces[2 * i]);
896 break;
897 }
898 }
899 if (i >= ctxt->nb_namespaces) {
900 ERROR5(NULL, NULL, NULL,
901 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
902 prefix);
903 ctxt->error = 1;
904 goto error;
905 }
906
907 xmlFree(prefix);
908 if (token == NULL) {
909 if (CUR == '*') {
910 NEXT;
911 PUSH(XML_OP_ATTR, NULL, URL);
912 } else {
913 ERROR(NULL, NULL, NULL,
914 "xmlCompileAttributeTest : Name expected\n");
915 ctxt->error = 1;
916 goto error;
917 }
918 } else {
919 PUSH(XML_OP_ATTR, token, URL);
920 }
921 } else {
922 PUSH(XML_OP_ATTR, name, NULL);
923 }
924 return;
925error:
926 if (URL != NULL)
927 xmlFree(URL);
928 if (token != NULL)
929 xmlFree(token);
930}
931
932
933/**
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000934 * xmlCompileStepPattern:
935 * @ctxt: the compilation context
936 *
937 * Compile the Step Pattern and generates a precompiled
938 * form suitable for fast matching.
939 *
940 * [3] Step ::= '.' | NameTest
941 * [4] NameTest ::= QName | '*' | NCName ':' '*'
942 */
943
944static void
945xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
946 xmlChar *token = NULL;
947 xmlChar *name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000948 xmlChar *URL = NULL;
949
950 SKIP_BLANKS;
951 if (CUR == '.') {
952 NEXT;
953 PUSH(XML_OP_ELEM, NULL, NULL);
954 return;
955 }
956 name = xmlPatScanNCName(ctxt);
957 if (name == NULL) {
958 if (CUR == '*') {
959 NEXT;
960 PUSH(XML_OP_ALL, NULL, NULL);
961 return;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000962 } else if (CUR == '@') {
963 NEXT;
964 xmlCompileAttributeTest(ctxt);
965 if (ctxt->error != 0)
966 goto error;
967 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000968 } else {
969 ERROR(NULL, NULL, NULL,
970 "xmlCompileStepPattern : Name expected\n");
971 ctxt->error = 1;
972 return;
973 }
974 }
975 SKIP_BLANKS;
976 if (CUR == ':') {
977 NEXT;
978 if (CUR != ':') {
979 xmlChar *prefix = name;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000980 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000981
982 /*
983 * This is a namespace match
984 */
985 token = xmlPatScanName(ctxt);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000986 for (i = 0;i < ctxt->nb_namespaces;i++) {
987 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Daniel Veillard0996a162005-02-05 14:00:10 +0000988 URL = xmlStrdup(ctxt->namespaces[2 * i]);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000989 break;
990 }
991 }
992 if (i >= ctxt->nb_namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000993 ERROR5(NULL, NULL, NULL,
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000994 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
995 prefix);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000996 ctxt->error = 1;
997 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000998 }
999 xmlFree(prefix);
1000 if (token == NULL) {
1001 if (CUR == '*') {
1002 NEXT;
1003 PUSH(XML_OP_NS, URL, NULL);
1004 } else {
1005 ERROR(NULL, NULL, NULL,
1006 "xmlCompileStepPattern : Name expected\n");
1007 ctxt->error = 1;
1008 goto error;
1009 }
1010 } else {
1011 PUSH(XML_OP_ELEM, token, URL);
1012 }
1013 } else {
1014 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001015 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1016 xmlFree(name);
1017 name = xmlPatScanName(ctxt);
1018 if (name == NULL) {
1019 if (CUR == '*') {
1020 NEXT;
1021 PUSH(XML_OP_ALL, NULL, NULL);
1022 return;
1023 } else {
1024 ERROR(NULL, NULL, NULL,
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001025 "xmlCompileStepPattern : QName expected\n");
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001026 ctxt->error = 1;
1027 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001028 }
1029 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001030 if (CUR == ':') {
1031 xmlChar *prefix = name;
1032 int i;
1033
1034 NEXT;
1035 /*
1036 * This is a namespace match
1037 */
1038 token = xmlPatScanName(ctxt);
1039 for (i = 0;i < ctxt->nb_namespaces;i++) {
1040 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1041 URL = xmlStrdup(ctxt->namespaces[2 * i]);
1042 break;
1043 }
1044 }
1045 if (i >= ctxt->nb_namespaces) {
1046 ERROR5(NULL, NULL, NULL,
1047 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1048 prefix);
1049 ctxt->error = 1;
1050 goto error;
1051 }
1052 xmlFree(prefix);
1053 if (token == NULL) {
1054 if (CUR == '*') {
1055 NEXT;
1056 PUSH(XML_OP_NS, URL, NULL);
1057 } else {
1058 ERROR(NULL, NULL, NULL,
1059 "xmlCompileStepPattern : Name expected\n");
1060 ctxt->error = 1;
1061 goto error;
1062 }
1063 } else {
1064 PUSH(XML_OP_CHILD, token, URL);
1065 }
1066 } else
1067 PUSH(XML_OP_CHILD, name, NULL);
1068 return;
1069 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1070 xmlFree(name);
1071 name = NULL;
1072 xmlCompileAttributeTest(ctxt);
1073 if (ctxt->error != 0)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001074 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001075 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001076 } else {
1077 ERROR(NULL, NULL, NULL,
1078 "xmlCompileStepPattern : 'child' or 'attribute' expected\n");
1079 ctxt->error = 1;
1080 goto error;
1081 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001082 xmlFree(name);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001083 }
1084 } else if (CUR == '*') {
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001085 if (name != NULL) {
1086 ctxt->error = 1;
1087 goto error;
1088 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001089 NEXT;
1090 PUSH(XML_OP_ALL, token, NULL);
1091 } else {
1092 if (name == NULL) {
1093 ctxt->error = 1;
1094 goto error;
1095 }
1096 PUSH(XML_OP_ELEM, name, NULL);
1097 }
1098 return;
1099error:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001100 if (URL != NULL)
1101 xmlFree(URL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001102 if (token != NULL)
1103 xmlFree(token);
1104 if (name != NULL)
1105 xmlFree(name);
1106}
1107
1108/**
1109 * xmlCompilePathPattern:
1110 * @ctxt: the compilation context
1111 *
1112 * Compile the Path Pattern and generates a precompiled
1113 * form suitable for fast matching.
1114 *
1115 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1116 */
1117static void
1118xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1119 SKIP_BLANKS;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001120 if (CUR == '/') {
1121 ctxt->comp->flags |= PAT_FROM_ROOT;
1122 } else if (CUR == '.') {
1123 ctxt->comp->flags |= PAT_FROM_CUR;
1124 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001125 if ((CUR == '/') && (NXT(1) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001126 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001127 NEXT;
1128 NEXT;
1129 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001130 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001131 NEXT;
1132 NEXT;
1133 NEXT;
1134 }
1135 if (CUR == '@') {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001136 NEXT;
1137 xmlCompileAttributeTest(ctxt);
1138 SKIP_BLANKS;
1139 if ((CUR != 0) || (CUR == '|')) {
1140 xmlCompileStepPattern(ctxt);
1141 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001142 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001143 if (CUR == '/') {
1144 PUSH(XML_OP_ROOT, NULL, NULL);
1145 NEXT;
1146 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001147 xmlCompileStepPattern(ctxt);
1148 SKIP_BLANKS;
1149 while (CUR == '/') {
1150 if ((CUR == '/') && (NXT(1) == '/')) {
1151 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1152 NEXT;
1153 NEXT;
1154 SKIP_BLANKS;
1155 xmlCompileStepPattern(ctxt);
1156 } else {
1157 PUSH(XML_OP_PARENT, NULL, NULL);
1158 NEXT;
1159 SKIP_BLANKS;
1160 if ((CUR != 0) || (CUR == '|')) {
1161 xmlCompileStepPattern(ctxt);
1162 }
1163 }
1164 }
1165 }
Daniel Veillard56de87e2005-02-16 00:22:29 +00001166 if (CUR != 0) {
1167 ERROR5(NULL, NULL, NULL,
1168 "Failed to compile pattern %s\n", ctxt->base);
1169 ctxt->error = 1;
1170 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001171error:
1172 return;
1173}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001174
1175/************************************************************************
1176 * *
1177 * The streaming code *
1178 * *
1179 ************************************************************************/
1180
1181#ifdef DEBUG_STREAMING
1182static void
1183xmlDebugStreamComp(xmlStreamCompPtr stream) {
1184 int i;
1185
1186 if (stream == NULL) {
1187 printf("Stream: NULL\n");
1188 return;
1189 }
1190 printf("Stream: %d steps\n", stream->nbStep);
1191 for (i = 0;i < stream->nbStep;i++) {
1192 if (stream->steps[i].ns != NULL) {
1193 printf("{%s}", stream->steps[i].ns);
1194 }
1195 if (stream->steps[i].name == NULL) {
1196 printf("* ");
1197 } else {
1198 printf("%s ", stream->steps[i].name);
1199 }
1200 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1201 printf("root ");
1202 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1203 printf("// ");
1204 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1205 printf("final ");
1206 printf("\n");
1207 }
1208}
1209static void
1210xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1211 int i;
1212
1213 if (ctxt == NULL) {
1214 printf("Stream: NULL\n");
1215 return;
1216 }
1217 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1218 if (match)
1219 printf("matches\n");
1220 else
1221 printf("\n");
1222 for (i = 0;i < ctxt->nbState;i++) {
1223 if (ctxt->states[2 * i] < 0)
1224 printf(" %d: free\n", i);
1225 else {
1226 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1227 ctxt->states[(2 * i) + 1]);
1228 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1229 XML_STREAM_STEP_DESC)
1230 printf(" //\n");
1231 else
1232 printf("\n");
1233 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001234 }
1235}
1236#endif
1237/**
1238 * xmlNewStreamComp:
1239 * @size: the number of expected steps
1240 *
1241 * build a new compiled pattern for streaming
1242 *
1243 * Returns the new structure or NULL in case of error.
1244 */
1245static xmlStreamCompPtr
1246xmlNewStreamComp(int size) {
1247 xmlStreamCompPtr cur;
1248
1249 if (size < 4)
1250 size = 4;
1251
1252 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1253 if (cur == NULL) {
1254 ERROR(NULL, NULL, NULL,
1255 "xmlNewStreamComp: malloc failed\n");
1256 return(NULL);
1257 }
1258 memset(cur, 0, sizeof(xmlStreamComp));
1259 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1260 if (cur->steps == NULL) {
1261 xmlFree(cur);
1262 ERROR(NULL, NULL, NULL,
1263 "xmlNewStreamComp: malloc failed\n");
1264 return(NULL);
1265 }
1266 cur->nbStep = 0;
1267 cur->maxStep = size;
1268 return(cur);
1269}
1270
1271/**
1272 * xmlFreeStreamComp:
1273 * @comp: the compiled pattern for streaming
1274 *
1275 * Free the compiled pattern for streaming
1276 */
1277static void
1278xmlFreeStreamComp(xmlStreamCompPtr comp) {
1279 if (comp != NULL) {
1280 if (comp->steps != NULL)
1281 xmlFree(comp->steps);
1282 if (comp->dict != NULL)
1283 xmlDictFree(comp->dict);
1284 xmlFree(comp);
1285 }
1286}
1287
1288/**
1289 * xmlStreamCompAddStep:
1290 * @comp: the compiled pattern for streaming
1291 * @name: the first string, the name, or NULL for *
1292 * @ns: the second step, the namespace name
1293 * @flags: the flags for that step
1294 *
1295 * Add a new step to the compiled pattern
1296 *
1297 * Returns -1 in case of error or the step index if successful
1298 */
1299static int
1300xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1301 const xmlChar *ns, int flags) {
1302 xmlStreamStepPtr cur;
1303
1304 if (comp->nbStep >= comp->maxStep) {
1305 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1306 comp->maxStep * 2 * sizeof(xmlStreamStep));
1307 if (cur == NULL) {
1308 ERROR(NULL, NULL, NULL,
1309 "xmlNewStreamComp: malloc failed\n");
1310 return(-1);
1311 }
1312 comp->steps = cur;
1313 comp->maxStep *= 2;
1314 }
1315 cur = &comp->steps[comp->nbStep++];
1316 cur->flags = flags;
1317 cur->name = name;
1318 cur->ns = ns;
1319 return(comp->nbStep - 1);
1320}
1321
1322/**
1323 * xmlStreamCompile:
1324 * @comp: the precompiled pattern
1325 *
1326 * Tries to stream compile a pattern
1327 *
1328 * Returns -1 in case of failure and 0 in case of success.
1329 */
1330static int
1331xmlStreamCompile(xmlPatternPtr comp) {
1332 xmlStreamCompPtr stream;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001333 int i, s = 0, root = 0, flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001334
1335 if ((comp == NULL) || (comp->steps == NULL))
1336 return(-1);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001337 /*
1338 * special case for .
1339 */
1340 if ((comp->nbStep == 1) &&
1341 (comp->steps[0].op == XML_OP_ELEM) &&
1342 (comp->steps[0].value == NULL) &&
1343 (comp->steps[0].value2 == NULL)) {
1344 stream = xmlNewStreamComp(0);
1345 if (stream == NULL)
1346 return(-1);
1347 comp->stream = stream;
1348 return(0);
1349 }
1350
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001351 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1352 if (stream == NULL)
1353 return(-1);
1354 if (comp->dict != NULL) {
1355 stream->dict = comp->dict;
1356 xmlDictReference(stream->dict);
1357 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001358
1359 /*
1360 * Skip leading ./ on relative paths
1361 */
1362 i = 0;
1363 while ((comp->flags & PAT_FROM_CUR) && (comp->nbStep > i + 2) &&
1364 (comp->steps[i].op == XML_OP_ELEM) &&
1365 (comp->steps[i].value == NULL) &&
1366 (comp->steps[i].value2 == NULL) &&
1367 (comp->steps[i + 1].op == XML_OP_PARENT)) {
1368 i += 2;
1369 }
1370 for (;i < comp->nbStep;i++) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001371 switch (comp->steps[i].op) {
1372 case XML_OP_END:
1373 break;
1374 case XML_OP_ROOT:
1375 if (i != 0)
1376 goto error;
1377 root = 1;
1378 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001379 case XML_OP_NS:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001380 s = xmlStreamCompAddStep(stream, NULL,
1381 comp->steps[i].value, flags);
1382 flags = 0;
1383 if (s < 0)
1384 goto error;
1385 break;
1386 case XML_OP_ATTR:
1387 flags |= XML_STREAM_STEP_ATTR;
1388 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1389 comp->steps[i].value2, flags);
1390 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001391 if (s < 0)
1392 goto error;
1393 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001394 case XML_OP_ELEM:
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001395 if ((comp->steps[i].value == NULL) &&
1396 (comp->steps[i].value2 == NULL) &&
1397 (comp->nbStep > i + 2) &&
1398 (comp->steps[i + 1].op == XML_OP_PARENT)) {
1399 i++;
1400 continue;
1401 }
1402 case XML_OP_CHILD:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001403 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1404 comp->steps[i].value2, flags);
1405 flags = 0;
1406 if (s < 0)
1407 goto error;
1408 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001409 case XML_OP_ALL:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001410 s = xmlStreamCompAddStep(stream, NULL, NULL, flags);
1411 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001412 if (s < 0)
1413 goto error;
1414 break;
1415 case XML_OP_PARENT:
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001416 if ((comp->nbStep > i + 1) &&
1417 (comp->steps[i + 1].op == XML_OP_ELEM) &&
1418 (comp->steps[i + 1].value == NULL) &&
1419 (comp->steps[i + 1].value2 == NULL)) {
1420 i++;
1421 continue;
1422 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001423 break;
1424 case XML_OP_ANCESTOR:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001425 flags |= XML_STREAM_STEP_DESC;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001426 break;
1427 }
1428 }
1429 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1430 if (root)
1431 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1432#ifdef DEBUG_STREAMING
1433 xmlDebugStreamComp(stream);
1434#endif
1435 comp->stream = stream;
1436 return(0);
1437error:
1438 xmlFreeStreamComp(stream);
1439 return(0);
1440}
1441
1442/**
1443 * xmlNewStreamCtxt:
1444 * @size: the number of expected states
1445 *
1446 * build a new stream context
1447 *
1448 * Returns the new structure or NULL in case of error.
1449 */
1450static xmlStreamCtxtPtr
1451xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1452 xmlStreamCtxtPtr cur;
1453
1454 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1455 if (cur == NULL) {
1456 ERROR(NULL, NULL, NULL,
1457 "xmlNewStreamCtxt: malloc failed\n");
1458 return(NULL);
1459 }
1460 memset(cur, 0, sizeof(xmlStreamCtxt));
1461 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1462 if (cur->states == NULL) {
1463 xmlFree(cur);
1464 ERROR(NULL, NULL, NULL,
1465 "xmlNewStreamCtxt: malloc failed\n");
1466 return(NULL);
1467 }
1468 cur->nbState = 0;
1469 cur->maxState = 4;
1470 cur->level = 0;
1471 cur->comp = stream;
1472 return(cur);
1473}
1474
1475/**
1476 * xmlFreeStreamCtxt:
1477 * @stream: the stream context
1478 *
1479 * Free the stream context
1480 */
1481void
1482xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001483 xmlStreamCtxtPtr next;
1484
1485 while (stream != NULL) {
1486 next = stream->next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001487 if (stream->states != NULL)
1488 xmlFree(stream->states);
1489 xmlFree(stream);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001490 stream = next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001491 }
1492}
1493
1494/**
1495 * xmlStreamCtxtAddState:
1496 * @comp: the stream context
1497 * @idx: the step index for that streaming state
1498 *
1499 * Add a new state to the stream context
1500 *
1501 * Returns -1 in case of error or the state index if successful
1502 */
1503static int
1504xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1505 int i;
1506 for (i = 0;i < comp->nbState;i++) {
1507 if (comp->states[2 * i] < 0) {
1508 comp->states[2 * i] = idx;
1509 comp->states[2 * i + 1] = level;
1510 return(i);
1511 }
1512 }
1513 if (comp->nbState >= comp->maxState) {
1514 int *cur;
1515
1516 cur = (int *) xmlRealloc(comp->states,
1517 comp->maxState * 4 * sizeof(int));
1518 if (cur == NULL) {
1519 ERROR(NULL, NULL, NULL,
1520 "xmlNewStreamCtxt: malloc failed\n");
1521 return(-1);
1522 }
1523 comp->states = cur;
1524 comp->maxState *= 2;
1525 }
1526 comp->states[2 * comp->nbState] = idx;
1527 comp->states[2 * comp->nbState++ + 1] = level;
1528 return(comp->nbState - 1);
1529}
1530
1531/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001532 * xmlStreamPushInternal:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001533 * @stream: the stream context
1534 * @name: the current name
1535 * @ns: the namespace name
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001536 * @nodeType: the type of the node
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001537 *
1538 * push new data onto the stream. NOTE: if the call xmlPatterncompile()
1539 * indicated a dictionnary, then strings for name and ns will be expected
1540 * to come from the dictionary.
1541 * Both @name and @ns being NULL means the / i.e. the root of the document.
1542 * This can also act as a reset.
1543 *
1544 * Returns: -1 in case of error, 1 if the current state in the stream is a
1545 * match and 0 otherwise.
1546 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001547static int
1548xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1549 const xmlChar *name, const xmlChar *ns,
1550 xmlElementType nodeType) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001551 int ret = 0, err = 0, tmp, i, m, match, step, desc, final;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001552 xmlStreamCompPtr comp;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001553#ifdef DEBUG_STREAMING
1554 xmlStreamCtxtPtr orig = stream;
1555#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001556
1557 if ((stream == NULL) || (stream->nbState < 0))
1558 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001559
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001560 while (stream != NULL) {
1561 comp = stream->comp;
1562 if ((name == NULL) && (ns == NULL)) {
1563 stream->nbState = 0;
1564 stream->level = 0;
1565 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1566 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1567 if (tmp < 0)
1568 err++;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001569 if (comp->nbStep == 0)
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001570 ret = 1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001571 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001572 continue; /* while */
1573 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001574 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001575 continue; /* while */
1576 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001577
1578 /*
1579 * Fast check for ".".
1580 */
1581 if (comp->nbStep == 0) {
1582 if (nodeType == XML_ELEMENT_NODE)
1583 ret = 1;
1584 goto stream_next;
1585 }
1586
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001587 /*
1588 * Check evolution of existing states
1589 */
1590 m = stream->nbState;
1591 for (i = 0;i < m;i++) {
1592 match = 0;
1593 step = stream->states[2 * i];
1594 /* dead states */
1595 if (step < 0) continue;
1596 /* skip new states just added */
1597 if (stream->states[(2 * i) + 1] > stream->level)
1598 continue;
1599 /* skip continuations */
1600 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1601 if ((stream->states[(2 * i) + 1] < stream->level) && (!desc))
1602 continue;
1603
1604 /* discard old states */
1605 /* something needed about old level discarded */
1606
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001607 /*
1608 * Check for correct node-type.
1609 */
1610 if ((comp->steps[step].flags & XML_STREAM_STEP_ATTR) &&
1611 (nodeType != XML_ATTRIBUTE_NODE))
1612 continue;
1613
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001614 if (comp->dict) {
1615 if (comp->steps[step].name == NULL) {
1616 if (comp->steps[step].ns == NULL)
1617 match = 1;
1618 else
1619 match = (comp->steps[step].ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001620 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001621 match = ((comp->steps[step].name == name) &&
1622 (comp->steps[step].ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001623 }
1624 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001625 if (comp->steps[step].name == NULL) {
1626 if (comp->steps[step].ns == NULL)
1627 match = 1;
1628 else
1629 match = xmlStrEqual(comp->steps[step].ns, ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001630 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001631 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
1632 (xmlStrEqual(comp->steps[step].ns, ns)));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001633 }
1634 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001635 if (match) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001636 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
1637 if (desc) {
1638 if (final) {
1639 ret = 1;
1640 } else {
1641 /* descending match create a new state */
1642 xmlStreamCtxtAddState(stream, step + 1,
1643 stream->level + 1);
1644 }
1645 } else {
1646 if (final) {
1647 ret = 1;
1648 } else {
1649 xmlStreamCtxtAddState(stream, step + 1,
1650 stream->level + 1);
1651 }
1652 }
1653 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001654 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001655
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001656 /*
1657 * Check creating a new state.
1658 */
1659 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001660
1661 /*
1662 * Check the start only if this is a "desc" evaluation
1663 * of if we are at the first level of evaluation.
1664 */
1665#ifdef SUPPORT_IDC
1666 desc = comp->steps[0].flags & XML_STREAM_STEP_DESC;
1667 if ((desc || (stream->level == 1)) &&
1668 (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT))) {
1669
1670 /*
1671 * Workaround for missing "self::node()" on "@foo".
1672 */
1673 if (comp->steps[0].flags & XML_STREAM_STEP_ATTR) {
1674 xmlStreamCtxtAddState(stream, 0, stream->level);
1675 goto stream_next;
1676 }
1677#else
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001678 if (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT)) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001679#endif
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001680 match = 0;
1681 if (comp->dict) {
1682 if (comp->steps[0].name == NULL) {
1683 if (comp->steps[0].ns == NULL)
1684 match = 1;
1685 else
1686 match = (comp->steps[0].ns == ns);
1687 } else {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001688#ifdef SUPPORT_IDC
1689 /*
1690 * Workaround for missing "self::node() on "foo".
1691 */
1692 if (!desc) {
1693 xmlStreamCtxtAddState(stream, 0, stream->level);
1694 goto stream_next;
1695 } else {
1696 match = ((comp->steps[0].name == name) &&
1697 (comp->steps[0].ns == ns));
1698 }
1699#else
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001700 match = ((comp->steps[0].name == name) &&
1701 (comp->steps[0].ns == ns));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001702#endif
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001703 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001704 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001705 if (comp->steps[0].name == NULL) {
1706 if (comp->steps[0].ns == NULL)
1707 match = 1;
1708 else
1709 match = xmlStrEqual(comp->steps[0].ns, ns);
1710 } else {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001711#ifdef SUPPORT_IDC
1712 /*
1713 * Workaround for missing "self::node() on "foo".
1714 */
1715 if (!desc) {
1716 xmlStreamCtxtAddState(stream, 0, stream->level);
1717 goto stream_next;
1718 } else {
1719 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1720 (xmlStrEqual(comp->steps[0].ns, ns)));
1721 }
1722#else
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001723 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1724 (xmlStrEqual(comp->steps[0].ns, ns)));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001725#endif
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001726 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001727 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001728 if (match) {
1729 if (comp->steps[0].flags & XML_STREAM_STEP_FINAL)
1730 ret = 1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001731 else
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001732 xmlStreamCtxtAddState(stream, 1, stream->level);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001733 }
1734 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001735stream_next:
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001736 stream = stream->next;
1737 } /* while stream != NULL */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001738
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001739 if (err > 0)
1740 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001741#ifdef DEBUG_STREAMING
1742 xmlDebugStreamCtxt(orig, ret);
1743#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001744 return(ret);
1745}
1746
1747/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001748 * xmlStreamPush:
1749 * @stream: the stream context
1750 * @name: the current name
1751 * @ns: the namespace name
1752 *
1753 * push new data onto the stream. NOTE: if the call xmlPatterncompile()
1754 * indicated a dictionnary, then strings for name and ns will be expected
1755 * to come from the dictionary.
1756 * Both @name and @ns being NULL means the / i.e. the root of the document.
1757 * This can also act as a reset.
1758 *
1759 * Returns: -1 in case of error, 1 if the current state in the stream is a
1760 * match and 0 otherwise.
1761 */
1762int
1763xmlStreamPush(xmlStreamCtxtPtr stream,
1764 const xmlChar *name, const xmlChar *ns) {
1765 return (xmlStreamPushInternal(stream, name, ns, XML_ELEMENT_NODE));
1766}
1767
1768/**
1769* xmlStreamPushAttr:
1770* @stream: the stream context
1771* @name: the current name
1772* @ns: the namespace name
1773*
1774* push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
1775* indicated a dictionnary, then strings for name and ns will be expected
1776* to come from the dictionary.
1777* Both @name and @ns being NULL means the / i.e. the root of the document.
1778* This can also act as a reset.
1779*
1780* Returns: -1 in case of error, 1 if the current state in the stream is a
1781* match and 0 otherwise.
1782*/
1783int
1784xmlStreamPushAttr(xmlStreamCtxtPtr stream,
1785 const xmlChar *name, const xmlChar *ns) {
1786 return (xmlStreamPushInternal(stream, name, ns, XML_ATTRIBUTE_NODE));
1787}
1788
1789/**
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001790 * xmlStreamPop:
1791 * @stream: the stream context
1792 *
1793 * push one level from the stream.
1794 *
1795 * Returns: -1 in case of error, 0 otherwise.
1796 */
1797int
1798xmlStreamPop(xmlStreamCtxtPtr stream) {
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001799 int i, m;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001800 int ret;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001801
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001802 if (stream == NULL)
1803 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001804 ret = 0;
1805 while (stream != NULL) {
1806 stream->level--;
1807 if (stream->level < 0)
1808 ret = -1;
1809
1810 /*
1811 * Check evolution of existing states
1812 */
1813 m = stream->nbState;
1814 for (i = 0;i < m;i++) {
1815 if (stream->states[(2 * i)] < 0) break;
1816 /* discard obsoleted states */
1817 if (stream->states[(2 * i) + 1] > stream->level)
1818 stream->states[(2 * i)] = -1;
1819 }
1820 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001821 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001822 return(0);
1823}
1824
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001825/************************************************************************
1826 * *
1827 * The public interfaces *
1828 * *
1829 ************************************************************************/
1830
1831/**
1832 * xmlPatterncompile:
1833 * @pattern: the pattern to compile
1834 * @dict: an optional dictionnary for interned strings
1835 * @flags: compilation flags, undefined yet
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001836 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001837 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001838 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001839 *
1840 * Returns the compiled for of the pattern or NULL in case of error
1841 */
1842xmlPatternPtr
Daniel Veillard427174f2003-12-10 10:42:59 +00001843xmlPatterncompile(const xmlChar *pattern, xmlDict *dict,
1844 int flags ATTRIBUTE_UNUSED,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001845 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001846 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001847 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001848 const xmlChar *or, *start;
1849 xmlChar *tmp = NULL;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001850 int type = 0;
1851 int streamable = 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001852
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001853 if (pattern == NULL)
1854 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001855
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001856 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001857 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001858 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001859 tmp = NULL;
1860 while ((*or != 0) && (*or != '|')) or++;
1861 if (*or == 0)
1862 ctxt = xmlNewPatParserContext(start, dict, namespaces);
1863 else {
1864 tmp = xmlStrndup(start, or - start);
1865 if (tmp != NULL) {
1866 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
1867 }
1868 or++;
1869 }
1870 if (ctxt == NULL) goto error;
1871 cur = xmlNewPattern();
1872 if (cur == NULL) goto error;
1873 if (ret == NULL)
1874 ret = cur;
1875 else {
1876 cur->next = ret->next;
1877 ret->next = cur;
1878 }
1879 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001880
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001881 xmlCompilePathPattern(ctxt);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001882 if (ctxt->error != 0)
1883 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001884 xmlFreePatParserContext(ctxt);
1885
1886
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001887 if (streamable) {
1888 if (type == 0) {
1889 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
1890 } else if (type == PAT_FROM_ROOT) {
1891 if (cur->flags & PAT_FROM_CUR)
1892 streamable = 0;
1893 } else if (type == PAT_FROM_CUR) {
1894 if (cur->flags & PAT_FROM_ROOT)
1895 streamable = 0;
1896 }
1897 }
1898 if (streamable)
1899 xmlStreamCompile(cur);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001900 if (xmlReversePattern(cur) < 0)
1901 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001902 if (tmp != NULL) {
1903 xmlFree(tmp);
1904 tmp = NULL;
1905 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001906 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001907 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001908 if (streamable == 0) {
1909 cur = ret;
1910 while (cur != NULL) {
1911 if (cur->stream != NULL) {
1912 xmlFreeStreamComp(cur->stream);
1913 cur->stream = NULL;
1914 }
1915 cur = cur->next;
1916 }
1917 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001918 return(ret);
1919error:
1920 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
1921 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001922 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001923 return(NULL);
1924}
1925
1926/**
1927 * xmlPatternMatch:
1928 * @comp: the precompiled pattern
1929 * @node: a node
1930 *
1931 * Test wether the node matches the pattern
1932 *
1933 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1934 */
1935int
1936xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
1937{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001938 int ret = 0;
1939
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001940 if ((comp == NULL) || (node == NULL))
1941 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001942
1943 while (comp != NULL) {
1944 ret = xmlPatMatch(comp, node);
1945 if (ret != 0)
1946 return(ret);
1947 comp = comp->next;
1948 }
1949 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001950}
1951
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001952/**
1953 * xmlPatternGetStreamCtxt:
1954 * @comp: the precompiled pattern
1955 *
1956 * Get a streaming context for that pattern
1957 * Use xmlFreeStreamCtxt to free the context.
1958 *
1959 * Returns a pointer to the context or NULL in case of failure
1960 */
1961xmlStreamCtxtPtr
1962xmlPatternGetStreamCtxt(xmlPatternPtr comp)
1963{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001964 xmlStreamCtxtPtr ret = NULL, cur;
1965
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001966 if ((comp == NULL) || (comp->stream == NULL))
1967 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001968
1969 while (comp != NULL) {
1970 if (comp->stream == NULL)
1971 goto failed;
1972 cur = xmlNewStreamCtxt(comp->stream);
1973 if (cur == NULL)
1974 goto failed;
1975 if (ret == NULL)
1976 ret = cur;
1977 else {
1978 cur->next = ret->next;
1979 ret->next = cur;
1980 }
1981 comp = comp->next;
1982 }
1983 return(ret);
1984failed:
1985 xmlFreeStreamCtxt(ret);
1986 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001987}
1988
Daniel Veillard56de87e2005-02-16 00:22:29 +00001989/**
1990 * xmlPatternStreamable:
1991 * @comp: the precompiled pattern
1992 *
1993 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
1994 * should work.
1995 *
1996 * Returns 1 if streamable, 0 if not and -1 in case of error.
1997 */
1998int
1999xmlPatternStreamable(xmlPatternPtr comp) {
2000 if (comp == NULL)
2001 return(-1);
2002 while (comp != NULL) {
2003 if (comp->stream == NULL)
2004 return(0);
2005 comp = comp->next;
2006 }
2007 return(1);
2008}
2009
2010/**
2011 * xmlPatternMaxDepth:
2012 * @comp: the precompiled pattern
2013 *
2014 * Check the maximum depth reachable by a pattern
2015 *
2016 * Returns -2 if no limit (using //), otherwise the depth,
2017 * and -1 in case of error
2018 */
2019int
2020xmlPatternMaxDepth(xmlPatternPtr comp) {
2021 int ret = 0, i;
2022 if (comp == NULL)
2023 return(-1);
2024 while (comp != NULL) {
2025 if (comp->stream == NULL)
2026 return(-1);
2027 for (i = 0;i < comp->stream->nbStep;i++)
2028 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2029 return(-2);
2030 if (comp->stream->nbStep > ret)
2031 ret = comp->stream->nbStep;
2032 comp = comp->next;
2033 }
2034 return(ret);
2035
2036}
2037
2038/**
2039 * xmlPatternFromRoot:
2040 * @comp: the precompiled pattern
2041 *
2042 * Check if the pattern must be looked at from the root.
2043 *
2044 * Returns 1 if true, 0 if false and -1 in case of error
2045 */
2046int
2047xmlPatternFromRoot(xmlPatternPtr comp) {
2048 if (comp == NULL)
2049 return(-1);
2050 while (comp != NULL) {
2051 if (comp->stream == NULL)
2052 return(-1);
2053 if (comp->flags & PAT_FROM_ROOT)
2054 return(1);
2055 comp = comp->next;
2056 }
2057 return(0);
2058
2059}
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002060
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002061#endif /* LIBXML_PATTERN_ENABLED */