blob: 041039ef7c3a72f24c6ac570fe2f34893d6f3318 [file] [log] [blame]
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001/*
2 * pattern.c: Implemetation of selectors for nodes
3 *
4 * Reference:
5 * http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
6 * to some extent
7 * http://www.w3.org/TR/1999/REC-xml-19991116
8 *
9 * See Copyright for the status of this software.
10 *
11 * daniel@veillard.com
12 */
13
Daniel Veillardf9d16912005-01-30 22:36:30 +000014/*
15 * TODO:
16 * - compilation flags to check for specific syntaxes
17 * using flags of xmlPatterncompile()
18 * - making clear how pattern starting with / or . need to be handled,
19 * currently push(NULL, NULL) means a reset of the streaming context
20 * and indicating we are on / (the document node), probably need
21 * something similar for .
Daniel Veillardd4301ab2005-02-03 22:24:10 +000022 * - get rid of the "compile" starting with lowercase
23 * - get rid of the Strdup/Strndup in case of dictionary
Daniel Veillardf9d16912005-01-30 22:36:30 +000024 */
25
Daniel Veillardb3de70c2003-12-02 22:32:15 +000026#define IN_LIBXML
27#include "libxml.h"
28
29#include <string.h>
30#include <libxml/xmlmemory.h>
31#include <libxml/tree.h>
32#include <libxml/hash.h>
33#include <libxml/dict.h>
34#include <libxml/xmlerror.h>
35#include <libxml/parserInternals.h>
36#include <libxml/pattern.h>
37
Daniel Veillardd4301ab2005-02-03 22:24:10 +000038#ifdef LIBXML_PATTERN_ENABLED
Daniel Veillardb3de70c2003-12-02 22:32:15 +000039
Daniel Veillardd4301ab2005-02-03 22:24:10 +000040/* #define DEBUG_STREAMING */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000041#define SUPPORT_IDC
Daniel Veillard2fc6df92005-01-30 18:42:55 +000042
Daniel Veillardb3de70c2003-12-02 22:32:15 +000043#define ERROR(a, b, c, d)
44#define ERROR5(a, b, c, d, e)
45
Daniel Veillard2fc6df92005-01-30 18:42:55 +000046#define XML_STREAM_STEP_DESC 1
47#define XML_STREAM_STEP_FINAL 2
48#define XML_STREAM_STEP_ROOT 4
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +000049#define XML_STREAM_STEP_ATTR 8
Daniel Veillard2fc6df92005-01-30 18:42:55 +000050
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000051#define XML_PATTERN_NOTPATTERN 1
52
Daniel Veillard2fc6df92005-01-30 18:42:55 +000053typedef struct _xmlStreamStep xmlStreamStep;
54typedef xmlStreamStep *xmlStreamStepPtr;
55struct _xmlStreamStep {
56 int flags; /* properties of that step */
57 const xmlChar *name; /* first string value if NULL accept all */
58 const xmlChar *ns; /* second string value */
59};
60
61typedef struct _xmlStreamComp xmlStreamComp;
62typedef xmlStreamComp *xmlStreamCompPtr;
63struct _xmlStreamComp {
64 xmlDict *dict; /* the dictionnary if any */
65 int nbStep; /* number of steps in the automata */
66 int maxStep; /* allocated number of steps */
67 xmlStreamStepPtr steps; /* the array of steps */
68};
69
70struct _xmlStreamCtxt {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +000071 struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000072 xmlStreamCompPtr comp; /* the compiled stream */
73 int nbState; /* number of state in the automata */
74 int maxState; /* allocated number of state */
75 int level; /* how deep are we ? */
76 int *states; /* the array of step indexes */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000077 int flags; /* validation options */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000078};
79
80static void xmlFreeStreamComp(xmlStreamCompPtr comp);
81
Daniel Veillardb3de70c2003-12-02 22:32:15 +000082/*
83 * Types are private:
84 */
85
86typedef enum {
87 XML_OP_END=0,
88 XML_OP_ROOT,
89 XML_OP_ELEM,
90 XML_OP_CHILD,
91 XML_OP_ATTR,
92 XML_OP_PARENT,
93 XML_OP_ANCESTOR,
94 XML_OP_NS,
95 XML_OP_ALL
96} xmlPatOp;
97
98
Daniel Veillardd4301ab2005-02-03 22:24:10 +000099typedef struct _xmlStepState xmlStepState;
100typedef xmlStepState *xmlStepStatePtr;
101struct _xmlStepState {
102 int step;
103 xmlNodePtr node;
104};
105
106typedef struct _xmlStepStates xmlStepStates;
107typedef xmlStepStates *xmlStepStatesPtr;
108struct _xmlStepStates {
109 int nbstates;
110 int maxstates;
111 xmlStepStatePtr states;
112};
113
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000114typedef struct _xmlStepOp xmlStepOp;
115typedef xmlStepOp *xmlStepOpPtr;
116struct _xmlStepOp {
117 xmlPatOp op;
118 const xmlChar *value;
119 const xmlChar *value2;
120};
121
Daniel Veillard56de87e2005-02-16 00:22:29 +0000122#define PAT_FROM_ROOT 1
123#define PAT_FROM_CUR 2
124
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000125struct _xmlPattern {
126 void *data; /* the associated template */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000127 xmlDictPtr dict; /* the optional dictionnary */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000128 struct _xmlPattern *next; /* next pattern if | is used */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000129 const xmlChar *pattern; /* the pattern */
130
Daniel Veillard56de87e2005-02-16 00:22:29 +0000131 int flags; /* flags */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000132 int nbStep;
133 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000134 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000135 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000136};
137
138typedef struct _xmlPatParserContext xmlPatParserContext;
139typedef xmlPatParserContext *xmlPatParserContextPtr;
140struct _xmlPatParserContext {
141 const xmlChar *cur; /* the current char being parsed */
142 const xmlChar *base; /* the full expression */
143 int error; /* error code */
144 xmlDictPtr dict; /* the dictionnary if any */
145 xmlPatternPtr comp; /* the result */
146 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000147 const xmlChar **namespaces; /* the namespaces definitions */
148 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000149};
150
151/************************************************************************
152 * *
153 * Type functions *
154 * *
155 ************************************************************************/
156
157/**
158 * xmlNewPattern:
159 *
160 * Create a new XSLT Pattern
161 *
162 * Returns the newly allocated xmlPatternPtr or NULL in case of error
163 */
164static xmlPatternPtr
165xmlNewPattern(void) {
166 xmlPatternPtr cur;
167
168 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
169 if (cur == NULL) {
170 ERROR(NULL, NULL, NULL,
171 "xmlNewPattern : malloc failed\n");
172 return(NULL);
173 }
174 memset(cur, 0, sizeof(xmlPattern));
175 cur->maxStep = 10;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000176 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
177 if (cur->steps == NULL) {
178 xmlFree(cur);
179 ERROR(NULL, NULL, NULL,
180 "xmlNewPattern : malloc failed\n");
181 return(NULL);
182 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000183 return(cur);
184}
185
186/**
187 * xmlFreePattern:
188 * @comp: an XSLT comp
189 *
190 * Free up the memory allocated by @comp
191 */
192void
193xmlFreePattern(xmlPatternPtr comp) {
194 xmlStepOpPtr op;
195 int i;
196
197 if (comp == NULL)
198 return;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000199 if (comp->next != NULL)
200 xmlFreePattern(comp->next);
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000201 if (comp->stream != NULL)
202 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000203 if (comp->pattern != NULL)
204 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000205 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000206 if (comp->dict == NULL) {
207 for (i = 0;i < comp->nbStep;i++) {
208 op = &comp->steps[i];
209 if (op->value != NULL)
210 xmlFree((xmlChar *) op->value);
211 if (op->value2 != NULL)
212 xmlFree((xmlChar *) op->value2);
213 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000214 }
215 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000216 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000217 if (comp->dict != NULL)
218 xmlDictFree(comp->dict);
219
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000220 memset(comp, -1, sizeof(xmlPattern));
221 xmlFree(comp);
222}
223
224/**
225 * xmlFreePatternList:
226 * @comp: an XSLT comp list
227 *
228 * Free up the memory allocated by all the elements of @comp
229 */
230void
231xmlFreePatternList(xmlPatternPtr comp) {
232 xmlPatternPtr cur;
233
234 while (comp != NULL) {
235 cur = comp;
236 comp = comp->next;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +0000237 cur->next = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000238 xmlFreePattern(cur);
239 }
240}
241
242/**
243 * xmlNewPatParserContext:
244 * @pattern: the pattern context
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000245 * @dict: the inherited dictionnary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000246 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
247 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000248 *
249 * Create a new XML pattern parser context
250 *
251 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
252 */
253static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000254xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
255 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000256 xmlPatParserContextPtr cur;
257
258 if (pattern == NULL)
259 return(NULL);
260
261 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
262 if (cur == NULL) {
263 ERROR(NULL, NULL, NULL,
264 "xmlNewPatParserContext : malloc failed\n");
265 return(NULL);
266 }
267 memset(cur, 0, sizeof(xmlPatParserContext));
268 cur->dict = dict;
269 cur->cur = pattern;
270 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000271 if (namespaces != NULL) {
272 int i;
273 for (i = 0;namespaces[2 * i] != NULL;i++);
274 cur->nb_namespaces = i;
275 } else {
276 cur->nb_namespaces = 0;
277 }
278 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000279 return(cur);
280}
281
282/**
283 * xmlFreePatParserContext:
284 * @ctxt: an XSLT parser context
285 *
286 * Free up the memory allocated by @ctxt
287 */
288static void
289xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
290 if (ctxt == NULL)
291 return;
292 memset(ctxt, -1, sizeof(xmlPatParserContext));
293 xmlFree(ctxt);
294}
295
296/**
297 * xmlPatternAdd:
298 * @comp: the compiled match expression
299 * @op: an op
300 * @value: the first value
301 * @value2: the second value
302 *
303 * Add an step to an XSLT Compiled Match
304 *
305 * Returns -1 in case of failure, 0 otherwise.
306 */
307static int
308xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
309 xmlPatternPtr comp,
310 xmlPatOp op, xmlChar * value, xmlChar * value2)
311{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000312 if (comp->nbStep >= comp->maxStep) {
313 xmlStepOpPtr temp;
314 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
315 sizeof(xmlStepOp));
316 if (temp == NULL) {
317 ERROR(ctxt, NULL, NULL,
318 "xmlPatternAdd: realloc failed\n");
319 return (-1);
320 }
321 comp->steps = temp;
322 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000323 }
324 comp->steps[comp->nbStep].op = op;
325 comp->steps[comp->nbStep].value = value;
326 comp->steps[comp->nbStep].value2 = value2;
327 comp->nbStep++;
328 return (0);
329}
330
331#if 0
332/**
333 * xsltSwapTopPattern:
334 * @comp: the compiled match expression
335 *
336 * reverse the two top steps.
337 */
338static void
339xsltSwapTopPattern(xmlPatternPtr comp) {
340 int i;
341 int j = comp->nbStep - 1;
342
343 if (j > 0) {
344 register const xmlChar *tmp;
345 register xmlPatOp op;
346 i = j - 1;
347 tmp = comp->steps[i].value;
348 comp->steps[i].value = comp->steps[j].value;
349 comp->steps[j].value = tmp;
350 tmp = comp->steps[i].value2;
351 comp->steps[i].value2 = comp->steps[j].value2;
352 comp->steps[j].value2 = tmp;
353 op = comp->steps[i].op;
354 comp->steps[i].op = comp->steps[j].op;
355 comp->steps[j].op = op;
356 }
357}
358#endif
359
360/**
361 * xmlReversePattern:
362 * @comp: the compiled match expression
363 *
364 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000365 *
366 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000367 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000368static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000369xmlReversePattern(xmlPatternPtr comp) {
Daniel Veillard56de87e2005-02-16 00:22:29 +0000370 int i, j;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000371
Daniel Veillard56de87e2005-02-16 00:22:29 +0000372 /*
373 * remove the leading // for //a or .//a
374 */
375 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
376 for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
377 comp->steps[i].value = comp->steps[j].value;
378 comp->steps[i].value2 = comp->steps[j].value2;
379 comp->steps[i].op = comp->steps[j].op;
380 }
381 comp->nbStep--;
382 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000383 if (comp->nbStep >= comp->maxStep) {
384 xmlStepOpPtr temp;
385 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
386 sizeof(xmlStepOp));
387 if (temp == NULL) {
388 ERROR(ctxt, NULL, NULL,
389 "xmlReversePattern: realloc failed\n");
390 return (-1);
391 }
392 comp->steps = temp;
393 comp->maxStep *= 2;
394 }
Daniel Veillard56de87e2005-02-16 00:22:29 +0000395 i = 0;
396 j = comp->nbStep - 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000397 while (j > i) {
398 register const xmlChar *tmp;
399 register xmlPatOp op;
400 tmp = comp->steps[i].value;
401 comp->steps[i].value = comp->steps[j].value;
402 comp->steps[j].value = tmp;
403 tmp = comp->steps[i].value2;
404 comp->steps[i].value2 = comp->steps[j].value2;
405 comp->steps[j].value2 = tmp;
406 op = comp->steps[i].op;
407 comp->steps[i].op = comp->steps[j].op;
408 comp->steps[j].op = op;
409 j--;
410 i++;
411 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000412 comp->steps[comp->nbStep].value = NULL;
413 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000414 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000415 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000416}
417
418/************************************************************************
419 * *
420 * The interpreter for the precompiled patterns *
421 * *
422 ************************************************************************/
423
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000424static int
425xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
426 if ((states->states == NULL) || (states->maxstates <= 0)) {
427 states->maxstates = 4;
428 states->nbstates = 0;
429 states->states = xmlMalloc(4 * sizeof(xmlStepState));
430 }
431 else if (states->maxstates <= states->nbstates) {
432 xmlStepState *tmp;
433
434 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
435 2 * states->maxstates * sizeof(xmlStepState));
436 if (tmp == NULL)
437 return(-1);
438 states->states = tmp;
439 states->maxstates *= 2;
440 }
441 states->states[states->nbstates].step = step;
442 states->states[states->nbstates++].node = node;
443#if 0
444 fprintf(stderr, "Push: %d, %s\n", step, node->name);
445#endif
446 return(0);
447}
448
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000449/**
450 * xmlPatMatch:
451 * @comp: the precompiled pattern
452 * @node: a node
453 *
454 * Test wether the node matches the pattern
455 *
456 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
457 */
458static int
459xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
460 int i;
461 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000462 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000463
464 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000465 i = 0;
466restart:
467 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000468 step = &comp->steps[i];
469 switch (step->op) {
470 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000471 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000472 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000473 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000474 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000475 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000476 if ((node->type == XML_DOCUMENT_NODE) ||
477#ifdef LIBXML_DOCB_ENABLED
478 (node->type == XML_DOCB_DOCUMENT_NODE) ||
479#endif
480 (node->type == XML_HTML_DOCUMENT_NODE))
481 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000482 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000483 case XML_OP_ELEM:
484 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000485 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000486 if (step->value == NULL)
487 continue;
488 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000489 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000490 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000491 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000492
493 /* Namespace test */
494 if (node->ns == NULL) {
495 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000496 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000497 } else if (node->ns->href != NULL) {
498 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000499 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000500 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000501 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000502 }
503 continue;
504 case XML_OP_CHILD: {
505 xmlNodePtr lst;
506
507 if ((node->type != XML_ELEMENT_NODE) &&
508 (node->type != XML_DOCUMENT_NODE) &&
509#ifdef LIBXML_DOCB_ENABLED
510 (node->type != XML_DOCB_DOCUMENT_NODE) &&
511#endif
512 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000513 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000514
515 lst = node->children;
516
517 if (step->value != NULL) {
518 while (lst != NULL) {
519 if ((lst->type == XML_ELEMENT_NODE) &&
520 (step->value[0] == lst->name[0]) &&
521 (xmlStrEqual(step->value, lst->name)))
522 break;
523 lst = lst->next;
524 }
525 if (lst != NULL)
526 continue;
527 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000528 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000529 }
530 case XML_OP_ATTR:
531 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000532 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000533 if (step->value != NULL) {
534 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000535 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000536 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000537 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000538 }
539 /* Namespace test */
540 if (node->ns == NULL) {
541 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000542 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000543 } else if (step->value2 != NULL) {
544 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000545 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000546 }
547 continue;
548 case XML_OP_PARENT:
549 if ((node->type == XML_DOCUMENT_NODE) ||
550 (node->type == XML_HTML_DOCUMENT_NODE) ||
551#ifdef LIBXML_DOCB_ENABLED
552 (node->type == XML_DOCB_DOCUMENT_NODE) ||
553#endif
554 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000555 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000556 node = node->parent;
557 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000558 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000559 if (step->value == NULL)
560 continue;
561 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000562 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000563 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000564 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000565 /* Namespace test */
566 if (node->ns == NULL) {
567 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000568 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000569 } else if (node->ns->href != NULL) {
570 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000571 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000572 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000573 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000574 }
575 continue;
576 case XML_OP_ANCESTOR:
577 /* TODO: implement coalescing of ANCESTOR/NODE ops */
578 if (step->value == NULL) {
579 i++;
580 step = &comp->steps[i];
581 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000582 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000583 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000584 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000585 if (step->value == NULL)
586 return(-1);
587 }
588 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000589 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000590 if ((node->type == XML_DOCUMENT_NODE) ||
591 (node->type == XML_HTML_DOCUMENT_NODE) ||
592#ifdef LIBXML_DOCB_ENABLED
593 (node->type == XML_DOCB_DOCUMENT_NODE) ||
594#endif
595 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000596 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000597 node = node->parent;
598 while (node != NULL) {
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_ELEMENT_NODE) &&
602 (step->value[0] == node->name[0]) &&
603 (xmlStrEqual(step->value, node->name))) {
604 /* Namespace test */
605 if (node->ns == NULL) {
606 if (step->value2 == NULL)
607 break;
608 } else if (node->ns->href != NULL) {
609 if ((step->value2 != NULL) &&
610 (xmlStrEqual(step->value2, node->ns->href)))
611 break;
612 }
613 }
614 node = node->parent;
615 }
616 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000617 goto rollback;
618 /*
619 * prepare a potential rollback from here
620 * for ancestors of that node.
621 */
622 if (step->op == XML_OP_ANCESTOR)
623 xmlPatPushState(&states, i, node);
624 else
625 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000626 continue;
627 case XML_OP_NS:
628 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000629 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000630 if (node->ns == NULL) {
631 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000632 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000633 } else if (node->ns->href != NULL) {
634 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000635 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000636 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000637 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000638 }
639 break;
640 case XML_OP_ALL:
641 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000642 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000643 break;
644 }
645 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000646found:
647 if (states.states != NULL) {
648 /* Free the rollback states */
649 xmlFree(states.states);
650 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000651 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000652rollback:
653 /* got an error try to rollback */
654 if (states.states == NULL)
655 return(0);
656 if (states.nbstates <= 0) {
657 xmlFree(states.states);
658 return(0);
659 }
660 states.nbstates--;
661 i = states.states[states.nbstates].step;
662 node = states.states[states.nbstates].node;
663#if 0
664 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
665#endif
666 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000667}
668
669/************************************************************************
670 * *
671 * Dedicated parser for templates *
672 * *
673 ************************************************************************/
674
675#define TODO \
676 xmlGenericError(xmlGenericErrorContext, \
677 "Unimplemented block at %s:%d\n", \
678 __FILE__, __LINE__);
679#define CUR (*ctxt->cur)
680#define SKIP(val) ctxt->cur += (val)
681#define NXT(val) ctxt->cur[(val)]
682#define CUR_PTR ctxt->cur
683
684#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000685 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000686
687#define CURRENT (*ctxt->cur)
688#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
689
690
691#define PUSH(op, val, val2) \
692 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
693
694#define XSLT_ERROR(X) \
695 { xsltError(ctxt, __FILE__, __LINE__, X); \
696 ctxt->error = (X); return; }
697
698#define XSLT_ERROR0(X) \
699 { xsltError(ctxt, __FILE__, __LINE__, X); \
700 ctxt->error = (X); return(0); }
701
702#if 0
703/**
704 * xmlPatScanLiteral:
705 * @ctxt: the XPath Parser context
706 *
707 * Parse an XPath Litteral:
708 *
709 * [29] Literal ::= '"' [^"]* '"'
710 * | "'" [^']* "'"
711 *
712 * Returns the Literal parsed or NULL
713 */
714
715static xmlChar *
716xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
717 const xmlChar *q, *cur;
718 xmlChar *ret = NULL;
719 int val, len;
720
721 SKIP_BLANKS;
722 if (CUR == '"') {
723 NEXT;
724 cur = q = CUR_PTR;
725 val = xmlStringCurrentChar(NULL, cur, &len);
726 while ((IS_CHAR(val)) && (val != '"')) {
727 cur += len;
728 val = xmlStringCurrentChar(NULL, cur, &len);
729 }
730 if (!IS_CHAR(val)) {
731 ctxt->error = 1;
732 return(NULL);
733 } else {
734 ret = xmlStrndup(q, cur - q);
735 }
736 cur += len;
737 CUR_PTR = cur;
738 } else 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 {
755 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
756 ctxt->error = 1;
757 return(NULL);
758 }
759 return(ret);
760}
761#endif
762
763/**
764 * xmlPatScanName:
765 * @ctxt: the XPath Parser context
766 *
767 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
768 * CombiningChar | Extender
769 *
770 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
771 *
772 * [6] Names ::= Name (S Name)*
773 *
774 * Returns the Name parsed or NULL
775 */
776
777static xmlChar *
778xmlPatScanName(xmlPatParserContextPtr ctxt) {
779 const xmlChar *q, *cur;
780 xmlChar *ret = NULL;
781 int val, len;
782
783 SKIP_BLANKS;
784
785 cur = q = CUR_PTR;
786 val = xmlStringCurrentChar(NULL, cur, &len);
787 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
788 return(NULL);
789
790 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
791 (val == '.') || (val == '-') ||
792 (val == '_') ||
793 (IS_COMBINING(val)) ||
794 (IS_EXTENDER(val))) {
795 cur += len;
796 val = xmlStringCurrentChar(NULL, cur, &len);
797 }
798 ret = xmlStrndup(q, cur - q);
799 CUR_PTR = cur;
800 return(ret);
801}
802
803/**
804 * xmlPatScanNCName:
805 * @ctxt: the XPath Parser context
806 *
807 * Parses a non qualified name
808 *
809 * Returns the Name parsed or NULL
810 */
811
812static xmlChar *
813xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
814 const xmlChar *q, *cur;
815 xmlChar *ret = NULL;
816 int val, len;
817
818 SKIP_BLANKS;
819
820 cur = q = CUR_PTR;
821 val = xmlStringCurrentChar(NULL, cur, &len);
822 if (!IS_LETTER(val) && (val != '_'))
823 return(NULL);
824
825 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
826 (val == '.') || (val == '-') ||
827 (val == '_') ||
828 (IS_COMBINING(val)) ||
829 (IS_EXTENDER(val))) {
830 cur += len;
831 val = xmlStringCurrentChar(NULL, cur, &len);
832 }
833 ret = xmlStrndup(q, cur - q);
834 CUR_PTR = cur;
835 return(ret);
836}
837
838#if 0
839/**
840 * xmlPatScanQName:
841 * @ctxt: the XPath Parser context
842 * @prefix: the place to store the prefix
843 *
844 * Parse a qualified name
845 *
846 * Returns the Name parsed or NULL
847 */
848
849static xmlChar *
850xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
851 xmlChar *ret = NULL;
852
853 *prefix = NULL;
854 ret = xmlPatScanNCName(ctxt);
855 if (CUR == ':') {
856 *prefix = ret;
857 NEXT;
858 ret = xmlPatScanNCName(ctxt);
859 }
860 return(ret);
861}
862#endif
863
864/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000865 * xmlCompileAttributeTest:
866 * @ctxt: the compilation context
867 *
868 * Compile an attribute test.
869 */
870static void
871xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
872 xmlChar *token = NULL;
873 xmlChar *name = NULL;
874 xmlChar *URL = NULL;
875
876 name = xmlPatScanNCName(ctxt);
877 if (name == NULL) {
878 if (CUR == '*') {
879 PUSH(XML_OP_ATTR, NULL, NULL);
880 } else {
881 ERROR(NULL, NULL, NULL,
882 "xmlCompileAttributeTest : Name expected\n");
883 ctxt->error = 1;
884 }
885 return;
886 }
887 if (CUR == ':') {
888 int i;
889 xmlChar *prefix = name;
890
891 NEXT;
892 /*
893 * This is a namespace match
894 */
895 token = xmlPatScanName(ctxt);
896 for (i = 0;i < ctxt->nb_namespaces;i++) {
897 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
898 URL = xmlStrdup(ctxt->namespaces[2 * i]);
899 break;
900 }
901 }
902 if (i >= ctxt->nb_namespaces) {
903 ERROR5(NULL, NULL, NULL,
904 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
905 prefix);
906 ctxt->error = 1;
907 goto error;
908 }
909
910 xmlFree(prefix);
911 if (token == NULL) {
912 if (CUR == '*') {
913 NEXT;
914 PUSH(XML_OP_ATTR, NULL, URL);
915 } else {
916 ERROR(NULL, NULL, NULL,
917 "xmlCompileAttributeTest : Name expected\n");
918 ctxt->error = 1;
919 goto error;
920 }
921 } else {
922 PUSH(XML_OP_ATTR, token, URL);
923 }
924 } else {
925 PUSH(XML_OP_ATTR, name, NULL);
926 }
927 return;
928error:
929 if (URL != NULL)
930 xmlFree(URL);
931 if (token != NULL)
932 xmlFree(token);
933}
934
935
936/**
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000937 * xmlCompileStepPattern:
938 * @ctxt: the compilation context
939 *
940 * Compile the Step Pattern and generates a precompiled
941 * form suitable for fast matching.
942 *
943 * [3] Step ::= '.' | NameTest
944 * [4] NameTest ::= QName | '*' | NCName ':' '*'
945 */
946
947static void
948xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
949 xmlChar *token = NULL;
950 xmlChar *name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000951 xmlChar *URL = NULL;
952
953 SKIP_BLANKS;
954 if (CUR == '.') {
955 NEXT;
956 PUSH(XML_OP_ELEM, NULL, NULL);
957 return;
958 }
959 name = xmlPatScanNCName(ctxt);
960 if (name == NULL) {
961 if (CUR == '*') {
962 NEXT;
963 PUSH(XML_OP_ALL, NULL, NULL);
964 return;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000965 } else if (CUR == '@') {
966 NEXT;
967 xmlCompileAttributeTest(ctxt);
968 if (ctxt->error != 0)
969 goto error;
970 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000971 } else {
972 ERROR(NULL, NULL, NULL,
973 "xmlCompileStepPattern : Name expected\n");
974 ctxt->error = 1;
975 return;
976 }
977 }
978 SKIP_BLANKS;
979 if (CUR == ':') {
980 NEXT;
981 if (CUR != ':') {
982 xmlChar *prefix = name;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000983 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000984
985 /*
986 * This is a namespace match
987 */
988 token = xmlPatScanName(ctxt);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000989 for (i = 0;i < ctxt->nb_namespaces;i++) {
990 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Daniel Veillard0996a162005-02-05 14:00:10 +0000991 URL = xmlStrdup(ctxt->namespaces[2 * i]);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000992 break;
993 }
994 }
995 if (i >= ctxt->nb_namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000996 ERROR5(NULL, NULL, NULL,
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000997 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
998 prefix);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000999 ctxt->error = 1;
1000 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001001 }
1002 xmlFree(prefix);
1003 if (token == NULL) {
1004 if (CUR == '*') {
1005 NEXT;
1006 PUSH(XML_OP_NS, URL, NULL);
1007 } else {
1008 ERROR(NULL, NULL, NULL,
1009 "xmlCompileStepPattern : Name expected\n");
1010 ctxt->error = 1;
1011 goto error;
1012 }
1013 } else {
1014 PUSH(XML_OP_ELEM, token, URL);
1015 }
1016 } else {
1017 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001018 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1019 xmlFree(name);
1020 name = xmlPatScanName(ctxt);
1021 if (name == NULL) {
1022 if (CUR == '*') {
1023 NEXT;
1024 PUSH(XML_OP_ALL, NULL, NULL);
1025 return;
1026 } else {
1027 ERROR(NULL, NULL, NULL,
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001028 "xmlCompileStepPattern : QName expected\n");
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001029 ctxt->error = 1;
1030 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001031 }
1032 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001033 if (CUR == ':') {
1034 xmlChar *prefix = name;
1035 int i;
1036
1037 NEXT;
1038 /*
1039 * This is a namespace match
1040 */
1041 token = xmlPatScanName(ctxt);
1042 for (i = 0;i < ctxt->nb_namespaces;i++) {
1043 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1044 URL = xmlStrdup(ctxt->namespaces[2 * i]);
1045 break;
1046 }
1047 }
1048 if (i >= ctxt->nb_namespaces) {
1049 ERROR5(NULL, NULL, NULL,
1050 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1051 prefix);
1052 ctxt->error = 1;
1053 goto error;
1054 }
1055 xmlFree(prefix);
1056 if (token == NULL) {
1057 if (CUR == '*') {
1058 NEXT;
1059 PUSH(XML_OP_NS, URL, NULL);
1060 } else {
1061 ERROR(NULL, NULL, NULL,
1062 "xmlCompileStepPattern : Name expected\n");
1063 ctxt->error = 1;
1064 goto error;
1065 }
1066 } else {
1067 PUSH(XML_OP_CHILD, token, URL);
1068 }
1069 } else
1070 PUSH(XML_OP_CHILD, name, NULL);
1071 return;
1072 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1073 xmlFree(name);
1074 name = NULL;
1075 xmlCompileAttributeTest(ctxt);
1076 if (ctxt->error != 0)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001077 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001078 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001079 } else {
1080 ERROR(NULL, NULL, NULL,
1081 "xmlCompileStepPattern : 'child' or 'attribute' expected\n");
1082 ctxt->error = 1;
1083 goto error;
1084 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001085 xmlFree(name);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001086 }
1087 } else if (CUR == '*') {
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001088 if (name != NULL) {
1089 ctxt->error = 1;
1090 goto error;
1091 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001092 NEXT;
1093 PUSH(XML_OP_ALL, token, NULL);
1094 } else {
1095 if (name == NULL) {
1096 ctxt->error = 1;
1097 goto error;
1098 }
1099 PUSH(XML_OP_ELEM, name, NULL);
1100 }
1101 return;
1102error:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001103 if (URL != NULL)
1104 xmlFree(URL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001105 if (token != NULL)
1106 xmlFree(token);
1107 if (name != NULL)
1108 xmlFree(name);
1109}
1110
1111/**
1112 * xmlCompilePathPattern:
1113 * @ctxt: the compilation context
1114 *
1115 * Compile the Path Pattern and generates a precompiled
1116 * form suitable for fast matching.
1117 *
1118 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1119 */
1120static void
1121xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1122 SKIP_BLANKS;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001123 if (CUR == '/') {
1124 ctxt->comp->flags |= PAT_FROM_ROOT;
1125 } else if (CUR == '.') {
1126 ctxt->comp->flags |= PAT_FROM_CUR;
1127 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001128 if ((CUR == '/') && (NXT(1) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001129 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001130 NEXT;
1131 NEXT;
1132 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001133 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001134 NEXT;
1135 NEXT;
1136 NEXT;
1137 }
1138 if (CUR == '@') {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001139 NEXT;
1140 xmlCompileAttributeTest(ctxt);
1141 SKIP_BLANKS;
1142 if ((CUR != 0) || (CUR == '|')) {
1143 xmlCompileStepPattern(ctxt);
1144 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001145 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001146 if (CUR == '/') {
1147 PUSH(XML_OP_ROOT, NULL, NULL);
1148 NEXT;
1149 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001150 xmlCompileStepPattern(ctxt);
1151 SKIP_BLANKS;
1152 while (CUR == '/') {
1153 if ((CUR == '/') && (NXT(1) == '/')) {
1154 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1155 NEXT;
1156 NEXT;
1157 SKIP_BLANKS;
1158 xmlCompileStepPattern(ctxt);
1159 } else {
1160 PUSH(XML_OP_PARENT, NULL, NULL);
1161 NEXT;
1162 SKIP_BLANKS;
1163 if ((CUR != 0) || (CUR == '|')) {
1164 xmlCompileStepPattern(ctxt);
1165 }
1166 }
1167 }
1168 }
Daniel Veillard56de87e2005-02-16 00:22:29 +00001169 if (CUR != 0) {
1170 ERROR5(NULL, NULL, NULL,
1171 "Failed to compile pattern %s\n", ctxt->base);
1172 ctxt->error = 1;
1173 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001174error:
1175 return;
1176}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001177
1178/************************************************************************
1179 * *
1180 * The streaming code *
1181 * *
1182 ************************************************************************/
1183
1184#ifdef DEBUG_STREAMING
1185static void
1186xmlDebugStreamComp(xmlStreamCompPtr stream) {
1187 int i;
1188
1189 if (stream == NULL) {
1190 printf("Stream: NULL\n");
1191 return;
1192 }
1193 printf("Stream: %d steps\n", stream->nbStep);
1194 for (i = 0;i < stream->nbStep;i++) {
1195 if (stream->steps[i].ns != NULL) {
1196 printf("{%s}", stream->steps[i].ns);
1197 }
1198 if (stream->steps[i].name == NULL) {
1199 printf("* ");
1200 } else {
1201 printf("%s ", stream->steps[i].name);
1202 }
1203 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1204 printf("root ");
1205 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1206 printf("// ");
1207 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1208 printf("final ");
1209 printf("\n");
1210 }
1211}
1212static void
1213xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1214 int i;
1215
1216 if (ctxt == NULL) {
1217 printf("Stream: NULL\n");
1218 return;
1219 }
1220 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1221 if (match)
1222 printf("matches\n");
1223 else
1224 printf("\n");
1225 for (i = 0;i < ctxt->nbState;i++) {
1226 if (ctxt->states[2 * i] < 0)
1227 printf(" %d: free\n", i);
1228 else {
1229 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1230 ctxt->states[(2 * i) + 1]);
1231 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1232 XML_STREAM_STEP_DESC)
1233 printf(" //\n");
1234 else
1235 printf("\n");
1236 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001237 }
1238}
1239#endif
1240/**
1241 * xmlNewStreamComp:
1242 * @size: the number of expected steps
1243 *
1244 * build a new compiled pattern for streaming
1245 *
1246 * Returns the new structure or NULL in case of error.
1247 */
1248static xmlStreamCompPtr
1249xmlNewStreamComp(int size) {
1250 xmlStreamCompPtr cur;
1251
1252 if (size < 4)
1253 size = 4;
1254
1255 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1256 if (cur == NULL) {
1257 ERROR(NULL, NULL, NULL,
1258 "xmlNewStreamComp: malloc failed\n");
1259 return(NULL);
1260 }
1261 memset(cur, 0, sizeof(xmlStreamComp));
1262 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1263 if (cur->steps == NULL) {
1264 xmlFree(cur);
1265 ERROR(NULL, NULL, NULL,
1266 "xmlNewStreamComp: malloc failed\n");
1267 return(NULL);
1268 }
1269 cur->nbStep = 0;
1270 cur->maxStep = size;
1271 return(cur);
1272}
1273
1274/**
1275 * xmlFreeStreamComp:
1276 * @comp: the compiled pattern for streaming
1277 *
1278 * Free the compiled pattern for streaming
1279 */
1280static void
1281xmlFreeStreamComp(xmlStreamCompPtr comp) {
1282 if (comp != NULL) {
1283 if (comp->steps != NULL)
1284 xmlFree(comp->steps);
1285 if (comp->dict != NULL)
1286 xmlDictFree(comp->dict);
1287 xmlFree(comp);
1288 }
1289}
1290
1291/**
1292 * xmlStreamCompAddStep:
1293 * @comp: the compiled pattern for streaming
1294 * @name: the first string, the name, or NULL for *
1295 * @ns: the second step, the namespace name
1296 * @flags: the flags for that step
1297 *
1298 * Add a new step to the compiled pattern
1299 *
1300 * Returns -1 in case of error or the step index if successful
1301 */
1302static int
1303xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1304 const xmlChar *ns, int flags) {
1305 xmlStreamStepPtr cur;
1306
1307 if (comp->nbStep >= comp->maxStep) {
1308 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1309 comp->maxStep * 2 * sizeof(xmlStreamStep));
1310 if (cur == NULL) {
1311 ERROR(NULL, NULL, NULL,
1312 "xmlNewStreamComp: malloc failed\n");
1313 return(-1);
1314 }
1315 comp->steps = cur;
1316 comp->maxStep *= 2;
1317 }
1318 cur = &comp->steps[comp->nbStep++];
1319 cur->flags = flags;
1320 cur->name = name;
1321 cur->ns = ns;
1322 return(comp->nbStep - 1);
1323}
1324
1325/**
1326 * xmlStreamCompile:
1327 * @comp: the precompiled pattern
1328 *
1329 * Tries to stream compile a pattern
1330 *
1331 * Returns -1 in case of failure and 0 in case of success.
1332 */
1333static int
1334xmlStreamCompile(xmlPatternPtr comp) {
1335 xmlStreamCompPtr stream;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001336 int i, s = 0, root = 0, flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001337
1338 if ((comp == NULL) || (comp->steps == NULL))
1339 return(-1);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001340 /*
1341 * special case for .
1342 */
1343 if ((comp->nbStep == 1) &&
1344 (comp->steps[0].op == XML_OP_ELEM) &&
1345 (comp->steps[0].value == NULL) &&
1346 (comp->steps[0].value2 == NULL)) {
1347 stream = xmlNewStreamComp(0);
1348 if (stream == NULL)
1349 return(-1);
1350 comp->stream = stream;
1351 return(0);
1352 }
1353
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001354 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1355 if (stream == NULL)
1356 return(-1);
1357 if (comp->dict != NULL) {
1358 stream->dict = comp->dict;
1359 xmlDictReference(stream->dict);
1360 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001361
1362 /*
1363 * Skip leading ./ on relative paths
1364 */
1365 i = 0;
1366 while ((comp->flags & PAT_FROM_CUR) && (comp->nbStep > i + 2) &&
1367 (comp->steps[i].op == XML_OP_ELEM) &&
1368 (comp->steps[i].value == NULL) &&
1369 (comp->steps[i].value2 == NULL) &&
1370 (comp->steps[i + 1].op == XML_OP_PARENT)) {
1371 i += 2;
1372 }
1373 for (;i < comp->nbStep;i++) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001374 switch (comp->steps[i].op) {
1375 case XML_OP_END:
1376 break;
1377 case XML_OP_ROOT:
1378 if (i != 0)
1379 goto error;
1380 root = 1;
1381 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001382 case XML_OP_NS:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001383 s = xmlStreamCompAddStep(stream, NULL,
1384 comp->steps[i].value, flags);
1385 flags = 0;
1386 if (s < 0)
1387 goto error;
1388 break;
1389 case XML_OP_ATTR:
1390 flags |= XML_STREAM_STEP_ATTR;
1391 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1392 comp->steps[i].value2, flags);
1393 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001394 if (s < 0)
1395 goto error;
1396 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001397 case XML_OP_ELEM:
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001398 if ((comp->steps[i].value == NULL) &&
1399 (comp->steps[i].value2 == NULL) &&
1400 (comp->nbStep > i + 2) &&
1401 (comp->steps[i + 1].op == XML_OP_PARENT)) {
1402 i++;
1403 continue;
1404 }
1405 case XML_OP_CHILD:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001406 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1407 comp->steps[i].value2, flags);
1408 flags = 0;
1409 if (s < 0)
1410 goto error;
1411 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001412 case XML_OP_ALL:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001413 s = xmlStreamCompAddStep(stream, NULL, NULL, flags);
1414 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001415 if (s < 0)
1416 goto error;
1417 break;
1418 case XML_OP_PARENT:
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001419 if ((comp->nbStep > i + 1) &&
1420 (comp->steps[i + 1].op == XML_OP_ELEM) &&
1421 (comp->steps[i + 1].value == NULL) &&
1422 (comp->steps[i + 1].value2 == NULL)) {
1423 i++;
1424 continue;
1425 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001426 break;
1427 case XML_OP_ANCESTOR:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001428 flags |= XML_STREAM_STEP_DESC;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001429 break;
1430 }
1431 }
1432 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1433 if (root)
1434 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1435#ifdef DEBUG_STREAMING
1436 xmlDebugStreamComp(stream);
1437#endif
1438 comp->stream = stream;
1439 return(0);
1440error:
1441 xmlFreeStreamComp(stream);
1442 return(0);
1443}
1444
1445/**
1446 * xmlNewStreamCtxt:
1447 * @size: the number of expected states
1448 *
1449 * build a new stream context
1450 *
1451 * Returns the new structure or NULL in case of error.
1452 */
1453static xmlStreamCtxtPtr
1454xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1455 xmlStreamCtxtPtr cur;
1456
1457 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1458 if (cur == NULL) {
1459 ERROR(NULL, NULL, NULL,
1460 "xmlNewStreamCtxt: malloc failed\n");
1461 return(NULL);
1462 }
1463 memset(cur, 0, sizeof(xmlStreamCtxt));
1464 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1465 if (cur->states == NULL) {
1466 xmlFree(cur);
1467 ERROR(NULL, NULL, NULL,
1468 "xmlNewStreamCtxt: malloc failed\n");
1469 return(NULL);
1470 }
1471 cur->nbState = 0;
1472 cur->maxState = 4;
1473 cur->level = 0;
1474 cur->comp = stream;
1475 return(cur);
1476}
1477
1478/**
1479 * xmlFreeStreamCtxt:
1480 * @stream: the stream context
1481 *
1482 * Free the stream context
1483 */
1484void
1485xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001486 xmlStreamCtxtPtr next;
1487
1488 while (stream != NULL) {
1489 next = stream->next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001490 if (stream->states != NULL)
1491 xmlFree(stream->states);
1492 xmlFree(stream);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001493 stream = next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001494 }
1495}
1496
1497/**
1498 * xmlStreamCtxtAddState:
1499 * @comp: the stream context
1500 * @idx: the step index for that streaming state
1501 *
1502 * Add a new state to the stream context
1503 *
1504 * Returns -1 in case of error or the state index if successful
1505 */
1506static int
1507xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1508 int i;
1509 for (i = 0;i < comp->nbState;i++) {
1510 if (comp->states[2 * i] < 0) {
1511 comp->states[2 * i] = idx;
1512 comp->states[2 * i + 1] = level;
1513 return(i);
1514 }
1515 }
1516 if (comp->nbState >= comp->maxState) {
1517 int *cur;
1518
1519 cur = (int *) xmlRealloc(comp->states,
1520 comp->maxState * 4 * sizeof(int));
1521 if (cur == NULL) {
1522 ERROR(NULL, NULL, NULL,
1523 "xmlNewStreamCtxt: malloc failed\n");
1524 return(-1);
1525 }
1526 comp->states = cur;
1527 comp->maxState *= 2;
1528 }
1529 comp->states[2 * comp->nbState] = idx;
1530 comp->states[2 * comp->nbState++ + 1] = level;
1531 return(comp->nbState - 1);
1532}
1533
1534/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001535 * xmlStreamPushInternal:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001536 * @stream: the stream context
1537 * @name: the current name
1538 * @ns: the namespace name
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001539 * @nodeType: the type of the node
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001540 *
1541 * push new data onto the stream. NOTE: if the call xmlPatterncompile()
1542 * indicated a dictionnary, then strings for name and ns will be expected
1543 * to come from the dictionary.
1544 * Both @name and @ns being NULL means the / i.e. the root of the document.
1545 * This can also act as a reset.
1546 *
1547 * Returns: -1 in case of error, 1 if the current state in the stream is a
1548 * match and 0 otherwise.
1549 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001550static int
1551xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1552 const xmlChar *name, const xmlChar *ns,
1553 xmlElementType nodeType) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001554 int ret = 0, err = 0, tmp, i, m, match, step, desc, final;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001555 xmlStreamCompPtr comp;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001556#ifdef DEBUG_STREAMING
1557 xmlStreamCtxtPtr orig = stream;
1558#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001559
1560 if ((stream == NULL) || (stream->nbState < 0))
1561 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001562
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001563 while (stream != NULL) {
1564 comp = stream->comp;
1565 if ((name == NULL) && (ns == NULL)) {
1566 stream->nbState = 0;
1567 stream->level = 0;
1568 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1569 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1570 if (tmp < 0)
1571 err++;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001572 if (comp->nbStep == 0)
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001573 ret = 1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001574 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001575 continue; /* while */
1576 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001577 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001578 continue; /* while */
1579 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001580
1581 /*
1582 * Fast check for ".".
1583 */
1584 if (comp->nbStep == 0) {
1585 if (nodeType == XML_ELEMENT_NODE)
1586 ret = 1;
1587 goto stream_next;
1588 }
1589
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001590 /*
1591 * Check evolution of existing states
1592 */
1593 m = stream->nbState;
1594 for (i = 0;i < m;i++) {
1595 match = 0;
1596 step = stream->states[2 * i];
1597 /* dead states */
1598 if (step < 0) continue;
1599 /* skip new states just added */
1600 if (stream->states[(2 * i) + 1] > stream->level)
1601 continue;
1602 /* skip continuations */
1603 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1604 if ((stream->states[(2 * i) + 1] < stream->level) && (!desc))
1605 continue;
1606
1607 /* discard old states */
1608 /* something needed about old level discarded */
1609
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001610 /*
1611 * Check for correct node-type.
1612 */
1613 if ((comp->steps[step].flags & XML_STREAM_STEP_ATTR) &&
1614 (nodeType != XML_ATTRIBUTE_NODE))
1615 continue;
1616
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001617 if (comp->dict) {
1618 if (comp->steps[step].name == NULL) {
1619 if (comp->steps[step].ns == NULL)
1620 match = 1;
1621 else
1622 match = (comp->steps[step].ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001623 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001624 match = ((comp->steps[step].name == name) &&
1625 (comp->steps[step].ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001626 }
1627 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001628 if (comp->steps[step].name == NULL) {
1629 if (comp->steps[step].ns == NULL)
1630 match = 1;
1631 else
1632 match = xmlStrEqual(comp->steps[step].ns, ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001633 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001634 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
1635 (xmlStrEqual(comp->steps[step].ns, ns)));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001636 }
1637 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001638 if (match) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001639 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
1640 if (desc) {
1641 if (final) {
1642 ret = 1;
1643 } else {
1644 /* descending match create a new state */
1645 xmlStreamCtxtAddState(stream, step + 1,
1646 stream->level + 1);
1647 }
1648 } else {
1649 if (final) {
1650 ret = 1;
1651 } else {
1652 xmlStreamCtxtAddState(stream, step + 1,
1653 stream->level + 1);
1654 }
1655 }
1656 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001657 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001658
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001659 /*
1660 * Check creating a new state.
1661 */
1662 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001663
1664 /*
1665 * Check the start only if this is a "desc" evaluation
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001666 * or if we are at the first level of evaluation.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001667 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001668 desc = comp->steps[0].flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001669 if ( ((comp->steps[0].flags & XML_STREAM_STEP_ROOT) == 0) &&
1670 ( ((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1671 ( (desc || (stream->level == 1)) )
1672 )
1673 ) {
1674
1675/*
1676#ifdef SUPPORT_IDC
1677
1678
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001679 if ((desc || (stream->level == 1)) &&
1680 (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT))) {
1681
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001682 *
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001683 * Workaround for missing "self::node()" on "@foo".
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001684 *
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001685 if (comp->steps[0].flags & XML_STREAM_STEP_ATTR) {
1686 xmlStreamCtxtAddState(stream, 0, stream->level);
1687 goto stream_next;
1688 }
1689#else
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001690
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001691 if (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT)) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001692#endif
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001693 */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001694 match = 0;
1695 if (comp->dict) {
1696 if (comp->steps[0].name == NULL) {
1697 if (comp->steps[0].ns == NULL)
1698 match = 1;
1699 else
1700 match = (comp->steps[0].ns == ns);
1701 } else {
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001702 if (stream->flags & XML_PATTERN_NOTPATTERN) {
1703 /*
1704 * Workaround for missing "self::node() on "foo".
1705 */
1706 if (!desc) {
1707 xmlStreamCtxtAddState(stream, 0, stream->level);
1708 goto stream_next;
1709 } else {
1710 match = ((comp->steps[0].name == name) &&
1711 (comp->steps[0].ns == ns));
1712 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001713 } else {
1714 match = ((comp->steps[0].name == name) &&
1715 (comp->steps[0].ns == ns));
1716 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001717 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001718 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001719 if (comp->steps[0].name == NULL) {
1720 if (comp->steps[0].ns == NULL)
1721 match = 1;
1722 else
1723 match = xmlStrEqual(comp->steps[0].ns, ns);
1724 } else {
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001725 if (stream->flags & XML_PATTERN_NOTPATTERN) {
1726 /*
1727 * Workaround for missing "self::node() on "foo".
1728 */
1729 if (!desc) {
1730 xmlStreamCtxtAddState(stream, 0, stream->level);
1731 goto stream_next;
1732 } else {
1733 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1734 (xmlStrEqual(comp->steps[0].ns, ns)));
1735 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001736 } else {
1737 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1738 (xmlStrEqual(comp->steps[0].ns, ns)));
1739 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001740 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001741 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001742 if (match) {
1743 if (comp->steps[0].flags & XML_STREAM_STEP_FINAL)
1744 ret = 1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001745 else
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001746 xmlStreamCtxtAddState(stream, 1, stream->level);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001747 }
1748 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001749stream_next:
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001750 stream = stream->next;
1751 } /* while stream != NULL */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001752
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001753 if (err > 0)
1754 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001755#ifdef DEBUG_STREAMING
1756 xmlDebugStreamCtxt(orig, ret);
1757#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001758 return(ret);
1759}
1760
1761/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001762 * xmlStreamPush:
1763 * @stream: the stream context
1764 * @name: the current name
1765 * @ns: the namespace name
1766 *
1767 * push new data onto the stream. NOTE: if the call xmlPatterncompile()
1768 * indicated a dictionnary, then strings for name and ns will be expected
1769 * to come from the dictionary.
1770 * Both @name and @ns being NULL means the / i.e. the root of the document.
1771 * This can also act as a reset.
1772 *
1773 * Returns: -1 in case of error, 1 if the current state in the stream is a
1774 * match and 0 otherwise.
1775 */
1776int
1777xmlStreamPush(xmlStreamCtxtPtr stream,
1778 const xmlChar *name, const xmlChar *ns) {
1779 return (xmlStreamPushInternal(stream, name, ns, XML_ELEMENT_NODE));
1780}
1781
1782/**
1783* xmlStreamPushAttr:
1784* @stream: the stream context
1785* @name: the current name
1786* @ns: the namespace name
1787*
1788* push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
1789* indicated a dictionnary, then strings for name and ns will be expected
1790* to come from the dictionary.
1791* Both @name and @ns being NULL means the / i.e. the root of the document.
1792* This can also act as a reset.
1793*
1794* Returns: -1 in case of error, 1 if the current state in the stream is a
1795* match and 0 otherwise.
1796*/
1797int
1798xmlStreamPushAttr(xmlStreamCtxtPtr stream,
1799 const xmlChar *name, const xmlChar *ns) {
1800 return (xmlStreamPushInternal(stream, name, ns, XML_ATTRIBUTE_NODE));
1801}
1802
1803/**
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001804 * xmlStreamPop:
1805 * @stream: the stream context
1806 *
1807 * push one level from the stream.
1808 *
1809 * Returns: -1 in case of error, 0 otherwise.
1810 */
1811int
1812xmlStreamPop(xmlStreamCtxtPtr stream) {
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001813 int i, m;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001814 int ret;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001815
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001816 if (stream == NULL)
1817 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001818 ret = 0;
1819 while (stream != NULL) {
1820 stream->level--;
1821 if (stream->level < 0)
1822 ret = -1;
1823
1824 /*
1825 * Check evolution of existing states
1826 */
1827 m = stream->nbState;
1828 for (i = 0;i < m;i++) {
1829 if (stream->states[(2 * i)] < 0) break;
1830 /* discard obsoleted states */
1831 if (stream->states[(2 * i) + 1] > stream->level)
1832 stream->states[(2 * i)] = -1;
1833 }
1834 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001835 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001836 return(0);
1837}
1838
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001839/************************************************************************
1840 * *
1841 * The public interfaces *
1842 * *
1843 ************************************************************************/
1844
1845/**
1846 * xmlPatterncompile:
1847 * @pattern: the pattern to compile
1848 * @dict: an optional dictionnary for interned strings
1849 * @flags: compilation flags, undefined yet
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001850 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001851 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001852 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001853 *
1854 * Returns the compiled for of the pattern or NULL in case of error
1855 */
1856xmlPatternPtr
Daniel Veillard427174f2003-12-10 10:42:59 +00001857xmlPatterncompile(const xmlChar *pattern, xmlDict *dict,
1858 int flags ATTRIBUTE_UNUSED,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001859 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001860 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001861 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001862 const xmlChar *or, *start;
1863 xmlChar *tmp = NULL;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001864 int type = 0;
1865 int streamable = 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001866
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001867 if (pattern == NULL)
1868 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001869
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001870 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001871 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001872 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001873 tmp = NULL;
1874 while ((*or != 0) && (*or != '|')) or++;
1875 if (*or == 0)
1876 ctxt = xmlNewPatParserContext(start, dict, namespaces);
1877 else {
1878 tmp = xmlStrndup(start, or - start);
1879 if (tmp != NULL) {
1880 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
1881 }
1882 or++;
1883 }
1884 if (ctxt == NULL) goto error;
1885 cur = xmlNewPattern();
1886 if (cur == NULL) goto error;
1887 if (ret == NULL)
1888 ret = cur;
1889 else {
1890 cur->next = ret->next;
1891 ret->next = cur;
1892 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001893 cur->flags = flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001894 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001895
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001896 xmlCompilePathPattern(ctxt);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001897 if (ctxt->error != 0)
1898 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001899 xmlFreePatParserContext(ctxt);
1900
1901
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001902 if (streamable) {
1903 if (type == 0) {
1904 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
1905 } else if (type == PAT_FROM_ROOT) {
1906 if (cur->flags & PAT_FROM_CUR)
1907 streamable = 0;
1908 } else if (type == PAT_FROM_CUR) {
1909 if (cur->flags & PAT_FROM_ROOT)
1910 streamable = 0;
1911 }
1912 }
1913 if (streamable)
1914 xmlStreamCompile(cur);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001915 if (xmlReversePattern(cur) < 0)
1916 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001917 if (tmp != NULL) {
1918 xmlFree(tmp);
1919 tmp = NULL;
1920 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001921 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001922 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001923 if (streamable == 0) {
1924 cur = ret;
1925 while (cur != NULL) {
1926 if (cur->stream != NULL) {
1927 xmlFreeStreamComp(cur->stream);
1928 cur->stream = NULL;
1929 }
1930 cur = cur->next;
1931 }
1932 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001933
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001934 return(ret);
1935error:
1936 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
1937 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001938 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001939 return(NULL);
1940}
1941
1942/**
1943 * xmlPatternMatch:
1944 * @comp: the precompiled pattern
1945 * @node: a node
1946 *
1947 * Test wether the node matches the pattern
1948 *
1949 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1950 */
1951int
1952xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
1953{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001954 int ret = 0;
1955
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001956 if ((comp == NULL) || (node == NULL))
1957 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001958
1959 while (comp != NULL) {
1960 ret = xmlPatMatch(comp, node);
1961 if (ret != 0)
1962 return(ret);
1963 comp = comp->next;
1964 }
1965 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001966}
1967
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001968/**
1969 * xmlPatternGetStreamCtxt:
1970 * @comp: the precompiled pattern
1971 *
1972 * Get a streaming context for that pattern
1973 * Use xmlFreeStreamCtxt to free the context.
1974 *
1975 * Returns a pointer to the context or NULL in case of failure
1976 */
1977xmlStreamCtxtPtr
1978xmlPatternGetStreamCtxt(xmlPatternPtr comp)
1979{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001980 xmlStreamCtxtPtr ret = NULL, cur;
1981
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001982 if ((comp == NULL) || (comp->stream == NULL))
1983 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001984
1985 while (comp != NULL) {
1986 if (comp->stream == NULL)
1987 goto failed;
1988 cur = xmlNewStreamCtxt(comp->stream);
1989 if (cur == NULL)
1990 goto failed;
1991 if (ret == NULL)
1992 ret = cur;
1993 else {
1994 cur->next = ret->next;
1995 ret->next = cur;
1996 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001997 cur->flags = comp->flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001998 comp = comp->next;
1999 }
2000 return(ret);
2001failed:
2002 xmlFreeStreamCtxt(ret);
2003 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002004}
2005
Daniel Veillard56de87e2005-02-16 00:22:29 +00002006/**
2007 * xmlPatternStreamable:
2008 * @comp: the precompiled pattern
2009 *
2010 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2011 * should work.
2012 *
2013 * Returns 1 if streamable, 0 if not and -1 in case of error.
2014 */
2015int
2016xmlPatternStreamable(xmlPatternPtr comp) {
2017 if (comp == NULL)
2018 return(-1);
2019 while (comp != NULL) {
2020 if (comp->stream == NULL)
2021 return(0);
2022 comp = comp->next;
2023 }
2024 return(1);
2025}
2026
2027/**
2028 * xmlPatternMaxDepth:
2029 * @comp: the precompiled pattern
2030 *
2031 * Check the maximum depth reachable by a pattern
2032 *
2033 * Returns -2 if no limit (using //), otherwise the depth,
2034 * and -1 in case of error
2035 */
2036int
2037xmlPatternMaxDepth(xmlPatternPtr comp) {
2038 int ret = 0, i;
2039 if (comp == NULL)
2040 return(-1);
2041 while (comp != NULL) {
2042 if (comp->stream == NULL)
2043 return(-1);
2044 for (i = 0;i < comp->stream->nbStep;i++)
2045 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2046 return(-2);
2047 if (comp->stream->nbStep > ret)
2048 ret = comp->stream->nbStep;
2049 comp = comp->next;
2050 }
2051 return(ret);
2052
2053}
2054
2055/**
2056 * xmlPatternFromRoot:
2057 * @comp: the precompiled pattern
2058 *
2059 * Check if the pattern must be looked at from the root.
2060 *
2061 * Returns 1 if true, 0 if false and -1 in case of error
2062 */
2063int
2064xmlPatternFromRoot(xmlPatternPtr comp) {
2065 if (comp == NULL)
2066 return(-1);
2067 while (comp != NULL) {
2068 if (comp->stream == NULL)
2069 return(-1);
2070 if (comp->flags & PAT_FROM_ROOT)
2071 return(1);
2072 comp = comp->next;
2073 }
2074 return(0);
2075
2076}
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002077
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002078#endif /* LIBXML_PATTERN_ENABLED */