blob: 2cc6dfbbfbfaff976ea25413c2ff957d90ae36b9 [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
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +000060#define XML_STREAM_XS_IDC(c) ((c)->flags & \
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000061 (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000062
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +000063#define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL)
64
65#define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD)
66
Daniel Veillard2fc6df92005-01-30 18:42:55 +000067typedef struct _xmlStreamStep xmlStreamStep;
68typedef xmlStreamStep *xmlStreamStepPtr;
69struct _xmlStreamStep {
70 int flags; /* properties of that step */
71 const xmlChar *name; /* first string value if NULL accept all */
72 const xmlChar *ns; /* second string value */
73};
74
75typedef struct _xmlStreamComp xmlStreamComp;
76typedef xmlStreamComp *xmlStreamCompPtr;
77struct _xmlStreamComp {
William M. Brackfbb619f2005-06-06 13:49:18 +000078 xmlDict *dict; /* the dictionary if any */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000079 int nbStep; /* number of steps in the automata */
80 int maxStep; /* allocated number of steps */
81 xmlStreamStepPtr steps; /* the array of steps */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000082 int flags;
Daniel Veillard2fc6df92005-01-30 18:42:55 +000083};
84
85struct _xmlStreamCtxt {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +000086 struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000087 xmlStreamCompPtr comp; /* the compiled stream */
William M. Brackfbb619f2005-06-06 13:49:18 +000088 int nbState; /* number of states in the automata */
89 int maxState; /* allocated number of states */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000090 int level; /* how deep are we ? */
91 int *states; /* the array of step indexes */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000092 int flags; /* validation options */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000093 int blockLevel;
Daniel Veillard2fc6df92005-01-30 18:42:55 +000094};
95
96static void xmlFreeStreamComp(xmlStreamCompPtr comp);
97
Daniel Veillardb3de70c2003-12-02 22:32:15 +000098/*
99 * Types are private:
100 */
101
102typedef enum {
103 XML_OP_END=0,
104 XML_OP_ROOT,
105 XML_OP_ELEM,
106 XML_OP_CHILD,
107 XML_OP_ATTR,
108 XML_OP_PARENT,
109 XML_OP_ANCESTOR,
110 XML_OP_NS,
111 XML_OP_ALL
112} xmlPatOp;
113
114
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000115typedef struct _xmlStepState xmlStepState;
116typedef xmlStepState *xmlStepStatePtr;
117struct _xmlStepState {
118 int step;
119 xmlNodePtr node;
120};
121
122typedef struct _xmlStepStates xmlStepStates;
123typedef xmlStepStates *xmlStepStatesPtr;
124struct _xmlStepStates {
125 int nbstates;
126 int maxstates;
127 xmlStepStatePtr states;
128};
129
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000130typedef struct _xmlStepOp xmlStepOp;
131typedef xmlStepOp *xmlStepOpPtr;
132struct _xmlStepOp {
133 xmlPatOp op;
134 const xmlChar *value;
135 const xmlChar *value2;
136};
137
William M. Brackea152c02005-06-09 18:12:28 +0000138#define PAT_FROM_ROOT (1<<8)
139#define PAT_FROM_CUR (1<<9)
Daniel Veillard56de87e2005-02-16 00:22:29 +0000140
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000141struct _xmlPattern {
142 void *data; /* the associated template */
William M. Brackfbb619f2005-06-06 13:49:18 +0000143 xmlDictPtr dict; /* the optional dictionary */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000144 struct _xmlPattern *next; /* next pattern if | is used */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000145 const xmlChar *pattern; /* the pattern */
Daniel Veillardf5812c32005-09-03 13:43:20 +0000146 int flags; /* flags */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000147 int nbStep;
148 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000149 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000150 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000151};
152
153typedef struct _xmlPatParserContext xmlPatParserContext;
154typedef xmlPatParserContext *xmlPatParserContextPtr;
155struct _xmlPatParserContext {
156 const xmlChar *cur; /* the current char being parsed */
157 const xmlChar *base; /* the full expression */
158 int error; /* error code */
William M. Brackfbb619f2005-06-06 13:49:18 +0000159 xmlDictPtr dict; /* the dictionary if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000160 xmlPatternPtr comp; /* the result */
161 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000162 const xmlChar **namespaces; /* the namespaces definitions */
163 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000164};
165
166/************************************************************************
167 * *
168 * Type functions *
169 * *
170 ************************************************************************/
171
172/**
173 * xmlNewPattern:
174 *
175 * Create a new XSLT Pattern
176 *
177 * Returns the newly allocated xmlPatternPtr or NULL in case of error
178 */
179static xmlPatternPtr
180xmlNewPattern(void) {
181 xmlPatternPtr cur;
182
183 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
184 if (cur == NULL) {
185 ERROR(NULL, NULL, NULL,
186 "xmlNewPattern : malloc failed\n");
187 return(NULL);
188 }
189 memset(cur, 0, sizeof(xmlPattern));
190 cur->maxStep = 10;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000191 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
192 if (cur->steps == NULL) {
193 xmlFree(cur);
194 ERROR(NULL, NULL, NULL,
195 "xmlNewPattern : malloc failed\n");
196 return(NULL);
197 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000198 return(cur);
199}
200
201/**
202 * xmlFreePattern:
203 * @comp: an XSLT comp
204 *
205 * Free up the memory allocated by @comp
206 */
207void
208xmlFreePattern(xmlPatternPtr comp) {
209 xmlStepOpPtr op;
210 int i;
211
212 if (comp == NULL)
213 return;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000214 if (comp->next != NULL)
215 xmlFreePattern(comp->next);
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000216 if (comp->stream != NULL)
217 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000218 if (comp->pattern != NULL)
219 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000220 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000221 if (comp->dict == NULL) {
222 for (i = 0;i < comp->nbStep;i++) {
223 op = &comp->steps[i];
224 if (op->value != NULL)
225 xmlFree((xmlChar *) op->value);
226 if (op->value2 != NULL)
227 xmlFree((xmlChar *) op->value2);
228 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000229 }
230 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000231 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000232 if (comp->dict != NULL)
233 xmlDictFree(comp->dict);
234
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000235 memset(comp, -1, sizeof(xmlPattern));
236 xmlFree(comp);
237}
238
239/**
240 * xmlFreePatternList:
241 * @comp: an XSLT comp list
242 *
243 * Free up the memory allocated by all the elements of @comp
244 */
245void
246xmlFreePatternList(xmlPatternPtr comp) {
247 xmlPatternPtr cur;
248
249 while (comp != NULL) {
250 cur = comp;
251 comp = comp->next;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +0000252 cur->next = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000253 xmlFreePattern(cur);
254 }
255}
256
257/**
258 * xmlNewPatParserContext:
259 * @pattern: the pattern context
William M. Brackfbb619f2005-06-06 13:49:18 +0000260 * @dict: the inherited dictionary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000261 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
262 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000263 *
264 * Create a new XML pattern parser context
265 *
266 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
267 */
268static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000269xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
270 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000271 xmlPatParserContextPtr cur;
272
273 if (pattern == NULL)
274 return(NULL);
275
276 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
277 if (cur == NULL) {
278 ERROR(NULL, NULL, NULL,
279 "xmlNewPatParserContext : malloc failed\n");
280 return(NULL);
281 }
282 memset(cur, 0, sizeof(xmlPatParserContext));
283 cur->dict = dict;
284 cur->cur = pattern;
285 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000286 if (namespaces != NULL) {
287 int i;
288 for (i = 0;namespaces[2 * i] != NULL;i++);
289 cur->nb_namespaces = i;
290 } else {
291 cur->nb_namespaces = 0;
292 }
293 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000294 return(cur);
295}
296
297/**
298 * xmlFreePatParserContext:
299 * @ctxt: an XSLT parser context
300 *
301 * Free up the memory allocated by @ctxt
302 */
303static void
304xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
305 if (ctxt == NULL)
306 return;
307 memset(ctxt, -1, sizeof(xmlPatParserContext));
308 xmlFree(ctxt);
309}
310
311/**
312 * xmlPatternAdd:
313 * @comp: the compiled match expression
314 * @op: an op
315 * @value: the first value
316 * @value2: the second value
317 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000318 * Add a step to an XSLT Compiled Match
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000319 *
320 * Returns -1 in case of failure, 0 otherwise.
321 */
322static int
323xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
324 xmlPatternPtr comp,
325 xmlPatOp op, xmlChar * value, xmlChar * value2)
326{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000327 if (comp->nbStep >= comp->maxStep) {
328 xmlStepOpPtr temp;
329 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
330 sizeof(xmlStepOp));
331 if (temp == NULL) {
332 ERROR(ctxt, NULL, NULL,
333 "xmlPatternAdd: realloc failed\n");
334 return (-1);
335 }
336 comp->steps = temp;
337 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000338 }
339 comp->steps[comp->nbStep].op = op;
340 comp->steps[comp->nbStep].value = value;
341 comp->steps[comp->nbStep].value2 = value2;
342 comp->nbStep++;
343 return (0);
344}
345
346#if 0
347/**
348 * xsltSwapTopPattern:
349 * @comp: the compiled match expression
350 *
351 * reverse the two top steps.
352 */
353static void
354xsltSwapTopPattern(xmlPatternPtr comp) {
355 int i;
356 int j = comp->nbStep - 1;
357
358 if (j > 0) {
359 register const xmlChar *tmp;
360 register xmlPatOp op;
361 i = j - 1;
362 tmp = comp->steps[i].value;
363 comp->steps[i].value = comp->steps[j].value;
364 comp->steps[j].value = tmp;
365 tmp = comp->steps[i].value2;
366 comp->steps[i].value2 = comp->steps[j].value2;
367 comp->steps[j].value2 = tmp;
368 op = comp->steps[i].op;
369 comp->steps[i].op = comp->steps[j].op;
370 comp->steps[j].op = op;
371 }
372}
373#endif
374
375/**
376 * xmlReversePattern:
377 * @comp: the compiled match expression
378 *
379 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000380 *
381 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000382 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000383static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000384xmlReversePattern(xmlPatternPtr comp) {
Daniel Veillard56de87e2005-02-16 00:22:29 +0000385 int i, j;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000386
Daniel Veillard56de87e2005-02-16 00:22:29 +0000387 /*
388 * remove the leading // for //a or .//a
389 */
390 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
391 for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
392 comp->steps[i].value = comp->steps[j].value;
393 comp->steps[i].value2 = comp->steps[j].value2;
394 comp->steps[i].op = comp->steps[j].op;
395 }
396 comp->nbStep--;
397 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000398 if (comp->nbStep >= comp->maxStep) {
399 xmlStepOpPtr temp;
400 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
401 sizeof(xmlStepOp));
402 if (temp == NULL) {
403 ERROR(ctxt, NULL, NULL,
404 "xmlReversePattern: realloc failed\n");
405 return (-1);
406 }
407 comp->steps = temp;
408 comp->maxStep *= 2;
409 }
Daniel Veillard56de87e2005-02-16 00:22:29 +0000410 i = 0;
411 j = comp->nbStep - 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000412 while (j > i) {
413 register const xmlChar *tmp;
414 register xmlPatOp op;
415 tmp = comp->steps[i].value;
416 comp->steps[i].value = comp->steps[j].value;
417 comp->steps[j].value = tmp;
418 tmp = comp->steps[i].value2;
419 comp->steps[i].value2 = comp->steps[j].value2;
420 comp->steps[j].value2 = tmp;
421 op = comp->steps[i].op;
422 comp->steps[i].op = comp->steps[j].op;
423 comp->steps[j].op = op;
424 j--;
425 i++;
426 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000427 comp->steps[comp->nbStep].value = NULL;
428 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000429 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000430 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000431}
432
433/************************************************************************
434 * *
435 * The interpreter for the precompiled patterns *
436 * *
437 ************************************************************************/
438
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000439static int
440xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
441 if ((states->states == NULL) || (states->maxstates <= 0)) {
442 states->maxstates = 4;
443 states->nbstates = 0;
444 states->states = xmlMalloc(4 * sizeof(xmlStepState));
445 }
446 else if (states->maxstates <= states->nbstates) {
447 xmlStepState *tmp;
448
449 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
450 2 * states->maxstates * sizeof(xmlStepState));
451 if (tmp == NULL)
452 return(-1);
453 states->states = tmp;
454 states->maxstates *= 2;
455 }
456 states->states[states->nbstates].step = step;
457 states->states[states->nbstates++].node = node;
458#if 0
459 fprintf(stderr, "Push: %d, %s\n", step, node->name);
460#endif
461 return(0);
462}
463
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000464/**
465 * xmlPatMatch:
466 * @comp: the precompiled pattern
467 * @node: a node
468 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000469 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000470 *
471 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
472 */
473static int
474xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
475 int i;
476 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000477 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000478
479 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000480 i = 0;
481restart:
482 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000483 step = &comp->steps[i];
484 switch (step->op) {
485 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000486 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000487 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000488 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000489 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000490 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000491 if ((node->type == XML_DOCUMENT_NODE) ||
492#ifdef LIBXML_DOCB_ENABLED
493 (node->type == XML_DOCB_DOCUMENT_NODE) ||
494#endif
495 (node->type == XML_HTML_DOCUMENT_NODE))
496 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000497 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000498 case XML_OP_ELEM:
499 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000500 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000501 if (step->value == NULL)
502 continue;
503 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000504 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000505 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000506 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000507
508 /* Namespace test */
509 if (node->ns == NULL) {
510 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000511 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000512 } else if (node->ns->href != NULL) {
513 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000514 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000515 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000516 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000517 }
518 continue;
519 case XML_OP_CHILD: {
520 xmlNodePtr lst;
521
522 if ((node->type != XML_ELEMENT_NODE) &&
523 (node->type != XML_DOCUMENT_NODE) &&
524#ifdef LIBXML_DOCB_ENABLED
525 (node->type != XML_DOCB_DOCUMENT_NODE) &&
526#endif
527 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000528 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000529
530 lst = node->children;
531
532 if (step->value != NULL) {
533 while (lst != NULL) {
534 if ((lst->type == XML_ELEMENT_NODE) &&
535 (step->value[0] == lst->name[0]) &&
536 (xmlStrEqual(step->value, lst->name)))
537 break;
538 lst = lst->next;
539 }
540 if (lst != NULL)
541 continue;
542 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000543 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000544 }
545 case XML_OP_ATTR:
546 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000547 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000548 if (step->value != NULL) {
549 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000550 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000551 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000552 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000553 }
554 /* Namespace test */
555 if (node->ns == NULL) {
556 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000557 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000558 } else if (step->value2 != NULL) {
559 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000560 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000561 }
562 continue;
563 case XML_OP_PARENT:
564 if ((node->type == XML_DOCUMENT_NODE) ||
565 (node->type == XML_HTML_DOCUMENT_NODE) ||
566#ifdef LIBXML_DOCB_ENABLED
567 (node->type == XML_DOCB_DOCUMENT_NODE) ||
568#endif
569 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000570 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000571 node = node->parent;
572 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000573 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000574 if (step->value == NULL)
575 continue;
576 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000577 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000578 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000579 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000580 /* Namespace test */
581 if (node->ns == NULL) {
582 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000583 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000584 } else if (node->ns->href != NULL) {
585 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000586 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000587 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000588 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000589 }
590 continue;
591 case XML_OP_ANCESTOR:
592 /* TODO: implement coalescing of ANCESTOR/NODE ops */
593 if (step->value == NULL) {
594 i++;
595 step = &comp->steps[i];
596 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000597 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000598 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000599 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000600 if (step->value == NULL)
601 return(-1);
602 }
603 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000604 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000605 if ((node->type == XML_DOCUMENT_NODE) ||
606 (node->type == XML_HTML_DOCUMENT_NODE) ||
607#ifdef LIBXML_DOCB_ENABLED
608 (node->type == XML_DOCB_DOCUMENT_NODE) ||
609#endif
610 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000611 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000612 node = node->parent;
613 while (node != NULL) {
614 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000615 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000616 if ((node->type == XML_ELEMENT_NODE) &&
617 (step->value[0] == node->name[0]) &&
618 (xmlStrEqual(step->value, node->name))) {
619 /* Namespace test */
620 if (node->ns == NULL) {
621 if (step->value2 == NULL)
622 break;
623 } else if (node->ns->href != NULL) {
624 if ((step->value2 != NULL) &&
625 (xmlStrEqual(step->value2, node->ns->href)))
626 break;
627 }
628 }
629 node = node->parent;
630 }
631 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000632 goto rollback;
633 /*
634 * prepare a potential rollback from here
635 * for ancestors of that node.
636 */
637 if (step->op == XML_OP_ANCESTOR)
638 xmlPatPushState(&states, i, node);
639 else
640 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000641 continue;
642 case XML_OP_NS:
643 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000644 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000645 if (node->ns == NULL) {
646 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000647 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000648 } else if (node->ns->href != NULL) {
649 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000650 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000651 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000652 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000653 }
654 break;
655 case XML_OP_ALL:
656 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000657 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000658 break;
659 }
660 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000661found:
662 if (states.states != NULL) {
663 /* Free the rollback states */
664 xmlFree(states.states);
665 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000666 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000667rollback:
668 /* got an error try to rollback */
669 if (states.states == NULL)
670 return(0);
671 if (states.nbstates <= 0) {
672 xmlFree(states.states);
673 return(0);
674 }
675 states.nbstates--;
676 i = states.states[states.nbstates].step;
677 node = states.states[states.nbstates].node;
678#if 0
679 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
680#endif
681 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000682}
683
684/************************************************************************
685 * *
686 * Dedicated parser for templates *
687 * *
688 ************************************************************************/
689
690#define TODO \
691 xmlGenericError(xmlGenericErrorContext, \
692 "Unimplemented block at %s:%d\n", \
693 __FILE__, __LINE__);
694#define CUR (*ctxt->cur)
695#define SKIP(val) ctxt->cur += (val)
696#define NXT(val) ctxt->cur[(val)]
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000697#define PEEKPREV(val) ctxt->cur[-(val)]
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000698#define CUR_PTR ctxt->cur
699
700#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000701 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000702
703#define CURRENT (*ctxt->cur)
704#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
705
706
707#define PUSH(op, val, val2) \
708 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
709
710#define XSLT_ERROR(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000711 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000712 ctxt->error = (X); return; }
713
714#define XSLT_ERROR0(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000715 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000716 ctxt->error = (X); return(0); }
717
718#if 0
719/**
720 * xmlPatScanLiteral:
721 * @ctxt: the XPath Parser context
722 *
723 * Parse an XPath Litteral:
724 *
725 * [29] Literal ::= '"' [^"]* '"'
726 * | "'" [^']* "'"
727 *
728 * Returns the Literal parsed or NULL
729 */
730
731static xmlChar *
732xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
733 const xmlChar *q, *cur;
734 xmlChar *ret = NULL;
735 int val, len;
736
737 SKIP_BLANKS;
738 if (CUR == '"') {
739 NEXT;
740 cur = q = CUR_PTR;
741 val = xmlStringCurrentChar(NULL, cur, &len);
742 while ((IS_CHAR(val)) && (val != '"')) {
743 cur += len;
744 val = xmlStringCurrentChar(NULL, cur, &len);
745 }
746 if (!IS_CHAR(val)) {
747 ctxt->error = 1;
748 return(NULL);
749 } else {
750 ret = xmlStrndup(q, cur - q);
751 }
752 cur += len;
753 CUR_PTR = cur;
754 } else if (CUR == '\'') {
755 NEXT;
756 cur = q = CUR_PTR;
757 val = xmlStringCurrentChar(NULL, cur, &len);
758 while ((IS_CHAR(val)) && (val != '\'')) {
759 cur += len;
760 val = xmlStringCurrentChar(NULL, cur, &len);
761 }
762 if (!IS_CHAR(val)) {
763 ctxt->error = 1;
764 return(NULL);
765 } else {
766 ret = xmlStrndup(q, cur - q);
767 }
768 cur += len;
769 CUR_PTR = cur;
770 } else {
771 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
772 ctxt->error = 1;
773 return(NULL);
774 }
775 return(ret);
776}
777#endif
778
779/**
780 * xmlPatScanName:
781 * @ctxt: the XPath Parser context
782 *
783 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
784 * CombiningChar | Extender
785 *
786 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
787 *
788 * [6] Names ::= Name (S Name)*
789 *
790 * Returns the Name parsed or NULL
791 */
792
793static xmlChar *
794xmlPatScanName(xmlPatParserContextPtr ctxt) {
795 const xmlChar *q, *cur;
796 xmlChar *ret = NULL;
797 int val, len;
798
799 SKIP_BLANKS;
800
801 cur = q = CUR_PTR;
802 val = xmlStringCurrentChar(NULL, cur, &len);
803 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
804 return(NULL);
805
806 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
807 (val == '.') || (val == '-') ||
808 (val == '_') ||
809 (IS_COMBINING(val)) ||
810 (IS_EXTENDER(val))) {
811 cur += len;
812 val = xmlStringCurrentChar(NULL, cur, &len);
813 }
814 ret = xmlStrndup(q, cur - q);
815 CUR_PTR = cur;
816 return(ret);
817}
818
819/**
820 * xmlPatScanNCName:
821 * @ctxt: the XPath Parser context
822 *
823 * Parses a non qualified name
824 *
825 * Returns the Name parsed or NULL
826 */
827
828static xmlChar *
829xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
830 const xmlChar *q, *cur;
831 xmlChar *ret = NULL;
832 int val, len;
833
834 SKIP_BLANKS;
835
836 cur = q = CUR_PTR;
837 val = xmlStringCurrentChar(NULL, cur, &len);
838 if (!IS_LETTER(val) && (val != '_'))
839 return(NULL);
840
841 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
842 (val == '.') || (val == '-') ||
843 (val == '_') ||
844 (IS_COMBINING(val)) ||
845 (IS_EXTENDER(val))) {
846 cur += len;
847 val = xmlStringCurrentChar(NULL, cur, &len);
848 }
849 ret = xmlStrndup(q, cur - q);
850 CUR_PTR = cur;
851 return(ret);
852}
853
854#if 0
855/**
856 * xmlPatScanQName:
857 * @ctxt: the XPath Parser context
858 * @prefix: the place to store the prefix
859 *
860 * Parse a qualified name
861 *
862 * Returns the Name parsed or NULL
863 */
864
865static xmlChar *
866xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
867 xmlChar *ret = NULL;
868
869 *prefix = NULL;
870 ret = xmlPatScanNCName(ctxt);
871 if (CUR == ':') {
872 *prefix = ret;
873 NEXT;
874 ret = xmlPatScanNCName(ctxt);
875 }
876 return(ret);
877}
878#endif
879
880/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000881 * xmlCompileAttributeTest:
882 * @ctxt: the compilation context
883 *
884 * Compile an attribute test.
885 */
886static void
887xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
888 xmlChar *token = NULL;
889 xmlChar *name = NULL;
890 xmlChar *URL = NULL;
891
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000892 SKIP_BLANKS;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000893 name = xmlPatScanNCName(ctxt);
894 if (name == NULL) {
895 if (CUR == '*') {
896 PUSH(XML_OP_ATTR, NULL, NULL);
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +0000897 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000898 } else {
899 ERROR(NULL, NULL, NULL,
900 "xmlCompileAttributeTest : Name expected\n");
901 ctxt->error = 1;
902 }
903 return;
904 }
905 if (CUR == ':') {
906 int i;
907 xmlChar *prefix = name;
908
909 NEXT;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000910
911 if (IS_BLANK_CH(CUR)) {
912 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
913 xmlFree(prefix);
914 ctxt->error = 1;
915 goto error;
916 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000917 /*
918 * This is a namespace match
919 */
920 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000921 if ((prefix[0] == 'x') &&
922 (prefix[1] == 'm') &&
923 (prefix[2] == 'l') &&
924 (prefix[3] == 0)) {
925 URL = xmlStrdup(XML_XML_NAMESPACE);
926 } else {
927 for (i = 0;i < ctxt->nb_namespaces;i++) {
928 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
929 URL = xmlStrdup(ctxt->namespaces[2 * i]);
930 break;
931 }
932 }
933 if (i >= ctxt->nb_namespaces) {
934 ERROR5(NULL, NULL, NULL,
935 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
936 prefix);
937 ctxt->error = 1;
938 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000939 }
940 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000941 xmlFree(prefix);
942 if (token == NULL) {
943 if (CUR == '*') {
944 NEXT;
945 PUSH(XML_OP_ATTR, NULL, URL);
946 } else {
947 ERROR(NULL, NULL, NULL,
948 "xmlCompileAttributeTest : Name expected\n");
949 ctxt->error = 1;
950 goto error;
951 }
952 } else {
953 PUSH(XML_OP_ATTR, token, URL);
954 }
955 } else {
956 PUSH(XML_OP_ATTR, name, NULL);
957 }
958 return;
959error:
960 if (URL != NULL)
961 xmlFree(URL);
962 if (token != NULL)
963 xmlFree(token);
964}
965
966
967/**
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000968 * xmlCompileStepPattern:
969 * @ctxt: the compilation context
970 *
971 * Compile the Step Pattern and generates a precompiled
972 * form suitable for fast matching.
973 *
974 * [3] Step ::= '.' | NameTest
975 * [4] NameTest ::= QName | '*' | NCName ':' '*'
976 */
977
978static void
979xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
980 xmlChar *token = NULL;
981 xmlChar *name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000982 xmlChar *URL = NULL;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000983 int hasBlanks = 0;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000984
985 SKIP_BLANKS;
986 if (CUR == '.') {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000987 /*
988 * Context node.
989 */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000990 NEXT;
991 PUSH(XML_OP_ELEM, NULL, NULL);
992 return;
993 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000994 if (CUR == '@') {
995 /*
996 * Attribute test.
997 */
998 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
999 ERROR5(NULL, NULL, NULL,
1000 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1001 ctxt->error = 1;
1002 return;
1003 }
1004 NEXT;
1005 xmlCompileAttributeTest(ctxt);
1006 if (ctxt->error != 0)
1007 goto error;
1008 return;
1009 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001010 name = xmlPatScanNCName(ctxt);
1011 if (name == NULL) {
1012 if (CUR == '*') {
1013 NEXT;
1014 PUSH(XML_OP_ALL, NULL, NULL);
1015 return;
1016 } else {
1017 ERROR(NULL, NULL, NULL,
1018 "xmlCompileStepPattern : Name expected\n");
1019 ctxt->error = 1;
1020 return;
1021 }
1022 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001023 if (IS_BLANK_CH(CUR)) {
1024 hasBlanks = 1;
1025 SKIP_BLANKS;
1026 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001027 if (CUR == ':') {
1028 NEXT;
1029 if (CUR != ':') {
1030 xmlChar *prefix = name;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001031 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001032
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001033 if (hasBlanks || IS_BLANK_CH(CUR)) {
1034 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1035 ctxt->error = 1;
1036 goto error;
1037 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001038 /*
1039 * This is a namespace match
1040 */
1041 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001042 if ((prefix[0] == 'x') &&
1043 (prefix[1] == 'm') &&
1044 (prefix[2] == 'l') &&
1045 (prefix[3] == 0)) {
1046 URL = xmlStrdup(XML_XML_NAMESPACE);
1047 } else {
1048 for (i = 0;i < ctxt->nb_namespaces;i++) {
1049 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1050 URL = xmlStrdup(ctxt->namespaces[2 * i]);
1051 break;
1052 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +00001053 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001054 if (i >= ctxt->nb_namespaces) {
1055 ERROR5(NULL, NULL, NULL,
1056 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1057 prefix);
1058 ctxt->error = 1;
1059 goto error;
1060 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001061 }
1062 xmlFree(prefix);
1063 if (token == NULL) {
1064 if (CUR == '*') {
1065 NEXT;
1066 PUSH(XML_OP_NS, URL, NULL);
1067 } else {
1068 ERROR(NULL, NULL, NULL,
1069 "xmlCompileStepPattern : Name expected\n");
1070 ctxt->error = 1;
1071 goto error;
1072 }
1073 } else {
1074 PUSH(XML_OP_ELEM, token, URL);
1075 }
1076 } else {
1077 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001078 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1079 xmlFree(name);
1080 name = xmlPatScanName(ctxt);
1081 if (name == NULL) {
1082 if (CUR == '*') {
1083 NEXT;
1084 PUSH(XML_OP_ALL, NULL, NULL);
1085 return;
1086 } else {
1087 ERROR(NULL, NULL, NULL,
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001088 "xmlCompileStepPattern : QName expected\n");
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001089 ctxt->error = 1;
1090 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001091 }
1092 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001093 if (CUR == ':') {
1094 xmlChar *prefix = name;
1095 int i;
1096
1097 NEXT;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001098 if (IS_BLANK_CH(CUR)) {
1099 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1100 ctxt->error = 1;
1101 goto error;
1102 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001103 /*
1104 * This is a namespace match
1105 */
1106 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001107 if ((prefix[0] == 'x') &&
1108 (prefix[1] == 'm') &&
1109 (prefix[2] == 'l') &&
1110 (prefix[3] == 0)) {
1111 URL = xmlStrdup(XML_XML_NAMESPACE);
1112 } else {
1113 for (i = 0;i < ctxt->nb_namespaces;i++) {
1114 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1115 URL = xmlStrdup(ctxt->namespaces[2 * i]);
1116 break;
1117 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001118 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001119 if (i >= ctxt->nb_namespaces) {
1120 ERROR5(NULL, NULL, NULL,
1121 "xmlCompileStepPattern : no namespace bound "
1122 "to prefix %s\n", prefix);
1123 ctxt->error = 1;
1124 goto error;
1125 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001126 }
1127 xmlFree(prefix);
1128 if (token == NULL) {
1129 if (CUR == '*') {
1130 NEXT;
1131 PUSH(XML_OP_NS, URL, NULL);
1132 } else {
1133 ERROR(NULL, NULL, NULL,
1134 "xmlCompileStepPattern : Name expected\n");
1135 ctxt->error = 1;
1136 goto error;
1137 }
1138 } else {
1139 PUSH(XML_OP_CHILD, token, URL);
1140 }
1141 } else
1142 PUSH(XML_OP_CHILD, name, NULL);
1143 return;
1144 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1145 xmlFree(name);
1146 name = NULL;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001147 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1148 ERROR5(NULL, NULL, NULL,
1149 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1150 ctxt->error = 1;
1151 goto error;
1152 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001153 xmlCompileAttributeTest(ctxt);
1154 if (ctxt->error != 0)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001155 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001156 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001157 } else {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001158 ERROR5(NULL, NULL, NULL,
1159 "The 'element' or 'attribute' axis is expected.\n", NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001160 ctxt->error = 1;
1161 goto error;
1162 }
Daniel Veillard0e460da2005-03-30 22:47:10 +00001163 /* NOT REACHED xmlFree(name); */
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001164 }
1165 } else if (CUR == '*') {
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001166 if (name != NULL) {
1167 ctxt->error = 1;
1168 goto error;
1169 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001170 NEXT;
1171 PUSH(XML_OP_ALL, token, NULL);
1172 } else {
1173 if (name == NULL) {
1174 ctxt->error = 1;
1175 goto error;
1176 }
1177 PUSH(XML_OP_ELEM, name, NULL);
1178 }
1179 return;
1180error:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001181 if (URL != NULL)
1182 xmlFree(URL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001183 if (token != NULL)
1184 xmlFree(token);
1185 if (name != NULL)
1186 xmlFree(name);
1187}
1188
1189/**
1190 * xmlCompilePathPattern:
1191 * @ctxt: the compilation context
1192 *
1193 * Compile the Path Pattern and generates a precompiled
1194 * form suitable for fast matching.
1195 *
1196 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1197 */
1198static void
1199xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1200 SKIP_BLANKS;
William M. Brack537f1172005-06-14 22:02:59 +00001201 if (CUR == '/') {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001202 ctxt->comp->flags |= PAT_FROM_ROOT;
William M. Brackea152c02005-06-09 18:12:28 +00001203 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001204 ctxt->comp->flags |= PAT_FROM_CUR;
1205 }
William M. Brackea152c02005-06-09 18:12:28 +00001206
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001207 if ((CUR == '/') && (NXT(1) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001208 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001209 NEXT;
1210 NEXT;
1211 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001212 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001213 NEXT;
1214 NEXT;
1215 NEXT;
1216 }
1217 if (CUR == '@') {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001218 NEXT;
1219 xmlCompileAttributeTest(ctxt);
1220 SKIP_BLANKS;
William M. Brackfbb619f2005-06-06 13:49:18 +00001221 if (CUR != 0) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001222 xmlCompileStepPattern(ctxt);
1223 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001224 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001225 if (CUR == '/') {
1226 PUSH(XML_OP_ROOT, NULL, NULL);
1227 NEXT;
1228 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001229 xmlCompileStepPattern(ctxt);
1230 SKIP_BLANKS;
1231 while (CUR == '/') {
William M. Brackfbb619f2005-06-06 13:49:18 +00001232 if (NXT(1) == '/') {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001233 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1234 NEXT;
1235 NEXT;
1236 SKIP_BLANKS;
1237 xmlCompileStepPattern(ctxt);
1238 } else {
1239 PUSH(XML_OP_PARENT, NULL, NULL);
1240 NEXT;
1241 SKIP_BLANKS;
William M. Brackfbb619f2005-06-06 13:49:18 +00001242 if (CUR != 0) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001243 xmlCompileStepPattern(ctxt);
1244 }
1245 }
1246 }
1247 }
Daniel Veillard56de87e2005-02-16 00:22:29 +00001248 if (CUR != 0) {
1249 ERROR5(NULL, NULL, NULL,
1250 "Failed to compile pattern %s\n", ctxt->base);
1251 ctxt->error = 1;
1252 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001253error:
1254 return;
1255}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001256
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001257/**
1258 * xmlCompileIDCXPathPath:
1259 * @ctxt: the compilation context
1260 *
1261 * Compile the Path Pattern and generates a precompiled
1262 * form suitable for fast matching.
1263 *
1264 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1265 */
1266static void
1267xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
1268 SKIP_BLANKS;
1269 if (CUR == '/') {
1270 ERROR5(NULL, NULL, NULL,
1271 "Unexpected selection of the document root in '%s'.\n",
1272 ctxt->base);
1273 goto error;
1274 }
1275 ctxt->comp->flags |= PAT_FROM_CUR;
1276
1277 if (CUR == '.') {
1278 /* "." - "self::node()" */
1279 NEXT;
1280 SKIP_BLANKS;
1281 if (CUR == 0) {
1282 /*
1283 * Selection of the context node.
1284 */
1285 PUSH(XML_OP_ELEM, NULL, NULL);
1286 return;
1287 }
1288 if (CUR != '/') {
1289 /* TODO: A more meaningful error message. */
1290 ERROR5(NULL, NULL, NULL,
1291 "Unexpected token after '.' in '%s'.\n", ctxt->base);
1292 goto error;
1293 }
1294 /* "./" - "self::node()/" */
1295 NEXT;
1296 SKIP_BLANKS;
1297 if (CUR == '/') {
1298 if (IS_BLANK_CH(PEEKPREV(1))) {
1299 /*
1300 * Disallow "./ /"
1301 */
1302 ERROR5(NULL, NULL, NULL,
1303 "Unexpected '/' token in '%s'.\n", ctxt->base);
1304 goto error;
1305 }
1306 /* ".//" - "self:node()/descendant-or-self::node()/" */
1307 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1308 NEXT;
1309 SKIP_BLANKS;
1310 }
1311 if (CUR == 0)
1312 goto error_unfinished;
1313 }
1314 /*
1315 * Process steps.
1316 */
1317 do {
1318 xmlCompileStepPattern(ctxt);
1319 if (ctxt->error != 0)
1320 goto error;
1321 SKIP_BLANKS;
1322 if (CUR != '/')
1323 break;
1324 PUSH(XML_OP_PARENT, NULL, NULL);
1325 NEXT;
1326 SKIP_BLANKS;
1327 if (CUR == '/') {
1328 /*
1329 * Disallow subsequent '//'.
1330 */
1331 ERROR5(NULL, NULL, NULL,
1332 "Unexpected subsequent '//' in '%s'.\n",
1333 ctxt->base);
1334 goto error;
1335 }
1336 if (CUR == 0)
1337 goto error_unfinished;
1338
1339 } while (CUR != 0);
1340
1341 if (CUR != 0) {
1342 ERROR5(NULL, NULL, NULL,
1343 "Failed to compile expression '%s'.\n", ctxt->base);
1344 ctxt->error = 1;
1345 }
1346 return;
1347error:
1348 ctxt->error = 1;
1349 return;
1350
1351error_unfinished:
1352 ctxt->error = 1;
1353 ERROR5(NULL, NULL, NULL,
1354 "Unfinished expression '%s'.\n", ctxt->base);
1355 return;
1356}
1357
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001358/************************************************************************
1359 * *
1360 * The streaming code *
1361 * *
1362 ************************************************************************/
1363
1364#ifdef DEBUG_STREAMING
1365static void
1366xmlDebugStreamComp(xmlStreamCompPtr stream) {
1367 int i;
1368
1369 if (stream == NULL) {
1370 printf("Stream: NULL\n");
1371 return;
1372 }
1373 printf("Stream: %d steps\n", stream->nbStep);
1374 for (i = 0;i < stream->nbStep;i++) {
1375 if (stream->steps[i].ns != NULL) {
1376 printf("{%s}", stream->steps[i].ns);
1377 }
1378 if (stream->steps[i].name == NULL) {
1379 printf("* ");
1380 } else {
1381 printf("%s ", stream->steps[i].name);
1382 }
1383 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1384 printf("root ");
1385 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1386 printf("// ");
1387 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1388 printf("final ");
1389 printf("\n");
1390 }
1391}
1392static void
1393xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1394 int i;
1395
1396 if (ctxt == NULL) {
1397 printf("Stream: NULL\n");
1398 return;
1399 }
1400 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1401 if (match)
1402 printf("matches\n");
1403 else
1404 printf("\n");
1405 for (i = 0;i < ctxt->nbState;i++) {
1406 if (ctxt->states[2 * i] < 0)
1407 printf(" %d: free\n", i);
1408 else {
1409 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1410 ctxt->states[(2 * i) + 1]);
1411 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1412 XML_STREAM_STEP_DESC)
1413 printf(" //\n");
1414 else
1415 printf("\n");
1416 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001417 }
1418}
1419#endif
1420/**
1421 * xmlNewStreamComp:
1422 * @size: the number of expected steps
1423 *
1424 * build a new compiled pattern for streaming
1425 *
1426 * Returns the new structure or NULL in case of error.
1427 */
1428static xmlStreamCompPtr
1429xmlNewStreamComp(int size) {
1430 xmlStreamCompPtr cur;
1431
1432 if (size < 4)
1433 size = 4;
1434
1435 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1436 if (cur == NULL) {
1437 ERROR(NULL, NULL, NULL,
1438 "xmlNewStreamComp: malloc failed\n");
1439 return(NULL);
1440 }
1441 memset(cur, 0, sizeof(xmlStreamComp));
1442 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1443 if (cur->steps == NULL) {
1444 xmlFree(cur);
1445 ERROR(NULL, NULL, NULL,
1446 "xmlNewStreamComp: malloc failed\n");
1447 return(NULL);
1448 }
1449 cur->nbStep = 0;
1450 cur->maxStep = size;
1451 return(cur);
1452}
1453
1454/**
1455 * xmlFreeStreamComp:
1456 * @comp: the compiled pattern for streaming
1457 *
1458 * Free the compiled pattern for streaming
1459 */
1460static void
1461xmlFreeStreamComp(xmlStreamCompPtr comp) {
1462 if (comp != NULL) {
1463 if (comp->steps != NULL)
1464 xmlFree(comp->steps);
1465 if (comp->dict != NULL)
1466 xmlDictFree(comp->dict);
1467 xmlFree(comp);
1468 }
1469}
1470
1471/**
1472 * xmlStreamCompAddStep:
1473 * @comp: the compiled pattern for streaming
1474 * @name: the first string, the name, or NULL for *
1475 * @ns: the second step, the namespace name
1476 * @flags: the flags for that step
1477 *
1478 * Add a new step to the compiled pattern
1479 *
1480 * Returns -1 in case of error or the step index if successful
1481 */
1482static int
1483xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1484 const xmlChar *ns, int flags) {
1485 xmlStreamStepPtr cur;
1486
1487 if (comp->nbStep >= comp->maxStep) {
1488 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1489 comp->maxStep * 2 * sizeof(xmlStreamStep));
1490 if (cur == NULL) {
1491 ERROR(NULL, NULL, NULL,
1492 "xmlNewStreamComp: malloc failed\n");
1493 return(-1);
1494 }
1495 comp->steps = cur;
1496 comp->maxStep *= 2;
1497 }
1498 cur = &comp->steps[comp->nbStep++];
1499 cur->flags = flags;
1500 cur->name = name;
1501 cur->ns = ns;
1502 return(comp->nbStep - 1);
1503}
1504
1505/**
1506 * xmlStreamCompile:
1507 * @comp: the precompiled pattern
1508 *
1509 * Tries to stream compile a pattern
1510 *
1511 * Returns -1 in case of failure and 0 in case of success.
1512 */
1513static int
1514xmlStreamCompile(xmlPatternPtr comp) {
1515 xmlStreamCompPtr stream;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001516 int i, s = 0, root = 0, flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001517
1518 if ((comp == NULL) || (comp->steps == NULL))
1519 return(-1);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001520 /*
1521 * special case for .
1522 */
1523 if ((comp->nbStep == 1) &&
1524 (comp->steps[0].op == XML_OP_ELEM) &&
1525 (comp->steps[0].value == NULL) &&
1526 (comp->steps[0].value2 == NULL)) {
1527 stream = xmlNewStreamComp(0);
1528 if (stream == NULL)
1529 return(-1);
1530 comp->stream = stream;
1531 return(0);
1532 }
1533
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001534 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1535 if (stream == NULL)
1536 return(-1);
1537 if (comp->dict != NULL) {
1538 stream->dict = comp->dict;
1539 xmlDictReference(stream->dict);
1540 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001541
1542 /*
1543 * Skip leading ./ on relative paths
1544 */
1545 i = 0;
1546 while ((comp->flags & PAT_FROM_CUR) && (comp->nbStep > i + 2) &&
1547 (comp->steps[i].op == XML_OP_ELEM) &&
1548 (comp->steps[i].value == NULL) &&
1549 (comp->steps[i].value2 == NULL) &&
1550 (comp->steps[i + 1].op == XML_OP_PARENT)) {
1551 i += 2;
1552 }
1553 for (;i < comp->nbStep;i++) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001554 switch (comp->steps[i].op) {
1555 case XML_OP_END:
1556 break;
1557 case XML_OP_ROOT:
1558 if (i != 0)
1559 goto error;
1560 root = 1;
1561 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001562 case XML_OP_NS:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001563 s = xmlStreamCompAddStep(stream, NULL,
1564 comp->steps[i].value, flags);
1565 flags = 0;
1566 if (s < 0)
1567 goto error;
1568 break;
1569 case XML_OP_ATTR:
1570 flags |= XML_STREAM_STEP_ATTR;
1571 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1572 comp->steps[i].value2, flags);
1573 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001574 if (s < 0)
1575 goto error;
1576 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001577 case XML_OP_ELEM:
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001578 if ((comp->steps[i].value == NULL) &&
1579 (comp->steps[i].value2 == NULL) &&
1580 (comp->nbStep > i + 2) &&
1581 (comp->steps[i + 1].op == XML_OP_PARENT)) {
1582 i++;
1583 continue;
1584 }
1585 case XML_OP_CHILD:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001586 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1587 comp->steps[i].value2, flags);
1588 flags = 0;
1589 if (s < 0)
1590 goto error;
1591 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001592 case XML_OP_ALL:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001593 s = xmlStreamCompAddStep(stream, NULL, NULL, flags);
1594 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001595 if (s < 0)
1596 goto error;
1597 break;
1598 case XML_OP_PARENT:
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001599 if ((comp->nbStep > i + 1) &&
1600 (comp->steps[i + 1].op == XML_OP_ELEM) &&
1601 (comp->steps[i + 1].value == NULL) &&
1602 (comp->steps[i + 1].value2 == NULL)) {
1603 i++;
1604 continue;
1605 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001606 break;
1607 case XML_OP_ANCESTOR:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001608 flags |= XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001609 /*
1610 * Mark the expression as having "//".
1611 */
1612 if ((stream->flags & XML_STREAM_DESC) == 0)
1613 stream->flags |= XML_STREAM_DESC;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001614 break;
1615 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001616 }
1617 if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1618 /*
1619 * If this should behave like a real pattern, we will mark
1620 * the first step as having "//", to be reentrant on every
1621 * tree level.
1622 */
1623 if ((stream->flags & XML_STREAM_DESC) == 0)
1624 stream->flags |= XML_STREAM_DESC;
1625
1626 if (stream->nbStep > 0) {
1627 if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0)
1628 stream->steps[0].flags |= XML_STREAM_STEP_DESC;
1629 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001630 }
1631 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1632 if (root)
1633 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1634#ifdef DEBUG_STREAMING
1635 xmlDebugStreamComp(stream);
1636#endif
1637 comp->stream = stream;
1638 return(0);
1639error:
1640 xmlFreeStreamComp(stream);
1641 return(0);
1642}
1643
1644/**
1645 * xmlNewStreamCtxt:
1646 * @size: the number of expected states
1647 *
1648 * build a new stream context
1649 *
1650 * Returns the new structure or NULL in case of error.
1651 */
1652static xmlStreamCtxtPtr
1653xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1654 xmlStreamCtxtPtr cur;
1655
1656 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1657 if (cur == NULL) {
1658 ERROR(NULL, NULL, NULL,
1659 "xmlNewStreamCtxt: malloc failed\n");
1660 return(NULL);
1661 }
1662 memset(cur, 0, sizeof(xmlStreamCtxt));
1663 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1664 if (cur->states == NULL) {
1665 xmlFree(cur);
1666 ERROR(NULL, NULL, NULL,
1667 "xmlNewStreamCtxt: malloc failed\n");
1668 return(NULL);
1669 }
1670 cur->nbState = 0;
1671 cur->maxState = 4;
1672 cur->level = 0;
1673 cur->comp = stream;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001674 cur->blockLevel = -1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001675 return(cur);
1676}
1677
1678/**
1679 * xmlFreeStreamCtxt:
1680 * @stream: the stream context
1681 *
1682 * Free the stream context
1683 */
1684void
1685xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001686 xmlStreamCtxtPtr next;
1687
1688 while (stream != NULL) {
1689 next = stream->next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001690 if (stream->states != NULL)
1691 xmlFree(stream->states);
1692 xmlFree(stream);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001693 stream = next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001694 }
1695}
1696
1697/**
1698 * xmlStreamCtxtAddState:
1699 * @comp: the stream context
1700 * @idx: the step index for that streaming state
1701 *
1702 * Add a new state to the stream context
1703 *
1704 * Returns -1 in case of error or the state index if successful
1705 */
1706static int
1707xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1708 int i;
1709 for (i = 0;i < comp->nbState;i++) {
1710 if (comp->states[2 * i] < 0) {
1711 comp->states[2 * i] = idx;
1712 comp->states[2 * i + 1] = level;
1713 return(i);
1714 }
1715 }
1716 if (comp->nbState >= comp->maxState) {
1717 int *cur;
1718
1719 cur = (int *) xmlRealloc(comp->states,
1720 comp->maxState * 4 * sizeof(int));
1721 if (cur == NULL) {
1722 ERROR(NULL, NULL, NULL,
1723 "xmlNewStreamCtxt: malloc failed\n");
1724 return(-1);
1725 }
1726 comp->states = cur;
1727 comp->maxState *= 2;
1728 }
1729 comp->states[2 * comp->nbState] = idx;
1730 comp->states[2 * comp->nbState++ + 1] = level;
1731 return(comp->nbState - 1);
1732}
1733
1734/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001735 * xmlStreamPushInternal:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001736 * @stream: the stream context
1737 * @name: the current name
1738 * @ns: the namespace name
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001739 * @nodeType: the type of the node
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001740 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001741 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1742 * indicated a dictionary, then strings for name and ns will be expected
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001743 * to come from the dictionary.
1744 * Both @name and @ns being NULL means the / i.e. the root of the document.
1745 * This can also act as a reset.
1746 *
1747 * Returns: -1 in case of error, 1 if the current state in the stream is a
1748 * match and 0 otherwise.
1749 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001750static int
1751xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1752 const xmlChar *name, const xmlChar *ns,
1753 xmlElementType nodeType) {
William M. Brack537f1172005-06-14 22:02:59 +00001754 int ret = 0, err = 0, final = 0, tmp, i, m, match, step, desc;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001755 xmlStreamCompPtr comp;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001756#ifdef DEBUG_STREAMING
1757 xmlStreamCtxtPtr orig = stream;
1758#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001759
1760 if ((stream == NULL) || (stream->nbState < 0))
1761 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001762
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001763 while (stream != NULL) {
1764 comp = stream->comp;
1765 if ((name == NULL) && (ns == NULL)) {
1766 stream->nbState = 0;
1767 stream->level = 0;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001768 stream->blockLevel = -1;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001769 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1770 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1771 if (tmp < 0)
1772 err++;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001773 if (comp->nbStep == 0)
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001774 ret = 1;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001775 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001776 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001777 continue; /* while */
1778 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001779
1780 /*
1781 * Fast check for ".".
1782 */
1783 if (comp->nbStep == 0) {
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001784 /*
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00001785 * / and . are handled at the XPath node set creation
1786 * level by checking min depth
1787 */
1788 if (stream->flags & XML_PATTERN_XPATH) {
1789 stream = stream->next;
1790 continue; /* while */
1791 }
1792 /*
William M. Brackea152c02005-06-09 18:12:28 +00001793 * For non-pattern like evaluation like XML Schema IDCs
1794 * or traditional XPath expressions, this will match if
1795 * we are at the first level only, otherwise on every level.
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001796 */
1797 if ((nodeType == XML_ELEMENT_NODE) &&
1798 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1799 (stream->level == 0))) {
1800 ret = 1;
1801 }
1802 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001803 goto stream_next;
1804 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001805 if (stream->blockLevel != -1) {
1806 /*
1807 * Skip blocked expressions.
1808 */
1809 stream->level++;
1810 goto stream_next;
William M. Brackea152c02005-06-09 18:12:28 +00001811 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001812 /*
1813 * Check evolution of existing states
1814 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001815 i = 0;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001816 m = stream->nbState;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001817 while (i < m) {
1818 if ((comp->flags & XML_STREAM_DESC) == 0) {
1819 /*
1820 * If there is no "//", then only the last
1821 * added state is of interest.
1822 */
1823 step = stream->states[2 * (stream->nbState -1)];
1824 /*
1825 * TODO: Security check, should not happen, remove it.
1826 */
1827 if (stream->states[(2 * (stream->nbState -1)) + 1] <
1828 stream->level) {
1829 return (-1);
1830 }
1831 desc = 0;
1832 /* loop-stopper */
1833 i = m;
1834 } else {
1835 /*
1836 * If there are "//", then we need to process every "//"
1837 * occuring in the states, plus any other state for this
1838 * level.
1839 */
1840 step = stream->states[2 * i];
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001841
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001842 /* TODO: should not happen anymore: dead states */
1843 if (step < 0)
1844 goto next_state;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001845
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001846 tmp = stream->states[(2 * i) + 1];
1847
1848 /* skip new states just added */
1849 if (tmp > stream->level)
1850 goto next_state;
1851
1852 /* skip states at ancestor levels, except if "//" */
1853 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1854 if ((tmp < stream->level) && (!desc))
1855 goto next_state;
1856 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001857 /*
1858 * Check for correct node-type.
1859 */
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00001860 if (nodeType == XML_ELEMENT_NODE) {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001861 if (comp->steps[step].flags & XML_STREAM_STEP_ATTR) {
1862 /*
1863 * Block this expression for deeper evaluation.
1864 */
1865 if ((comp->flags & XML_STREAM_DESC) == 0)
1866 stream->blockLevel = stream->level +1;
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00001867 goto next_state;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001868 }
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00001869
1870 } else if (nodeType == XML_ATTRIBUTE_NODE) {
1871 if ((comp->steps[step].flags & XML_STREAM_STEP_ATTR) == 0)
1872 goto next_state;
1873 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001874 /*
1875 * Compare local/namespace-name.
1876 */
1877 match = 0;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001878 if (comp->dict) {
1879 if (comp->steps[step].name == NULL) {
1880 if (comp->steps[step].ns == NULL)
1881 match = 1;
1882 else
1883 match = (comp->steps[step].ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001884 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001885 match = ((comp->steps[step].name == name) &&
1886 (comp->steps[step].ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001887 }
1888 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001889 if (comp->steps[step].name == NULL) {
1890 if (comp->steps[step].ns == NULL)
1891 match = 1;
1892 else
1893 match = xmlStrEqual(comp->steps[step].ns, ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001894 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001895 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
1896 (xmlStrEqual(comp->steps[step].ns, ns)));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001897 }
1898 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001899 if (match) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001900 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
1901 if (desc) {
1902 if (final) {
1903 ret = 1;
1904 } else {
1905 /* descending match create a new state */
1906 xmlStreamCtxtAddState(stream, step + 1,
1907 stream->level + 1);
1908 }
1909 } else {
1910 if (final) {
1911 ret = 1;
1912 } else {
1913 xmlStreamCtxtAddState(stream, step + 1,
1914 stream->level + 1);
1915 }
1916 }
1917 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001918 if (((comp->flags & XML_STREAM_DESC) == 0) &&
1919 ((! match) || final)) {
1920 /*
1921 * Mark this expression as blocked for any evaluation at
1922 * deeper levels. Note that this includes "/foo"
1923 * expressions if the *pattern* behaviour is used.
1924 */
1925 stream->blockLevel = stream->level +1;
1926 }
1927next_state:
1928 i++;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001929 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001930
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001931 stream->level++;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001932
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001933 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001934 * Re/enter the expression.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001935 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001936 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT)
1937 goto stream_next;
1938
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001939 desc = comp->steps[0].flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001940 if (stream->flags & XML_PATTERN_NOTPATTERN) {
1941 /*
1942 * Re/enter the expression if it is a "descendant" one,
1943 * or if we are at the 1st level of evaluation.
1944 */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001945
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001946 if (stream->level == 1) {
1947 if (XML_STREAM_XS_IDC(stream)) {
William M. Brackea152c02005-06-09 18:12:28 +00001948 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001949 * XS-IDC: The missing "self::node()" will always
1950 * match the first given node.
1951 */
William M. Brackea152c02005-06-09 18:12:28 +00001952 goto stream_next;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001953 } else
1954 goto compare;
1955 }
1956 /*
1957 * A "//" is always reentrant.
1958 */
1959 if (desc)
1960 goto compare;
1961
1962 /*
1963 * XS-IDC: Process the 2nd level, since the missing
1964 * "self::node()" is responsible for the 2nd level being
1965 * the real start level.
1966 */
1967 if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
1968 goto compare;
1969
1970 goto stream_next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001971 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001972
1973compare:
1974 /*
1975 * Check expected node-type.
1976 */
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00001977 if (nodeType == XML_ELEMENT_NODE) {
1978 if (comp->steps[0].flags & XML_STREAM_STEP_ATTR)
1979 goto stream_next;
1980
1981 } else if (nodeType == XML_ATTRIBUTE_NODE) {
1982 if ((comp->steps[0].flags & XML_STREAM_STEP_ATTR) == 0)
1983 goto stream_next;
1984 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001985 /*
1986 * Compare local/namespace-name.
1987 */
1988 match = 0;
1989 if (comp->steps[0].name == NULL) {
1990 if (comp->steps[0].ns == NULL)
1991 match = 1;
1992 else {
1993 if (comp->dict)
1994 match = (comp->steps[0].ns == ns);
1995 else
1996 match = xmlStrEqual(comp->steps[0].ns, ns);
1997 }
1998 } else {
1999 if (comp->dict)
2000 match = ((comp->steps[0].name == name) &&
2001 (comp->steps[0].ns == ns));
2002 else
2003 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
2004 (xmlStrEqual(comp->steps[0].ns, ns)));
2005 }
2006 if (match) {
2007 final = comp->steps[0].flags & XML_STREAM_STEP_FINAL;
2008 if (final)
2009 ret = 1;
2010 else
2011 xmlStreamCtxtAddState(stream, 1, stream->level);
2012 }
2013 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2014 ((! match) || final)) {
2015 /*
2016 * Mark this expression as blocked for any evaluation at
2017 * deeper levels.
2018 */
2019 stream->blockLevel = stream->level;
2020 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002021
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002022stream_next:
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002023 stream = stream->next;
2024 } /* while stream != NULL */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002025
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002026 if (err > 0)
2027 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002028#ifdef DEBUG_STREAMING
2029 xmlDebugStreamCtxt(orig, ret);
2030#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002031 return(ret);
2032}
2033
2034/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002035 * xmlStreamPush:
2036 * @stream: the stream context
2037 * @name: the current name
2038 * @ns: the namespace name
2039 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002040 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2041 * indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002042 * to come from the dictionary.
2043 * Both @name and @ns being NULL means the / i.e. the root of the document.
2044 * This can also act as a reset.
2045 *
2046 * Returns: -1 in case of error, 1 if the current state in the stream is a
2047 * match and 0 otherwise.
2048 */
2049int
2050xmlStreamPush(xmlStreamCtxtPtr stream,
2051 const xmlChar *name, const xmlChar *ns) {
2052 return (xmlStreamPushInternal(stream, name, ns, XML_ELEMENT_NODE));
2053}
2054
2055/**
2056* xmlStreamPushAttr:
2057* @stream: the stream context
2058* @name: the current name
2059* @ns: the namespace name
2060*
William M. Brackfbb619f2005-06-06 13:49:18 +00002061* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2062* indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002063* to come from the dictionary.
2064* Both @name and @ns being NULL means the / i.e. the root of the document.
2065* This can also act as a reset.
2066*
2067* Returns: -1 in case of error, 1 if the current state in the stream is a
2068* match and 0 otherwise.
2069*/
2070int
2071xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2072 const xmlChar *name, const xmlChar *ns) {
2073 return (xmlStreamPushInternal(stream, name, ns, XML_ATTRIBUTE_NODE));
2074}
2075
2076/**
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002077 * xmlStreamPop:
2078 * @stream: the stream context
2079 *
2080 * push one level from the stream.
2081 *
2082 * Returns: -1 in case of error, 0 otherwise.
2083 */
2084int
2085xmlStreamPop(xmlStreamCtxtPtr stream) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002086 int i, lev;
Kasimier T. Buchcik65c2f1d2005-10-17 12:39:58 +00002087
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002088 if (stream == NULL)
2089 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002090 while (stream != NULL) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002091 /*
2092 * Reset block-level.
2093 */
2094 if (stream->blockLevel == stream->level)
2095 stream->blockLevel = -1;
2096
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002097 stream->level--;
2098 if (stream->level < 0)
Kasimier T. Buchcik65c2f1d2005-10-17 12:39:58 +00002099 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002100 /*
2101 * Check evolution of existing states
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002102 */
2103 for (i = stream->nbState -1; i >= 0; i--) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002104 /* discard obsoleted states */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002105 lev = stream->states[(2 * i) + 1];
2106 if (lev > stream->level)
2107 stream->nbState--;
2108 if (lev <= stream->level)
2109 break;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002110 }
2111 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00002112 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002113 return(0);
2114}
2115
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002116/************************************************************************
2117 * *
2118 * The public interfaces *
2119 * *
2120 ************************************************************************/
2121
2122/**
2123 * xmlPatterncompile:
2124 * @pattern: the pattern to compile
William M. Brackfbb619f2005-06-06 13:49:18 +00002125 * @dict: an optional dictionary for interned strings
Daniel Veillarded6c5492005-07-23 15:00:22 +00002126 * @flags: compilation flags, see xmlPatternFlags
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002127 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002128 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002129 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002130 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002131 * Returns the compiled form of the pattern or NULL in case of error
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002132 */
2133xmlPatternPtr
Daniel Veillarded6c5492005-07-23 15:00:22 +00002134xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002135 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002136 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002137 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002138 const xmlChar *or, *start;
2139 xmlChar *tmp = NULL;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002140 int type = 0;
2141 int streamable = 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002142
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002143 if (pattern == NULL)
2144 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002145
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002146 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002147 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002148 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002149 tmp = NULL;
2150 while ((*or != 0) && (*or != '|')) or++;
2151 if (*or == 0)
2152 ctxt = xmlNewPatParserContext(start, dict, namespaces);
2153 else {
2154 tmp = xmlStrndup(start, or - start);
2155 if (tmp != NULL) {
2156 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2157 }
2158 or++;
2159 }
2160 if (ctxt == NULL) goto error;
2161 cur = xmlNewPattern();
2162 if (cur == NULL) goto error;
2163 if (ret == NULL)
2164 ret = cur;
2165 else {
2166 cur->next = ret->next;
2167 ret->next = cur;
2168 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002169 cur->flags = flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002170 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002171
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002172 if (XML_STREAM_XS_IDC(cur))
2173 xmlCompileIDCXPathPath(ctxt);
2174 else
2175 xmlCompilePathPattern(ctxt);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002176 if (ctxt->error != 0)
2177 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002178 xmlFreePatParserContext(ctxt);
William M. Brackfbb619f2005-06-06 13:49:18 +00002179 ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002180
2181
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002182 if (streamable) {
2183 if (type == 0) {
2184 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2185 } else if (type == PAT_FROM_ROOT) {
2186 if (cur->flags & PAT_FROM_CUR)
2187 streamable = 0;
2188 } else if (type == PAT_FROM_CUR) {
2189 if (cur->flags & PAT_FROM_ROOT)
2190 streamable = 0;
2191 }
2192 }
2193 if (streamable)
2194 xmlStreamCompile(cur);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002195 if (xmlReversePattern(cur) < 0)
2196 goto error;
William M. Brackea152c02005-06-09 18:12:28 +00002197 if (tmp != NULL) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002198 xmlFree(tmp);
William M. Brackea152c02005-06-09 18:12:28 +00002199 tmp = NULL;
2200 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002201 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002202 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002203 if (streamable == 0) {
2204 cur = ret;
2205 while (cur != NULL) {
2206 if (cur->stream != NULL) {
2207 xmlFreeStreamComp(cur->stream);
2208 cur->stream = NULL;
2209 }
2210 cur = cur->next;
2211 }
2212 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002213
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002214 return(ret);
2215error:
2216 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2217 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002218 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002219 return(NULL);
2220}
2221
2222/**
2223 * xmlPatternMatch:
2224 * @comp: the precompiled pattern
2225 * @node: a node
2226 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002227 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002228 *
2229 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2230 */
2231int
2232xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2233{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002234 int ret = 0;
2235
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002236 if ((comp == NULL) || (node == NULL))
2237 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002238
2239 while (comp != NULL) {
2240 ret = xmlPatMatch(comp, node);
2241 if (ret != 0)
2242 return(ret);
2243 comp = comp->next;
2244 }
2245 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002246}
2247
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002248/**
2249 * xmlPatternGetStreamCtxt:
2250 * @comp: the precompiled pattern
2251 *
2252 * Get a streaming context for that pattern
2253 * Use xmlFreeStreamCtxt to free the context.
2254 *
2255 * Returns a pointer to the context or NULL in case of failure
2256 */
2257xmlStreamCtxtPtr
2258xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2259{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002260 xmlStreamCtxtPtr ret = NULL, cur;
2261
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002262 if ((comp == NULL) || (comp->stream == NULL))
2263 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002264
2265 while (comp != NULL) {
2266 if (comp->stream == NULL)
2267 goto failed;
2268 cur = xmlNewStreamCtxt(comp->stream);
2269 if (cur == NULL)
2270 goto failed;
2271 if (ret == NULL)
2272 ret = cur;
2273 else {
2274 cur->next = ret->next;
2275 ret->next = cur;
2276 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002277 cur->flags = comp->flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002278 comp = comp->next;
2279 }
2280 return(ret);
2281failed:
2282 xmlFreeStreamCtxt(ret);
2283 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002284}
2285
Daniel Veillard56de87e2005-02-16 00:22:29 +00002286/**
2287 * xmlPatternStreamable:
2288 * @comp: the precompiled pattern
2289 *
2290 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2291 * should work.
2292 *
2293 * Returns 1 if streamable, 0 if not and -1 in case of error.
2294 */
2295int
2296xmlPatternStreamable(xmlPatternPtr comp) {
2297 if (comp == NULL)
2298 return(-1);
2299 while (comp != NULL) {
2300 if (comp->stream == NULL)
2301 return(0);
2302 comp = comp->next;
2303 }
2304 return(1);
2305}
2306
2307/**
2308 * xmlPatternMaxDepth:
2309 * @comp: the precompiled pattern
2310 *
2311 * Check the maximum depth reachable by a pattern
2312 *
2313 * Returns -2 if no limit (using //), otherwise the depth,
2314 * and -1 in case of error
2315 */
2316int
2317xmlPatternMaxDepth(xmlPatternPtr comp) {
2318 int ret = 0, i;
2319 if (comp == NULL)
2320 return(-1);
2321 while (comp != NULL) {
2322 if (comp->stream == NULL)
2323 return(-1);
2324 for (i = 0;i < comp->stream->nbStep;i++)
2325 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2326 return(-2);
2327 if (comp->stream->nbStep > ret)
2328 ret = comp->stream->nbStep;
2329 comp = comp->next;
2330 }
2331 return(ret);
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002332}
Daniel Veillard56de87e2005-02-16 00:22:29 +00002333
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002334/**
2335 * xmlPatternMinDepth:
2336 * @comp: the precompiled pattern
2337 *
2338 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2339 * part of the set.
2340 *
2341 * Returns -1 in case of error otherwise the depth,
2342 *
2343 */
2344int
2345xmlPatternMinDepth(xmlPatternPtr comp) {
2346 int ret = 12345678;
2347 if (comp == NULL)
2348 return(-1);
2349 while (comp != NULL) {
2350 if (comp->stream == NULL)
2351 return(-1);
2352 if (comp->stream->nbStep < ret)
2353 ret = comp->stream->nbStep;
2354 if (ret == 0)
2355 return(0);
2356 comp = comp->next;
2357 }
2358 return(ret);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002359}
2360
2361/**
2362 * xmlPatternFromRoot:
2363 * @comp: the precompiled pattern
2364 *
2365 * Check if the pattern must be looked at from the root.
2366 *
2367 * Returns 1 if true, 0 if false and -1 in case of error
2368 */
2369int
2370xmlPatternFromRoot(xmlPatternPtr comp) {
2371 if (comp == NULL)
2372 return(-1);
2373 while (comp != NULL) {
2374 if (comp->stream == NULL)
2375 return(-1);
2376 if (comp->flags & PAT_FROM_ROOT)
2377 return(1);
2378 comp = comp->next;
2379 }
2380 return(0);
2381
2382}
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002383
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002384#define bottom_pattern
2385#include "elfgcchack.h"
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002386#endif /* LIBXML_PATTERN_ENABLED */