blob: 2438099c2b06fbad1c9cf536b8ec42d540ae06d4 [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
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +000048#define XML_STREAM_STEP_ATTR 8
Daniel Veillard2fc6df92005-01-30 18:42:55 +000049
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000050/*
51* TODO: This is used on _xmlStreamCtxt, so don't use any values
52* from xmlPatternFlags.
53*/
54#define XML_STREAM_DESC 1<<16
55
William M. Brackea152c02005-06-09 18:12:28 +000056#define XML_PATTERN_NOTPATTERN (XML_PATTERN_XPATH | \
57 XML_PATTERN_XSSEL | \
58 XML_PATTERN_XSFIELD)
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000059
60#define XML_STREAM_XS_IDC(item) (item->flags & \
61 (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000062
Daniel Veillard2fc6df92005-01-30 18:42:55 +000063typedef struct _xmlStreamStep xmlStreamStep;
64typedef xmlStreamStep *xmlStreamStepPtr;
65struct _xmlStreamStep {
66 int flags; /* properties of that step */
67 const xmlChar *name; /* first string value if NULL accept all */
68 const xmlChar *ns; /* second string value */
69};
70
71typedef struct _xmlStreamComp xmlStreamComp;
72typedef xmlStreamComp *xmlStreamCompPtr;
73struct _xmlStreamComp {
William M. Brackfbb619f2005-06-06 13:49:18 +000074 xmlDict *dict; /* the dictionary if any */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000075 int nbStep; /* number of steps in the automata */
76 int maxStep; /* allocated number of steps */
77 xmlStreamStepPtr steps; /* the array of steps */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000078 int flags;
Daniel Veillard2fc6df92005-01-30 18:42:55 +000079};
80
81struct _xmlStreamCtxt {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +000082 struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000083 xmlStreamCompPtr comp; /* the compiled stream */
William M. Brackfbb619f2005-06-06 13:49:18 +000084 int nbState; /* number of states in the automata */
85 int maxState; /* allocated number of states */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000086 int level; /* how deep are we ? */
87 int *states; /* the array of step indexes */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000088 int flags; /* validation options */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000089 int blockLevel;
Daniel Veillard2fc6df92005-01-30 18:42:55 +000090};
91
92static void xmlFreeStreamComp(xmlStreamCompPtr comp);
93
Daniel Veillardb3de70c2003-12-02 22:32:15 +000094/*
95 * Types are private:
96 */
97
98typedef enum {
99 XML_OP_END=0,
100 XML_OP_ROOT,
101 XML_OP_ELEM,
102 XML_OP_CHILD,
103 XML_OP_ATTR,
104 XML_OP_PARENT,
105 XML_OP_ANCESTOR,
106 XML_OP_NS,
107 XML_OP_ALL
108} xmlPatOp;
109
110
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000111typedef struct _xmlStepState xmlStepState;
112typedef xmlStepState *xmlStepStatePtr;
113struct _xmlStepState {
114 int step;
115 xmlNodePtr node;
116};
117
118typedef struct _xmlStepStates xmlStepStates;
119typedef xmlStepStates *xmlStepStatesPtr;
120struct _xmlStepStates {
121 int nbstates;
122 int maxstates;
123 xmlStepStatePtr states;
124};
125
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000126typedef struct _xmlStepOp xmlStepOp;
127typedef xmlStepOp *xmlStepOpPtr;
128struct _xmlStepOp {
129 xmlPatOp op;
130 const xmlChar *value;
131 const xmlChar *value2;
132};
133
William M. Brackea152c02005-06-09 18:12:28 +0000134#define PAT_FROM_ROOT (1<<8)
135#define PAT_FROM_CUR (1<<9)
Daniel Veillard56de87e2005-02-16 00:22:29 +0000136
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000137struct _xmlPattern {
138 void *data; /* the associated template */
William M. Brackfbb619f2005-06-06 13:49:18 +0000139 xmlDictPtr dict; /* the optional dictionary */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000140 struct _xmlPattern *next; /* next pattern if | is used */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000141 const xmlChar *pattern; /* the pattern */
William M. Brackea152c02005-06-09 18:12:28 +0000142 xmlPatternFlags flags; /* flags */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000143 int nbStep;
144 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000145 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000146 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000147};
148
149typedef struct _xmlPatParserContext xmlPatParserContext;
150typedef xmlPatParserContext *xmlPatParserContextPtr;
151struct _xmlPatParserContext {
152 const xmlChar *cur; /* the current char being parsed */
153 const xmlChar *base; /* the full expression */
154 int error; /* error code */
William M. Brackfbb619f2005-06-06 13:49:18 +0000155 xmlDictPtr dict; /* the dictionary if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000156 xmlPatternPtr comp; /* the result */
157 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000158 const xmlChar **namespaces; /* the namespaces definitions */
159 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000160};
161
162/************************************************************************
163 * *
164 * Type functions *
165 * *
166 ************************************************************************/
167
168/**
169 * xmlNewPattern:
170 *
171 * Create a new XSLT Pattern
172 *
173 * Returns the newly allocated xmlPatternPtr or NULL in case of error
174 */
175static xmlPatternPtr
176xmlNewPattern(void) {
177 xmlPatternPtr cur;
178
179 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
180 if (cur == NULL) {
181 ERROR(NULL, NULL, NULL,
182 "xmlNewPattern : malloc failed\n");
183 return(NULL);
184 }
185 memset(cur, 0, sizeof(xmlPattern));
186 cur->maxStep = 10;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000187 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
188 if (cur->steps == NULL) {
189 xmlFree(cur);
190 ERROR(NULL, NULL, NULL,
191 "xmlNewPattern : malloc failed\n");
192 return(NULL);
193 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000194 return(cur);
195}
196
197/**
198 * xmlFreePattern:
199 * @comp: an XSLT comp
200 *
201 * Free up the memory allocated by @comp
202 */
203void
204xmlFreePattern(xmlPatternPtr comp) {
205 xmlStepOpPtr op;
206 int i;
207
208 if (comp == NULL)
209 return;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000210 if (comp->next != NULL)
211 xmlFreePattern(comp->next);
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000212 if (comp->stream != NULL)
213 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000214 if (comp->pattern != NULL)
215 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000216 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000217 if (comp->dict == NULL) {
218 for (i = 0;i < comp->nbStep;i++) {
219 op = &comp->steps[i];
220 if (op->value != NULL)
221 xmlFree((xmlChar *) op->value);
222 if (op->value2 != NULL)
223 xmlFree((xmlChar *) op->value2);
224 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000225 }
226 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000227 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000228 if (comp->dict != NULL)
229 xmlDictFree(comp->dict);
230
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000231 memset(comp, -1, sizeof(xmlPattern));
232 xmlFree(comp);
233}
234
235/**
236 * xmlFreePatternList:
237 * @comp: an XSLT comp list
238 *
239 * Free up the memory allocated by all the elements of @comp
240 */
241void
242xmlFreePatternList(xmlPatternPtr comp) {
243 xmlPatternPtr cur;
244
245 while (comp != NULL) {
246 cur = comp;
247 comp = comp->next;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +0000248 cur->next = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000249 xmlFreePattern(cur);
250 }
251}
252
253/**
254 * xmlNewPatParserContext:
255 * @pattern: the pattern context
William M. Brackfbb619f2005-06-06 13:49:18 +0000256 * @dict: the inherited dictionary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000257 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
258 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000259 *
260 * Create a new XML pattern parser context
261 *
262 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
263 */
264static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000265xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
266 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000267 xmlPatParserContextPtr cur;
268
269 if (pattern == NULL)
270 return(NULL);
271
272 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
273 if (cur == NULL) {
274 ERROR(NULL, NULL, NULL,
275 "xmlNewPatParserContext : malloc failed\n");
276 return(NULL);
277 }
278 memset(cur, 0, sizeof(xmlPatParserContext));
279 cur->dict = dict;
280 cur->cur = pattern;
281 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000282 if (namespaces != NULL) {
283 int i;
284 for (i = 0;namespaces[2 * i] != NULL;i++);
285 cur->nb_namespaces = i;
286 } else {
287 cur->nb_namespaces = 0;
288 }
289 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000290 return(cur);
291}
292
293/**
294 * xmlFreePatParserContext:
295 * @ctxt: an XSLT parser context
296 *
297 * Free up the memory allocated by @ctxt
298 */
299static void
300xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
301 if (ctxt == NULL)
302 return;
303 memset(ctxt, -1, sizeof(xmlPatParserContext));
304 xmlFree(ctxt);
305}
306
307/**
308 * xmlPatternAdd:
309 * @comp: the compiled match expression
310 * @op: an op
311 * @value: the first value
312 * @value2: the second value
313 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000314 * Add a step to an XSLT Compiled Match
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000315 *
316 * Returns -1 in case of failure, 0 otherwise.
317 */
318static int
319xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
320 xmlPatternPtr comp,
321 xmlPatOp op, xmlChar * value, xmlChar * value2)
322{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000323 if (comp->nbStep >= comp->maxStep) {
324 xmlStepOpPtr temp;
325 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
326 sizeof(xmlStepOp));
327 if (temp == NULL) {
328 ERROR(ctxt, NULL, NULL,
329 "xmlPatternAdd: realloc failed\n");
330 return (-1);
331 }
332 comp->steps = temp;
333 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000334 }
335 comp->steps[comp->nbStep].op = op;
336 comp->steps[comp->nbStep].value = value;
337 comp->steps[comp->nbStep].value2 = value2;
338 comp->nbStep++;
339 return (0);
340}
341
342#if 0
343/**
344 * xsltSwapTopPattern:
345 * @comp: the compiled match expression
346 *
347 * reverse the two top steps.
348 */
349static void
350xsltSwapTopPattern(xmlPatternPtr comp) {
351 int i;
352 int j = comp->nbStep - 1;
353
354 if (j > 0) {
355 register const xmlChar *tmp;
356 register xmlPatOp op;
357 i = j - 1;
358 tmp = comp->steps[i].value;
359 comp->steps[i].value = comp->steps[j].value;
360 comp->steps[j].value = tmp;
361 tmp = comp->steps[i].value2;
362 comp->steps[i].value2 = comp->steps[j].value2;
363 comp->steps[j].value2 = tmp;
364 op = comp->steps[i].op;
365 comp->steps[i].op = comp->steps[j].op;
366 comp->steps[j].op = op;
367 }
368}
369#endif
370
371/**
372 * xmlReversePattern:
373 * @comp: the compiled match expression
374 *
375 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000376 *
377 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000378 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000379static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000380xmlReversePattern(xmlPatternPtr comp) {
Daniel Veillard56de87e2005-02-16 00:22:29 +0000381 int i, j;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000382
Daniel Veillard56de87e2005-02-16 00:22:29 +0000383 /*
384 * remove the leading // for //a or .//a
385 */
386 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
387 for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
388 comp->steps[i].value = comp->steps[j].value;
389 comp->steps[i].value2 = comp->steps[j].value2;
390 comp->steps[i].op = comp->steps[j].op;
391 }
392 comp->nbStep--;
393 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000394 if (comp->nbStep >= comp->maxStep) {
395 xmlStepOpPtr temp;
396 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
397 sizeof(xmlStepOp));
398 if (temp == NULL) {
399 ERROR(ctxt, NULL, NULL,
400 "xmlReversePattern: realloc failed\n");
401 return (-1);
402 }
403 comp->steps = temp;
404 comp->maxStep *= 2;
405 }
Daniel Veillard56de87e2005-02-16 00:22:29 +0000406 i = 0;
407 j = comp->nbStep - 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000408 while (j > i) {
409 register const xmlChar *tmp;
410 register xmlPatOp op;
411 tmp = comp->steps[i].value;
412 comp->steps[i].value = comp->steps[j].value;
413 comp->steps[j].value = tmp;
414 tmp = comp->steps[i].value2;
415 comp->steps[i].value2 = comp->steps[j].value2;
416 comp->steps[j].value2 = tmp;
417 op = comp->steps[i].op;
418 comp->steps[i].op = comp->steps[j].op;
419 comp->steps[j].op = op;
420 j--;
421 i++;
422 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000423 comp->steps[comp->nbStep].value = NULL;
424 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000425 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000426 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000427}
428
429/************************************************************************
430 * *
431 * The interpreter for the precompiled patterns *
432 * *
433 ************************************************************************/
434
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000435static int
436xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
437 if ((states->states == NULL) || (states->maxstates <= 0)) {
438 states->maxstates = 4;
439 states->nbstates = 0;
440 states->states = xmlMalloc(4 * sizeof(xmlStepState));
441 }
442 else if (states->maxstates <= states->nbstates) {
443 xmlStepState *tmp;
444
445 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
446 2 * states->maxstates * sizeof(xmlStepState));
447 if (tmp == NULL)
448 return(-1);
449 states->states = tmp;
450 states->maxstates *= 2;
451 }
452 states->states[states->nbstates].step = step;
453 states->states[states->nbstates++].node = node;
454#if 0
455 fprintf(stderr, "Push: %d, %s\n", step, node->name);
456#endif
457 return(0);
458}
459
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000460/**
461 * xmlPatMatch:
462 * @comp: the precompiled pattern
463 * @node: a node
464 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000465 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000466 *
467 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
468 */
469static int
470xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
471 int i;
472 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000473 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000474
475 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000476 i = 0;
477restart:
478 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000479 step = &comp->steps[i];
480 switch (step->op) {
481 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000482 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000483 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000484 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000485 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000486 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000487 if ((node->type == XML_DOCUMENT_NODE) ||
488#ifdef LIBXML_DOCB_ENABLED
489 (node->type == XML_DOCB_DOCUMENT_NODE) ||
490#endif
491 (node->type == XML_HTML_DOCUMENT_NODE))
492 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000493 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000494 case XML_OP_ELEM:
495 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000496 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000497 if (step->value == NULL)
498 continue;
499 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000500 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000501 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000502 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000503
504 /* Namespace test */
505 if (node->ns == NULL) {
506 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000507 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000508 } else if (node->ns->href != NULL) {
509 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000510 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000511 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000512 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000513 }
514 continue;
515 case XML_OP_CHILD: {
516 xmlNodePtr lst;
517
518 if ((node->type != XML_ELEMENT_NODE) &&
519 (node->type != XML_DOCUMENT_NODE) &&
520#ifdef LIBXML_DOCB_ENABLED
521 (node->type != XML_DOCB_DOCUMENT_NODE) &&
522#endif
523 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000524 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000525
526 lst = node->children;
527
528 if (step->value != NULL) {
529 while (lst != NULL) {
530 if ((lst->type == XML_ELEMENT_NODE) &&
531 (step->value[0] == lst->name[0]) &&
532 (xmlStrEqual(step->value, lst->name)))
533 break;
534 lst = lst->next;
535 }
536 if (lst != NULL)
537 continue;
538 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000539 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000540 }
541 case XML_OP_ATTR:
542 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000543 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000544 if (step->value != NULL) {
545 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000546 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000547 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000548 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000549 }
550 /* Namespace test */
551 if (node->ns == NULL) {
552 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000553 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000554 } else if (step->value2 != NULL) {
555 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000556 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000557 }
558 continue;
559 case XML_OP_PARENT:
560 if ((node->type == XML_DOCUMENT_NODE) ||
561 (node->type == XML_HTML_DOCUMENT_NODE) ||
562#ifdef LIBXML_DOCB_ENABLED
563 (node->type == XML_DOCB_DOCUMENT_NODE) ||
564#endif
565 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000566 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000567 node = node->parent;
568 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000569 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000570 if (step->value == NULL)
571 continue;
572 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000573 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000574 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000575 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000576 /* Namespace test */
577 if (node->ns == NULL) {
578 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000579 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000580 } else if (node->ns->href != NULL) {
581 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000582 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000583 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000584 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000585 }
586 continue;
587 case XML_OP_ANCESTOR:
588 /* TODO: implement coalescing of ANCESTOR/NODE ops */
589 if (step->value == NULL) {
590 i++;
591 step = &comp->steps[i];
592 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000593 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000594 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000595 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000596 if (step->value == NULL)
597 return(-1);
598 }
599 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000600 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000601 if ((node->type == XML_DOCUMENT_NODE) ||
602 (node->type == XML_HTML_DOCUMENT_NODE) ||
603#ifdef LIBXML_DOCB_ENABLED
604 (node->type == XML_DOCB_DOCUMENT_NODE) ||
605#endif
606 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000607 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000608 node = node->parent;
609 while (node != NULL) {
610 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000611 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000612 if ((node->type == XML_ELEMENT_NODE) &&
613 (step->value[0] == node->name[0]) &&
614 (xmlStrEqual(step->value, node->name))) {
615 /* Namespace test */
616 if (node->ns == NULL) {
617 if (step->value2 == NULL)
618 break;
619 } else if (node->ns->href != NULL) {
620 if ((step->value2 != NULL) &&
621 (xmlStrEqual(step->value2, node->ns->href)))
622 break;
623 }
624 }
625 node = node->parent;
626 }
627 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000628 goto rollback;
629 /*
630 * prepare a potential rollback from here
631 * for ancestors of that node.
632 */
633 if (step->op == XML_OP_ANCESTOR)
634 xmlPatPushState(&states, i, node);
635 else
636 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000637 continue;
638 case XML_OP_NS:
639 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000640 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000641 if (node->ns == NULL) {
642 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000643 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000644 } else if (node->ns->href != NULL) {
645 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000646 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000647 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000648 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000649 }
650 break;
651 case XML_OP_ALL:
652 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000653 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000654 break;
655 }
656 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000657found:
658 if (states.states != NULL) {
659 /* Free the rollback states */
660 xmlFree(states.states);
661 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000662 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000663rollback:
664 /* got an error try to rollback */
665 if (states.states == NULL)
666 return(0);
667 if (states.nbstates <= 0) {
668 xmlFree(states.states);
669 return(0);
670 }
671 states.nbstates--;
672 i = states.states[states.nbstates].step;
673 node = states.states[states.nbstates].node;
674#if 0
675 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
676#endif
677 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000678}
679
680/************************************************************************
681 * *
682 * Dedicated parser for templates *
683 * *
684 ************************************************************************/
685
686#define TODO \
687 xmlGenericError(xmlGenericErrorContext, \
688 "Unimplemented block at %s:%d\n", \
689 __FILE__, __LINE__);
690#define CUR (*ctxt->cur)
691#define SKIP(val) ctxt->cur += (val)
692#define NXT(val) ctxt->cur[(val)]
693#define CUR_PTR ctxt->cur
694
695#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000696 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000697
698#define CURRENT (*ctxt->cur)
699#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
700
701
702#define PUSH(op, val, val2) \
703 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
704
705#define XSLT_ERROR(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000706 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000707 ctxt->error = (X); return; }
708
709#define XSLT_ERROR0(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000710 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000711 ctxt->error = (X); return(0); }
712
713#if 0
714/**
715 * xmlPatScanLiteral:
716 * @ctxt: the XPath Parser context
717 *
718 * Parse an XPath Litteral:
719 *
720 * [29] Literal ::= '"' [^"]* '"'
721 * | "'" [^']* "'"
722 *
723 * Returns the Literal parsed or NULL
724 */
725
726static xmlChar *
727xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
728 const xmlChar *q, *cur;
729 xmlChar *ret = NULL;
730 int val, len;
731
732 SKIP_BLANKS;
733 if (CUR == '"') {
734 NEXT;
735 cur = q = CUR_PTR;
736 val = xmlStringCurrentChar(NULL, cur, &len);
737 while ((IS_CHAR(val)) && (val != '"')) {
738 cur += len;
739 val = xmlStringCurrentChar(NULL, cur, &len);
740 }
741 if (!IS_CHAR(val)) {
742 ctxt->error = 1;
743 return(NULL);
744 } else {
745 ret = xmlStrndup(q, cur - q);
746 }
747 cur += len;
748 CUR_PTR = cur;
749 } else if (CUR == '\'') {
750 NEXT;
751 cur = q = CUR_PTR;
752 val = xmlStringCurrentChar(NULL, cur, &len);
753 while ((IS_CHAR(val)) && (val != '\'')) {
754 cur += len;
755 val = xmlStringCurrentChar(NULL, cur, &len);
756 }
757 if (!IS_CHAR(val)) {
758 ctxt->error = 1;
759 return(NULL);
760 } else {
761 ret = xmlStrndup(q, cur - q);
762 }
763 cur += len;
764 CUR_PTR = cur;
765 } else {
766 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
767 ctxt->error = 1;
768 return(NULL);
769 }
770 return(ret);
771}
772#endif
773
774/**
775 * xmlPatScanName:
776 * @ctxt: the XPath Parser context
777 *
778 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
779 * CombiningChar | Extender
780 *
781 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
782 *
783 * [6] Names ::= Name (S Name)*
784 *
785 * Returns the Name parsed or NULL
786 */
787
788static xmlChar *
789xmlPatScanName(xmlPatParserContextPtr ctxt) {
790 const xmlChar *q, *cur;
791 xmlChar *ret = NULL;
792 int val, len;
793
794 SKIP_BLANKS;
795
796 cur = q = CUR_PTR;
797 val = xmlStringCurrentChar(NULL, cur, &len);
798 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
799 return(NULL);
800
801 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
802 (val == '.') || (val == '-') ||
803 (val == '_') ||
804 (IS_COMBINING(val)) ||
805 (IS_EXTENDER(val))) {
806 cur += len;
807 val = xmlStringCurrentChar(NULL, cur, &len);
808 }
809 ret = xmlStrndup(q, cur - q);
810 CUR_PTR = cur;
811 return(ret);
812}
813
814/**
815 * xmlPatScanNCName:
816 * @ctxt: the XPath Parser context
817 *
818 * Parses a non qualified name
819 *
820 * Returns the Name parsed or NULL
821 */
822
823static xmlChar *
824xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
825 const xmlChar *q, *cur;
826 xmlChar *ret = NULL;
827 int val, len;
828
829 SKIP_BLANKS;
830
831 cur = q = CUR_PTR;
832 val = xmlStringCurrentChar(NULL, cur, &len);
833 if (!IS_LETTER(val) && (val != '_'))
834 return(NULL);
835
836 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
837 (val == '.') || (val == '-') ||
838 (val == '_') ||
839 (IS_COMBINING(val)) ||
840 (IS_EXTENDER(val))) {
841 cur += len;
842 val = xmlStringCurrentChar(NULL, cur, &len);
843 }
844 ret = xmlStrndup(q, cur - q);
845 CUR_PTR = cur;
846 return(ret);
847}
848
849#if 0
850/**
851 * xmlPatScanQName:
852 * @ctxt: the XPath Parser context
853 * @prefix: the place to store the prefix
854 *
855 * Parse a qualified name
856 *
857 * Returns the Name parsed or NULL
858 */
859
860static xmlChar *
861xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
862 xmlChar *ret = NULL;
863
864 *prefix = NULL;
865 ret = xmlPatScanNCName(ctxt);
866 if (CUR == ':') {
867 *prefix = ret;
868 NEXT;
869 ret = xmlPatScanNCName(ctxt);
870 }
871 return(ret);
872}
873#endif
874
875/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000876 * xmlCompileAttributeTest:
877 * @ctxt: the compilation context
878 *
879 * Compile an attribute test.
880 */
881static void
882xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
883 xmlChar *token = NULL;
884 xmlChar *name = NULL;
885 xmlChar *URL = NULL;
886
887 name = xmlPatScanNCName(ctxt);
888 if (name == NULL) {
889 if (CUR == '*') {
890 PUSH(XML_OP_ATTR, NULL, NULL);
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +0000891 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000892 } else {
893 ERROR(NULL, NULL, NULL,
894 "xmlCompileAttributeTest : Name expected\n");
895 ctxt->error = 1;
896 }
897 return;
898 }
899 if (CUR == ':') {
900 int i;
901 xmlChar *prefix = name;
902
903 NEXT;
904 /*
905 * This is a namespace match
906 */
907 token = xmlPatScanName(ctxt);
908 for (i = 0;i < ctxt->nb_namespaces;i++) {
909 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
910 URL = xmlStrdup(ctxt->namespaces[2 * i]);
911 break;
912 }
913 }
914 if (i >= ctxt->nb_namespaces) {
915 ERROR5(NULL, NULL, NULL,
916 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
917 prefix);
918 ctxt->error = 1;
919 goto error;
920 }
921
922 xmlFree(prefix);
923 if (token == NULL) {
924 if (CUR == '*') {
925 NEXT;
926 PUSH(XML_OP_ATTR, NULL, URL);
927 } else {
928 ERROR(NULL, NULL, NULL,
929 "xmlCompileAttributeTest : Name expected\n");
930 ctxt->error = 1;
931 goto error;
932 }
933 } else {
934 PUSH(XML_OP_ATTR, token, URL);
935 }
936 } else {
937 PUSH(XML_OP_ATTR, name, NULL);
938 }
939 return;
940error:
941 if (URL != NULL)
942 xmlFree(URL);
943 if (token != NULL)
944 xmlFree(token);
945}
946
947
948/**
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000949 * xmlCompileStepPattern:
950 * @ctxt: the compilation context
951 *
952 * Compile the Step Pattern and generates a precompiled
953 * form suitable for fast matching.
954 *
955 * [3] Step ::= '.' | NameTest
956 * [4] NameTest ::= QName | '*' | NCName ':' '*'
957 */
958
959static void
960xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
961 xmlChar *token = NULL;
962 xmlChar *name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000963 xmlChar *URL = NULL;
964
965 SKIP_BLANKS;
966 if (CUR == '.') {
967 NEXT;
968 PUSH(XML_OP_ELEM, NULL, NULL);
969 return;
970 }
971 name = xmlPatScanNCName(ctxt);
972 if (name == NULL) {
973 if (CUR == '*') {
974 NEXT;
975 PUSH(XML_OP_ALL, NULL, NULL);
976 return;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000977 } else if (CUR == '@') {
978 NEXT;
979 xmlCompileAttributeTest(ctxt);
980 if (ctxt->error != 0)
981 goto error;
982 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000983 } else {
984 ERROR(NULL, NULL, NULL,
985 "xmlCompileStepPattern : Name expected\n");
986 ctxt->error = 1;
987 return;
988 }
989 }
990 SKIP_BLANKS;
991 if (CUR == ':') {
992 NEXT;
993 if (CUR != ':') {
994 xmlChar *prefix = name;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000995 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000996
997 /*
998 * This is a namespace match
999 */
1000 token = xmlPatScanName(ctxt);
Daniel Veillardd4301ab2005-02-03 22:24:10 +00001001 for (i = 0;i < ctxt->nb_namespaces;i++) {
1002 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Daniel Veillard0996a162005-02-05 14:00:10 +00001003 URL = xmlStrdup(ctxt->namespaces[2 * i]);
Daniel Veillardd4301ab2005-02-03 22:24:10 +00001004 break;
1005 }
1006 }
1007 if (i >= ctxt->nb_namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001008 ERROR5(NULL, NULL, NULL,
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001009 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1010 prefix);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001011 ctxt->error = 1;
1012 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001013 }
1014 xmlFree(prefix);
1015 if (token == NULL) {
1016 if (CUR == '*') {
1017 NEXT;
1018 PUSH(XML_OP_NS, URL, NULL);
1019 } else {
1020 ERROR(NULL, NULL, NULL,
1021 "xmlCompileStepPattern : Name expected\n");
1022 ctxt->error = 1;
1023 goto error;
1024 }
1025 } else {
1026 PUSH(XML_OP_ELEM, token, URL);
1027 }
1028 } else {
1029 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001030 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1031 xmlFree(name);
1032 name = xmlPatScanName(ctxt);
1033 if (name == NULL) {
1034 if (CUR == '*') {
1035 NEXT;
1036 PUSH(XML_OP_ALL, NULL, NULL);
1037 return;
1038 } else {
1039 ERROR(NULL, NULL, NULL,
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001040 "xmlCompileStepPattern : QName expected\n");
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001041 ctxt->error = 1;
1042 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001043 }
1044 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001045 if (CUR == ':') {
1046 xmlChar *prefix = name;
1047 int i;
1048
1049 NEXT;
1050 /*
1051 * This is a namespace match
1052 */
1053 token = xmlPatScanName(ctxt);
1054 for (i = 0;i < ctxt->nb_namespaces;i++) {
1055 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1056 URL = xmlStrdup(ctxt->namespaces[2 * i]);
1057 break;
1058 }
1059 }
1060 if (i >= ctxt->nb_namespaces) {
1061 ERROR5(NULL, NULL, NULL,
William M. Brackea152c02005-06-09 18:12:28 +00001062 "xmlCompileStepPattern : no namespace bound "
1063 "to prefix %s\n", prefix);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001064 ctxt->error = 1;
1065 goto error;
1066 }
1067 xmlFree(prefix);
1068 if (token == NULL) {
1069 if (CUR == '*') {
1070 NEXT;
1071 PUSH(XML_OP_NS, URL, NULL);
1072 } else {
1073 ERROR(NULL, NULL, NULL,
1074 "xmlCompileStepPattern : Name expected\n");
1075 ctxt->error = 1;
1076 goto error;
1077 }
1078 } else {
1079 PUSH(XML_OP_CHILD, token, URL);
1080 }
1081 } else
1082 PUSH(XML_OP_CHILD, name, NULL);
1083 return;
1084 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1085 xmlFree(name);
1086 name = NULL;
1087 xmlCompileAttributeTest(ctxt);
1088 if (ctxt->error != 0)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001089 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001090 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001091 } else {
1092 ERROR(NULL, NULL, NULL,
1093 "xmlCompileStepPattern : 'child' or 'attribute' expected\n");
1094 ctxt->error = 1;
1095 goto error;
1096 }
Daniel Veillard0e460da2005-03-30 22:47:10 +00001097 /* NOT REACHED xmlFree(name); */
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001098 }
1099 } else if (CUR == '*') {
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001100 if (name != NULL) {
1101 ctxt->error = 1;
1102 goto error;
1103 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001104 NEXT;
1105 PUSH(XML_OP_ALL, token, NULL);
1106 } else {
1107 if (name == NULL) {
1108 ctxt->error = 1;
1109 goto error;
1110 }
1111 PUSH(XML_OP_ELEM, name, NULL);
1112 }
1113 return;
1114error:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001115 if (URL != NULL)
1116 xmlFree(URL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001117 if (token != NULL)
1118 xmlFree(token);
1119 if (name != NULL)
1120 xmlFree(name);
1121}
1122
1123/**
1124 * xmlCompilePathPattern:
1125 * @ctxt: the compilation context
1126 *
1127 * Compile the Path Pattern and generates a precompiled
1128 * form suitable for fast matching.
1129 *
1130 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1131 */
1132static void
1133xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1134 SKIP_BLANKS;
William M. Brack537f1172005-06-14 22:02:59 +00001135 if (CUR == '/') {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001136 ctxt->comp->flags |= PAT_FROM_ROOT;
William M. Brackea152c02005-06-09 18:12:28 +00001137 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001138 ctxt->comp->flags |= PAT_FROM_CUR;
1139 }
William M. Brackea152c02005-06-09 18:12:28 +00001140
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001141 if ((CUR == '/') && (NXT(1) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001142 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001143 NEXT;
1144 NEXT;
1145 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001146 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001147 NEXT;
1148 NEXT;
1149 NEXT;
1150 }
1151 if (CUR == '@') {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001152 NEXT;
1153 xmlCompileAttributeTest(ctxt);
1154 SKIP_BLANKS;
William M. Brackfbb619f2005-06-06 13:49:18 +00001155 if (CUR != 0) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001156 xmlCompileStepPattern(ctxt);
1157 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001158 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001159 if (CUR == '/') {
1160 PUSH(XML_OP_ROOT, NULL, NULL);
1161 NEXT;
1162 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001163 xmlCompileStepPattern(ctxt);
1164 SKIP_BLANKS;
1165 while (CUR == '/') {
William M. Brackfbb619f2005-06-06 13:49:18 +00001166 if (NXT(1) == '/') {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001167 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1168 NEXT;
1169 NEXT;
1170 SKIP_BLANKS;
1171 xmlCompileStepPattern(ctxt);
1172 } else {
1173 PUSH(XML_OP_PARENT, NULL, NULL);
1174 NEXT;
1175 SKIP_BLANKS;
William M. Brackfbb619f2005-06-06 13:49:18 +00001176 if (CUR != 0) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001177 xmlCompileStepPattern(ctxt);
1178 }
1179 }
1180 }
1181 }
Daniel Veillard56de87e2005-02-16 00:22:29 +00001182 if (CUR != 0) {
1183 ERROR5(NULL, NULL, NULL,
1184 "Failed to compile pattern %s\n", ctxt->base);
1185 ctxt->error = 1;
1186 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001187error:
1188 return;
1189}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001190
1191/************************************************************************
1192 * *
1193 * The streaming code *
1194 * *
1195 ************************************************************************/
1196
1197#ifdef DEBUG_STREAMING
1198static void
1199xmlDebugStreamComp(xmlStreamCompPtr stream) {
1200 int i;
1201
1202 if (stream == NULL) {
1203 printf("Stream: NULL\n");
1204 return;
1205 }
1206 printf("Stream: %d steps\n", stream->nbStep);
1207 for (i = 0;i < stream->nbStep;i++) {
1208 if (stream->steps[i].ns != NULL) {
1209 printf("{%s}", stream->steps[i].ns);
1210 }
1211 if (stream->steps[i].name == NULL) {
1212 printf("* ");
1213 } else {
1214 printf("%s ", stream->steps[i].name);
1215 }
1216 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1217 printf("root ");
1218 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1219 printf("// ");
1220 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1221 printf("final ");
1222 printf("\n");
1223 }
1224}
1225static void
1226xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1227 int i;
1228
1229 if (ctxt == NULL) {
1230 printf("Stream: NULL\n");
1231 return;
1232 }
1233 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1234 if (match)
1235 printf("matches\n");
1236 else
1237 printf("\n");
1238 for (i = 0;i < ctxt->nbState;i++) {
1239 if (ctxt->states[2 * i] < 0)
1240 printf(" %d: free\n", i);
1241 else {
1242 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1243 ctxt->states[(2 * i) + 1]);
1244 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1245 XML_STREAM_STEP_DESC)
1246 printf(" //\n");
1247 else
1248 printf("\n");
1249 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001250 }
1251}
1252#endif
1253/**
1254 * xmlNewStreamComp:
1255 * @size: the number of expected steps
1256 *
1257 * build a new compiled pattern for streaming
1258 *
1259 * Returns the new structure or NULL in case of error.
1260 */
1261static xmlStreamCompPtr
1262xmlNewStreamComp(int size) {
1263 xmlStreamCompPtr cur;
1264
1265 if (size < 4)
1266 size = 4;
1267
1268 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1269 if (cur == NULL) {
1270 ERROR(NULL, NULL, NULL,
1271 "xmlNewStreamComp: malloc failed\n");
1272 return(NULL);
1273 }
1274 memset(cur, 0, sizeof(xmlStreamComp));
1275 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1276 if (cur->steps == NULL) {
1277 xmlFree(cur);
1278 ERROR(NULL, NULL, NULL,
1279 "xmlNewStreamComp: malloc failed\n");
1280 return(NULL);
1281 }
1282 cur->nbStep = 0;
1283 cur->maxStep = size;
1284 return(cur);
1285}
1286
1287/**
1288 * xmlFreeStreamComp:
1289 * @comp: the compiled pattern for streaming
1290 *
1291 * Free the compiled pattern for streaming
1292 */
1293static void
1294xmlFreeStreamComp(xmlStreamCompPtr comp) {
1295 if (comp != NULL) {
1296 if (comp->steps != NULL)
1297 xmlFree(comp->steps);
1298 if (comp->dict != NULL)
1299 xmlDictFree(comp->dict);
1300 xmlFree(comp);
1301 }
1302}
1303
1304/**
1305 * xmlStreamCompAddStep:
1306 * @comp: the compiled pattern for streaming
1307 * @name: the first string, the name, or NULL for *
1308 * @ns: the second step, the namespace name
1309 * @flags: the flags for that step
1310 *
1311 * Add a new step to the compiled pattern
1312 *
1313 * Returns -1 in case of error or the step index if successful
1314 */
1315static int
1316xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1317 const xmlChar *ns, int flags) {
1318 xmlStreamStepPtr cur;
1319
1320 if (comp->nbStep >= comp->maxStep) {
1321 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1322 comp->maxStep * 2 * sizeof(xmlStreamStep));
1323 if (cur == NULL) {
1324 ERROR(NULL, NULL, NULL,
1325 "xmlNewStreamComp: malloc failed\n");
1326 return(-1);
1327 }
1328 comp->steps = cur;
1329 comp->maxStep *= 2;
1330 }
1331 cur = &comp->steps[comp->nbStep++];
1332 cur->flags = flags;
1333 cur->name = name;
1334 cur->ns = ns;
1335 return(comp->nbStep - 1);
1336}
1337
1338/**
1339 * xmlStreamCompile:
1340 * @comp: the precompiled pattern
1341 *
1342 * Tries to stream compile a pattern
1343 *
1344 * Returns -1 in case of failure and 0 in case of success.
1345 */
1346static int
1347xmlStreamCompile(xmlPatternPtr comp) {
1348 xmlStreamCompPtr stream;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001349 int i, s = 0, root = 0, flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001350
1351 if ((comp == NULL) || (comp->steps == NULL))
1352 return(-1);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001353 /*
1354 * special case for .
1355 */
1356 if ((comp->nbStep == 1) &&
1357 (comp->steps[0].op == XML_OP_ELEM) &&
1358 (comp->steps[0].value == NULL) &&
1359 (comp->steps[0].value2 == NULL)) {
1360 stream = xmlNewStreamComp(0);
1361 if (stream == NULL)
1362 return(-1);
1363 comp->stream = stream;
1364 return(0);
1365 }
1366
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001367 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1368 if (stream == NULL)
1369 return(-1);
1370 if (comp->dict != NULL) {
1371 stream->dict = comp->dict;
1372 xmlDictReference(stream->dict);
1373 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001374
1375 /*
1376 * Skip leading ./ on relative paths
1377 */
1378 i = 0;
1379 while ((comp->flags & PAT_FROM_CUR) && (comp->nbStep > i + 2) &&
1380 (comp->steps[i].op == XML_OP_ELEM) &&
1381 (comp->steps[i].value == NULL) &&
1382 (comp->steps[i].value2 == NULL) &&
1383 (comp->steps[i + 1].op == XML_OP_PARENT)) {
1384 i += 2;
1385 }
1386 for (;i < comp->nbStep;i++) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001387 switch (comp->steps[i].op) {
1388 case XML_OP_END:
1389 break;
1390 case XML_OP_ROOT:
1391 if (i != 0)
1392 goto error;
1393 root = 1;
1394 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001395 case XML_OP_NS:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001396 s = xmlStreamCompAddStep(stream, NULL,
1397 comp->steps[i].value, flags);
1398 flags = 0;
1399 if (s < 0)
1400 goto error;
1401 break;
1402 case XML_OP_ATTR:
1403 flags |= XML_STREAM_STEP_ATTR;
1404 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1405 comp->steps[i].value2, flags);
1406 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001407 if (s < 0)
1408 goto error;
1409 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001410 case XML_OP_ELEM:
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001411 if ((comp->steps[i].value == NULL) &&
1412 (comp->steps[i].value2 == NULL) &&
1413 (comp->nbStep > i + 2) &&
1414 (comp->steps[i + 1].op == XML_OP_PARENT)) {
1415 i++;
1416 continue;
1417 }
1418 case XML_OP_CHILD:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001419 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1420 comp->steps[i].value2, flags);
1421 flags = 0;
1422 if (s < 0)
1423 goto error;
1424 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001425 case XML_OP_ALL:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001426 s = xmlStreamCompAddStep(stream, NULL, NULL, flags);
1427 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001428 if (s < 0)
1429 goto error;
1430 break;
1431 case XML_OP_PARENT:
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001432 if ((comp->nbStep > i + 1) &&
1433 (comp->steps[i + 1].op == XML_OP_ELEM) &&
1434 (comp->steps[i + 1].value == NULL) &&
1435 (comp->steps[i + 1].value2 == NULL)) {
1436 i++;
1437 continue;
1438 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001439 break;
1440 case XML_OP_ANCESTOR:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001441 flags |= XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001442 /*
1443 * Mark the expression as having "//".
1444 */
1445 if ((stream->flags & XML_STREAM_DESC) == 0)
1446 stream->flags |= XML_STREAM_DESC;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001447 break;
1448 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001449 }
1450 if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1451 /*
1452 * If this should behave like a real pattern, we will mark
1453 * the first step as having "//", to be reentrant on every
1454 * tree level.
1455 */
1456 if ((stream->flags & XML_STREAM_DESC) == 0)
1457 stream->flags |= XML_STREAM_DESC;
1458
1459 if (stream->nbStep > 0) {
1460 if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0)
1461 stream->steps[0].flags |= XML_STREAM_STEP_DESC;
1462 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001463 }
1464 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1465 if (root)
1466 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1467#ifdef DEBUG_STREAMING
1468 xmlDebugStreamComp(stream);
1469#endif
1470 comp->stream = stream;
1471 return(0);
1472error:
1473 xmlFreeStreamComp(stream);
1474 return(0);
1475}
1476
1477/**
1478 * xmlNewStreamCtxt:
1479 * @size: the number of expected states
1480 *
1481 * build a new stream context
1482 *
1483 * Returns the new structure or NULL in case of error.
1484 */
1485static xmlStreamCtxtPtr
1486xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1487 xmlStreamCtxtPtr cur;
1488
1489 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1490 if (cur == NULL) {
1491 ERROR(NULL, NULL, NULL,
1492 "xmlNewStreamCtxt: malloc failed\n");
1493 return(NULL);
1494 }
1495 memset(cur, 0, sizeof(xmlStreamCtxt));
1496 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1497 if (cur->states == NULL) {
1498 xmlFree(cur);
1499 ERROR(NULL, NULL, NULL,
1500 "xmlNewStreamCtxt: malloc failed\n");
1501 return(NULL);
1502 }
1503 cur->nbState = 0;
1504 cur->maxState = 4;
1505 cur->level = 0;
1506 cur->comp = stream;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001507 cur->blockLevel = -1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001508 return(cur);
1509}
1510
1511/**
1512 * xmlFreeStreamCtxt:
1513 * @stream: the stream context
1514 *
1515 * Free the stream context
1516 */
1517void
1518xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001519 xmlStreamCtxtPtr next;
1520
1521 while (stream != NULL) {
1522 next = stream->next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001523 if (stream->states != NULL)
1524 xmlFree(stream->states);
1525 xmlFree(stream);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001526 stream = next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001527 }
1528}
1529
1530/**
1531 * xmlStreamCtxtAddState:
1532 * @comp: the stream context
1533 * @idx: the step index for that streaming state
1534 *
1535 * Add a new state to the stream context
1536 *
1537 * Returns -1 in case of error or the state index if successful
1538 */
1539static int
1540xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1541 int i;
1542 for (i = 0;i < comp->nbState;i++) {
1543 if (comp->states[2 * i] < 0) {
1544 comp->states[2 * i] = idx;
1545 comp->states[2 * i + 1] = level;
1546 return(i);
1547 }
1548 }
1549 if (comp->nbState >= comp->maxState) {
1550 int *cur;
1551
1552 cur = (int *) xmlRealloc(comp->states,
1553 comp->maxState * 4 * sizeof(int));
1554 if (cur == NULL) {
1555 ERROR(NULL, NULL, NULL,
1556 "xmlNewStreamCtxt: malloc failed\n");
1557 return(-1);
1558 }
1559 comp->states = cur;
1560 comp->maxState *= 2;
1561 }
1562 comp->states[2 * comp->nbState] = idx;
1563 comp->states[2 * comp->nbState++ + 1] = level;
1564 return(comp->nbState - 1);
1565}
1566
1567/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001568 * xmlStreamPushInternal:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001569 * @stream: the stream context
1570 * @name: the current name
1571 * @ns: the namespace name
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001572 * @nodeType: the type of the node
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001573 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001574 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1575 * indicated a dictionary, then strings for name and ns will be expected
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001576 * to come from the dictionary.
1577 * Both @name and @ns being NULL means the / i.e. the root of the document.
1578 * This can also act as a reset.
1579 *
1580 * Returns: -1 in case of error, 1 if the current state in the stream is a
1581 * match and 0 otherwise.
1582 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001583static int
1584xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1585 const xmlChar *name, const xmlChar *ns,
1586 xmlElementType nodeType) {
William M. Brack537f1172005-06-14 22:02:59 +00001587 int ret = 0, err = 0, final = 0, tmp, i, m, match, step, desc;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001588 xmlStreamCompPtr comp;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001589#ifdef DEBUG_STREAMING
1590 xmlStreamCtxtPtr orig = stream;
1591#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001592
1593 if ((stream == NULL) || (stream->nbState < 0))
1594 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001595
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001596 while (stream != NULL) {
1597 comp = stream->comp;
1598 if ((name == NULL) && (ns == NULL)) {
1599 stream->nbState = 0;
1600 stream->level = 0;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001601 stream->blockLevel = -1;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001602 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1603 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1604 if (tmp < 0)
1605 err++;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001606 if (comp->nbStep == 0)
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001607 ret = 1;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001608 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001609 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001610 continue; /* while */
1611 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001612
1613 /*
1614 * Fast check for ".".
1615 */
1616 if (comp->nbStep == 0) {
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001617 /*
William M. Brackea152c02005-06-09 18:12:28 +00001618 * For non-pattern like evaluation like XML Schema IDCs
1619 * or traditional XPath expressions, this will match if
1620 * we are at the first level only, otherwise on every level.
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001621 */
1622 if ((nodeType == XML_ELEMENT_NODE) &&
1623 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1624 (stream->level == 0))) {
1625 ret = 1;
1626 }
1627 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001628 goto stream_next;
1629 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001630 if (stream->blockLevel != -1) {
1631 /*
1632 * Skip blocked expressions.
1633 */
1634 stream->level++;
1635 goto stream_next;
William M. Brackea152c02005-06-09 18:12:28 +00001636 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001637 /*
1638 * Check evolution of existing states
1639 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001640 i = 0;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001641 m = stream->nbState;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001642 while (i < m) {
1643 if ((comp->flags & XML_STREAM_DESC) == 0) {
1644 /*
1645 * If there is no "//", then only the last
1646 * added state is of interest.
1647 */
1648 step = stream->states[2 * (stream->nbState -1)];
1649 /*
1650 * TODO: Security check, should not happen, remove it.
1651 */
1652 if (stream->states[(2 * (stream->nbState -1)) + 1] <
1653 stream->level) {
1654 return (-1);
1655 }
1656 desc = 0;
1657 /* loop-stopper */
1658 i = m;
1659 } else {
1660 /*
1661 * If there are "//", then we need to process every "//"
1662 * occuring in the states, plus any other state for this
1663 * level.
1664 */
1665 step = stream->states[2 * i];
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001666
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001667 /* TODO: should not happen anymore: dead states */
1668 if (step < 0)
1669 goto next_state;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001670
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001671 tmp = stream->states[(2 * i) + 1];
1672
1673 /* skip new states just added */
1674 if (tmp > stream->level)
1675 goto next_state;
1676
1677 /* skip states at ancestor levels, except if "//" */
1678 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1679 if ((tmp < stream->level) && (!desc))
1680 goto next_state;
1681 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001682 /*
1683 * Check for correct node-type.
1684 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001685 if ((nodeType == XML_ATTRIBUTE_NODE) &&
1686 ((comp->steps[0].flags & XML_STREAM_STEP_ATTR) == 0))
1687 goto next_state;
1688 /*
1689 * Compare local/namespace-name.
1690 */
1691 match = 0;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001692 if (comp->dict) {
1693 if (comp->steps[step].name == NULL) {
1694 if (comp->steps[step].ns == NULL)
1695 match = 1;
1696 else
1697 match = (comp->steps[step].ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001698 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001699 match = ((comp->steps[step].name == name) &&
1700 (comp->steps[step].ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001701 }
1702 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001703 if (comp->steps[step].name == NULL) {
1704 if (comp->steps[step].ns == NULL)
1705 match = 1;
1706 else
1707 match = xmlStrEqual(comp->steps[step].ns, ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001708 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001709 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
1710 (xmlStrEqual(comp->steps[step].ns, ns)));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001711 }
1712 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001713 if (match) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001714 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
1715 if (desc) {
1716 if (final) {
1717 ret = 1;
1718 } else {
1719 /* descending match create a new state */
1720 xmlStreamCtxtAddState(stream, step + 1,
1721 stream->level + 1);
1722 }
1723 } else {
1724 if (final) {
1725 ret = 1;
1726 } else {
1727 xmlStreamCtxtAddState(stream, step + 1,
1728 stream->level + 1);
1729 }
1730 }
1731 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001732 if (((comp->flags & XML_STREAM_DESC) == 0) &&
1733 ((! match) || final)) {
1734 /*
1735 * Mark this expression as blocked for any evaluation at
1736 * deeper levels. Note that this includes "/foo"
1737 * expressions if the *pattern* behaviour is used.
1738 */
1739 stream->blockLevel = stream->level +1;
1740 }
1741next_state:
1742 i++;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001743 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001744
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001745 stream->level++;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001746
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001747 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001748 * Re/enter the expression.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001749 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001750 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT)
1751 goto stream_next;
1752
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001753 desc = comp->steps[0].flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001754 if (stream->flags & XML_PATTERN_NOTPATTERN) {
1755 /*
1756 * Re/enter the expression if it is a "descendant" one,
1757 * or if we are at the 1st level of evaluation.
1758 */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001759
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001760 if (stream->level == 1) {
1761 if (XML_STREAM_XS_IDC(stream)) {
William M. Brackea152c02005-06-09 18:12:28 +00001762 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001763 * XS-IDC: The missing "self::node()" will always
1764 * match the first given node.
1765 */
William M. Brackea152c02005-06-09 18:12:28 +00001766 goto stream_next;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001767 } else
1768 goto compare;
1769 }
1770 /*
1771 * A "//" is always reentrant.
1772 */
1773 if (desc)
1774 goto compare;
1775
1776 /*
1777 * XS-IDC: Process the 2nd level, since the missing
1778 * "self::node()" is responsible for the 2nd level being
1779 * the real start level.
1780 */
1781 if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
1782 goto compare;
1783
1784 goto stream_next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001785 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001786
1787compare:
1788 /*
1789 * Check expected node-type.
1790 */
1791 if ((nodeType == XML_ATTRIBUTE_NODE) &&
1792 ((comp->steps[0].flags & XML_STREAM_STEP_ATTR) == 0))
1793 goto stream_next;
1794 /*
1795 * Compare local/namespace-name.
1796 */
1797 match = 0;
1798 if (comp->steps[0].name == NULL) {
1799 if (comp->steps[0].ns == NULL)
1800 match = 1;
1801 else {
1802 if (comp->dict)
1803 match = (comp->steps[0].ns == ns);
1804 else
1805 match = xmlStrEqual(comp->steps[0].ns, ns);
1806 }
1807 } else {
1808 if (comp->dict)
1809 match = ((comp->steps[0].name == name) &&
1810 (comp->steps[0].ns == ns));
1811 else
1812 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1813 (xmlStrEqual(comp->steps[0].ns, ns)));
1814 }
1815 if (match) {
1816 final = comp->steps[0].flags & XML_STREAM_STEP_FINAL;
1817 if (final)
1818 ret = 1;
1819 else
1820 xmlStreamCtxtAddState(stream, 1, stream->level);
1821 }
1822 if (((comp->flags & XML_STREAM_DESC) == 0) &&
1823 ((! match) || final)) {
1824 /*
1825 * Mark this expression as blocked for any evaluation at
1826 * deeper levels.
1827 */
1828 stream->blockLevel = stream->level;
1829 }
1830
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001831stream_next:
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001832 stream = stream->next;
1833 } /* while stream != NULL */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001834
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001835 if (err > 0)
1836 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001837#ifdef DEBUG_STREAMING
1838 xmlDebugStreamCtxt(orig, ret);
1839#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001840 return(ret);
1841}
1842
1843/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001844 * xmlStreamPush:
1845 * @stream: the stream context
1846 * @name: the current name
1847 * @ns: the namespace name
1848 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001849 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1850 * indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001851 * to come from the dictionary.
1852 * Both @name and @ns being NULL means the / i.e. the root of the document.
1853 * This can also act as a reset.
1854 *
1855 * Returns: -1 in case of error, 1 if the current state in the stream is a
1856 * match and 0 otherwise.
1857 */
1858int
1859xmlStreamPush(xmlStreamCtxtPtr stream,
1860 const xmlChar *name, const xmlChar *ns) {
1861 return (xmlStreamPushInternal(stream, name, ns, XML_ELEMENT_NODE));
1862}
1863
1864/**
1865* xmlStreamPushAttr:
1866* @stream: the stream context
1867* @name: the current name
1868* @ns: the namespace name
1869*
William M. Brackfbb619f2005-06-06 13:49:18 +00001870* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
1871* indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001872* to come from the dictionary.
1873* Both @name and @ns being NULL means the / i.e. the root of the document.
1874* This can also act as a reset.
1875*
1876* Returns: -1 in case of error, 1 if the current state in the stream is a
1877* match and 0 otherwise.
1878*/
1879int
1880xmlStreamPushAttr(xmlStreamCtxtPtr stream,
1881 const xmlChar *name, const xmlChar *ns) {
1882 return (xmlStreamPushInternal(stream, name, ns, XML_ATTRIBUTE_NODE));
1883}
1884
1885/**
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001886 * xmlStreamPop:
1887 * @stream: the stream context
1888 *
1889 * push one level from the stream.
1890 *
1891 * Returns: -1 in case of error, 0 otherwise.
1892 */
1893int
1894xmlStreamPop(xmlStreamCtxtPtr stream) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001895 int i, lev;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001896 int ret;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001897
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001898 if (stream == NULL)
1899 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001900 ret = 0;
1901 while (stream != NULL) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001902 /*
1903 * Reset block-level.
1904 */
1905 if (stream->blockLevel == stream->level)
1906 stream->blockLevel = -1;
1907
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001908 stream->level--;
1909 if (stream->level < 0)
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001910 ret = -1;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001911 /*
1912 * Check evolution of existing states
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001913 */
1914 for (i = stream->nbState -1; i >= 0; i--) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001915 /* discard obsoleted states */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001916 lev = stream->states[(2 * i) + 1];
1917 if (lev > stream->level)
1918 stream->nbState--;
1919 if (lev <= stream->level)
1920 break;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001921 }
1922 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001923 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001924 return(0);
1925}
1926
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001927/************************************************************************
1928 * *
1929 * The public interfaces *
1930 * *
1931 ************************************************************************/
1932
1933/**
1934 * xmlPatterncompile:
1935 * @pattern: the pattern to compile
William M. Brackfbb619f2005-06-06 13:49:18 +00001936 * @dict: an optional dictionary for interned strings
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001937 * @flags: compilation flags, undefined yet
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001938 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001939 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001940 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001941 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001942 * Returns the compiled form of the pattern or NULL in case of error
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001943 */
1944xmlPatternPtr
Daniel Veillard427174f2003-12-10 10:42:59 +00001945xmlPatterncompile(const xmlChar *pattern, xmlDict *dict,
William M. Brackea152c02005-06-09 18:12:28 +00001946 xmlPatternFlags flags,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001947 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001948 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001949 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001950 const xmlChar *or, *start;
1951 xmlChar *tmp = NULL;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001952 int type = 0;
1953 int streamable = 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001954
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001955 if (pattern == NULL)
1956 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001957
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001958 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001959 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001960 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001961 tmp = NULL;
1962 while ((*or != 0) && (*or != '|')) or++;
1963 if (*or == 0)
1964 ctxt = xmlNewPatParserContext(start, dict, namespaces);
1965 else {
1966 tmp = xmlStrndup(start, or - start);
1967 if (tmp != NULL) {
1968 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
1969 }
1970 or++;
1971 }
1972 if (ctxt == NULL) goto error;
1973 cur = xmlNewPattern();
1974 if (cur == NULL) goto error;
1975 if (ret == NULL)
1976 ret = cur;
1977 else {
1978 cur->next = ret->next;
1979 ret->next = cur;
1980 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001981 cur->flags = flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001982 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001983
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001984 xmlCompilePathPattern(ctxt);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001985 if (ctxt->error != 0)
1986 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001987 xmlFreePatParserContext(ctxt);
William M. Brackfbb619f2005-06-06 13:49:18 +00001988 ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001989
1990
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001991 if (streamable) {
1992 if (type == 0) {
1993 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
1994 } else if (type == PAT_FROM_ROOT) {
1995 if (cur->flags & PAT_FROM_CUR)
1996 streamable = 0;
1997 } else if (type == PAT_FROM_CUR) {
1998 if (cur->flags & PAT_FROM_ROOT)
1999 streamable = 0;
2000 }
2001 }
2002 if (streamable)
2003 xmlStreamCompile(cur);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002004 if (xmlReversePattern(cur) < 0)
2005 goto error;
William M. Brackea152c02005-06-09 18:12:28 +00002006 if (tmp != NULL) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002007 xmlFree(tmp);
William M. Brackea152c02005-06-09 18:12:28 +00002008 tmp = NULL;
2009 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002010 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002011 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002012 if (streamable == 0) {
2013 cur = ret;
2014 while (cur != NULL) {
2015 if (cur->stream != NULL) {
2016 xmlFreeStreamComp(cur->stream);
2017 cur->stream = NULL;
2018 }
2019 cur = cur->next;
2020 }
2021 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002022
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002023 return(ret);
2024error:
2025 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2026 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002027 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002028 return(NULL);
2029}
2030
2031/**
2032 * xmlPatternMatch:
2033 * @comp: the precompiled pattern
2034 * @node: a node
2035 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002036 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002037 *
2038 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2039 */
2040int
2041xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2042{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002043 int ret = 0;
2044
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002045 if ((comp == NULL) || (node == NULL))
2046 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002047
2048 while (comp != NULL) {
2049 ret = xmlPatMatch(comp, node);
2050 if (ret != 0)
2051 return(ret);
2052 comp = comp->next;
2053 }
2054 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002055}
2056
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002057/**
2058 * xmlPatternGetStreamCtxt:
2059 * @comp: the precompiled pattern
2060 *
2061 * Get a streaming context for that pattern
2062 * Use xmlFreeStreamCtxt to free the context.
2063 *
2064 * Returns a pointer to the context or NULL in case of failure
2065 */
2066xmlStreamCtxtPtr
2067xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2068{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002069 xmlStreamCtxtPtr ret = NULL, cur;
2070
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002071 if ((comp == NULL) || (comp->stream == NULL))
2072 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002073
2074 while (comp != NULL) {
2075 if (comp->stream == NULL)
2076 goto failed;
2077 cur = xmlNewStreamCtxt(comp->stream);
2078 if (cur == NULL)
2079 goto failed;
2080 if (ret == NULL)
2081 ret = cur;
2082 else {
2083 cur->next = ret->next;
2084 ret->next = cur;
2085 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002086 cur->flags = comp->flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002087 comp = comp->next;
2088 }
2089 return(ret);
2090failed:
2091 xmlFreeStreamCtxt(ret);
2092 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002093}
2094
Daniel Veillard56de87e2005-02-16 00:22:29 +00002095/**
2096 * xmlPatternStreamable:
2097 * @comp: the precompiled pattern
2098 *
2099 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2100 * should work.
2101 *
2102 * Returns 1 if streamable, 0 if not and -1 in case of error.
2103 */
2104int
2105xmlPatternStreamable(xmlPatternPtr comp) {
2106 if (comp == NULL)
2107 return(-1);
2108 while (comp != NULL) {
2109 if (comp->stream == NULL)
2110 return(0);
2111 comp = comp->next;
2112 }
2113 return(1);
2114}
2115
2116/**
2117 * xmlPatternMaxDepth:
2118 * @comp: the precompiled pattern
2119 *
2120 * Check the maximum depth reachable by a pattern
2121 *
2122 * Returns -2 if no limit (using //), otherwise the depth,
2123 * and -1 in case of error
2124 */
2125int
2126xmlPatternMaxDepth(xmlPatternPtr comp) {
2127 int ret = 0, i;
2128 if (comp == NULL)
2129 return(-1);
2130 while (comp != NULL) {
2131 if (comp->stream == NULL)
2132 return(-1);
2133 for (i = 0;i < comp->stream->nbStep;i++)
2134 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2135 return(-2);
2136 if (comp->stream->nbStep > ret)
2137 ret = comp->stream->nbStep;
2138 comp = comp->next;
2139 }
2140 return(ret);
2141
2142}
2143
2144/**
2145 * xmlPatternFromRoot:
2146 * @comp: the precompiled pattern
2147 *
2148 * Check if the pattern must be looked at from the root.
2149 *
2150 * Returns 1 if true, 0 if false and -1 in case of error
2151 */
2152int
2153xmlPatternFromRoot(xmlPatternPtr comp) {
2154 if (comp == NULL)
2155 return(-1);
2156 while (comp != NULL) {
2157 if (comp->stream == NULL)
2158 return(-1);
2159 if (comp->flags & PAT_FROM_ROOT)
2160 return(1);
2161 comp = comp->next;
2162 }
2163 return(0);
2164
2165}
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002166
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002167#define bottom_pattern
2168#include "elfgcchack.h"
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002169#endif /* LIBXML_PATTERN_ENABLED */