blob: ca0580fc7e8a2e20526c5d34364b1e5fb7bf8c9e [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 }
Daniel Veillard0e460da2005-03-30 22:47:10 +00001085 /* NOT REACHED 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) {
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001585 /*
1586 * For non-pattern like evaluation like XML Schema IDCs,
1587 * this will match if we are at the first level only,
1588 * otherwise on every level.
1589 */
1590 if ((nodeType == XML_ELEMENT_NODE) &&
1591 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1592 (stream->level == 0))) {
1593 ret = 1;
1594 }
1595 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001596 goto stream_next;
1597 }
1598
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001599 /*
1600 * Check evolution of existing states
1601 */
1602 m = stream->nbState;
1603 for (i = 0;i < m;i++) {
1604 match = 0;
1605 step = stream->states[2 * i];
1606 /* dead states */
1607 if (step < 0) continue;
1608 /* skip new states just added */
1609 if (stream->states[(2 * i) + 1] > stream->level)
1610 continue;
1611 /* skip continuations */
1612 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1613 if ((stream->states[(2 * i) + 1] < stream->level) && (!desc))
1614 continue;
1615
1616 /* discard old states */
1617 /* something needed about old level discarded */
1618
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001619 /*
1620 * Check for correct node-type.
1621 */
1622 if ((comp->steps[step].flags & XML_STREAM_STEP_ATTR) &&
1623 (nodeType != XML_ATTRIBUTE_NODE))
1624 continue;
1625
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001626 if (comp->dict) {
1627 if (comp->steps[step].name == NULL) {
1628 if (comp->steps[step].ns == NULL)
1629 match = 1;
1630 else
1631 match = (comp->steps[step].ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001632 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001633 match = ((comp->steps[step].name == name) &&
1634 (comp->steps[step].ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001635 }
1636 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001637 if (comp->steps[step].name == NULL) {
1638 if (comp->steps[step].ns == NULL)
1639 match = 1;
1640 else
1641 match = xmlStrEqual(comp->steps[step].ns, ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001642 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001643 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
1644 (xmlStrEqual(comp->steps[step].ns, ns)));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001645 }
1646 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001647 if (match) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001648 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
1649 if (desc) {
1650 if (final) {
1651 ret = 1;
1652 } else {
1653 /* descending match create a new state */
1654 xmlStreamCtxtAddState(stream, step + 1,
1655 stream->level + 1);
1656 }
1657 } else {
1658 if (final) {
1659 ret = 1;
1660 } else {
1661 xmlStreamCtxtAddState(stream, step + 1,
1662 stream->level + 1);
1663 }
1664 }
1665 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001666 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001667
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001668 /*
1669 * Check creating a new state.
1670 */
1671 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001672
1673 /*
1674 * Check the start only if this is a "desc" evaluation
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001675 * or if we are at the first level of evaluation.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001676 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001677 desc = comp->steps[0].flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001678 if ( ((comp->steps[0].flags & XML_STREAM_STEP_ROOT) == 0) &&
1679 ( ((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1680 ( (desc || (stream->level == 1)) )
1681 )
1682 ) {
1683
1684/*
1685#ifdef SUPPORT_IDC
1686
1687
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001688 if ((desc || (stream->level == 1)) &&
1689 (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT))) {
1690
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001691 *
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001692 * Workaround for missing "self::node()" on "@foo".
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001693 *
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001694 if (comp->steps[0].flags & XML_STREAM_STEP_ATTR) {
1695 xmlStreamCtxtAddState(stream, 0, stream->level);
1696 goto stream_next;
1697 }
1698#else
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001699
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001700 if (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT)) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001701#endif
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001702 */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001703 match = 0;
1704 if (comp->dict) {
1705 if (comp->steps[0].name == NULL) {
1706 if (comp->steps[0].ns == NULL)
1707 match = 1;
1708 else
1709 match = (comp->steps[0].ns == ns);
1710 } else {
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001711 if (stream->flags & XML_PATTERN_NOTPATTERN) {
1712 /*
1713 * Workaround for missing "self::node() on "foo".
1714 */
1715 if (!desc) {
1716 xmlStreamCtxtAddState(stream, 0, stream->level);
1717 goto stream_next;
1718 } else {
1719 match = ((comp->steps[0].name == name) &&
1720 (comp->steps[0].ns == ns));
1721 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001722 } else {
1723 match = ((comp->steps[0].name == name) &&
1724 (comp->steps[0].ns == ns));
1725 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001726 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001727 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001728 if (comp->steps[0].name == NULL) {
1729 if (comp->steps[0].ns == NULL)
1730 match = 1;
1731 else
1732 match = xmlStrEqual(comp->steps[0].ns, ns);
1733 } else {
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001734 if (stream->flags & XML_PATTERN_NOTPATTERN) {
1735 /*
1736 * Workaround for missing "self::node() on "foo".
1737 */
1738 if (!desc) {
1739 xmlStreamCtxtAddState(stream, 0, stream->level);
1740 goto stream_next;
1741 } else {
1742 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1743 (xmlStrEqual(comp->steps[0].ns, ns)));
1744 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001745 } else {
1746 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1747 (xmlStrEqual(comp->steps[0].ns, ns)));
1748 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001749 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001750 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001751 if (match) {
1752 if (comp->steps[0].flags & XML_STREAM_STEP_FINAL)
1753 ret = 1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001754 else
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001755 xmlStreamCtxtAddState(stream, 1, stream->level);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001756 }
1757 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001758stream_next:
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001759 stream = stream->next;
1760 } /* while stream != NULL */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001761
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001762 if (err > 0)
1763 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001764#ifdef DEBUG_STREAMING
1765 xmlDebugStreamCtxt(orig, ret);
1766#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001767 return(ret);
1768}
1769
1770/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001771 * xmlStreamPush:
1772 * @stream: the stream context
1773 * @name: the current name
1774 * @ns: the namespace name
1775 *
1776 * push new data onto the stream. NOTE: if the call xmlPatterncompile()
1777 * indicated a dictionnary, then strings for name and ns will be expected
1778 * to come from the dictionary.
1779 * Both @name and @ns being NULL means the / i.e. the root of the document.
1780 * This can also act as a reset.
1781 *
1782 * Returns: -1 in case of error, 1 if the current state in the stream is a
1783 * match and 0 otherwise.
1784 */
1785int
1786xmlStreamPush(xmlStreamCtxtPtr stream,
1787 const xmlChar *name, const xmlChar *ns) {
1788 return (xmlStreamPushInternal(stream, name, ns, XML_ELEMENT_NODE));
1789}
1790
1791/**
1792* xmlStreamPushAttr:
1793* @stream: the stream context
1794* @name: the current name
1795* @ns: the namespace name
1796*
1797* push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
1798* indicated a dictionnary, then strings for name and ns will be expected
1799* to come from the dictionary.
1800* Both @name and @ns being NULL means the / i.e. the root of the document.
1801* This can also act as a reset.
1802*
1803* Returns: -1 in case of error, 1 if the current state in the stream is a
1804* match and 0 otherwise.
1805*/
1806int
1807xmlStreamPushAttr(xmlStreamCtxtPtr stream,
1808 const xmlChar *name, const xmlChar *ns) {
1809 return (xmlStreamPushInternal(stream, name, ns, XML_ATTRIBUTE_NODE));
1810}
1811
1812/**
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001813 * xmlStreamPop:
1814 * @stream: the stream context
1815 *
1816 * push one level from the stream.
1817 *
1818 * Returns: -1 in case of error, 0 otherwise.
1819 */
1820int
1821xmlStreamPop(xmlStreamCtxtPtr stream) {
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001822 int i, m;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001823 int ret;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001824
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001825 if (stream == NULL)
1826 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001827 ret = 0;
1828 while (stream != NULL) {
1829 stream->level--;
1830 if (stream->level < 0)
1831 ret = -1;
1832
1833 /*
1834 * Check evolution of existing states
1835 */
1836 m = stream->nbState;
1837 for (i = 0;i < m;i++) {
1838 if (stream->states[(2 * i)] < 0) break;
1839 /* discard obsoleted states */
1840 if (stream->states[(2 * i) + 1] > stream->level)
1841 stream->states[(2 * i)] = -1;
1842 }
1843 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001844 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001845 return(0);
1846}
1847
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001848/************************************************************************
1849 * *
1850 * The public interfaces *
1851 * *
1852 ************************************************************************/
1853
1854/**
1855 * xmlPatterncompile:
1856 * @pattern: the pattern to compile
1857 * @dict: an optional dictionnary for interned strings
1858 * @flags: compilation flags, undefined yet
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001859 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001860 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001861 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001862 *
1863 * Returns the compiled for of the pattern or NULL in case of error
1864 */
1865xmlPatternPtr
Daniel Veillard427174f2003-12-10 10:42:59 +00001866xmlPatterncompile(const xmlChar *pattern, xmlDict *dict,
1867 int flags ATTRIBUTE_UNUSED,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001868 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001869 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001870 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001871 const xmlChar *or, *start;
1872 xmlChar *tmp = NULL;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001873 int type = 0;
1874 int streamable = 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001875
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001876 if (pattern == NULL)
1877 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001878
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001879 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001880 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001881 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001882 tmp = NULL;
1883 while ((*or != 0) && (*or != '|')) or++;
1884 if (*or == 0)
1885 ctxt = xmlNewPatParserContext(start, dict, namespaces);
1886 else {
1887 tmp = xmlStrndup(start, or - start);
1888 if (tmp != NULL) {
1889 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
1890 }
1891 or++;
1892 }
1893 if (ctxt == NULL) goto error;
1894 cur = xmlNewPattern();
1895 if (cur == NULL) goto error;
1896 if (ret == NULL)
1897 ret = cur;
1898 else {
1899 cur->next = ret->next;
1900 ret->next = cur;
1901 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001902 cur->flags = flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001903 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001904
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001905 xmlCompilePathPattern(ctxt);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001906 if (ctxt->error != 0)
1907 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001908 xmlFreePatParserContext(ctxt);
1909
1910
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001911 if (streamable) {
1912 if (type == 0) {
1913 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
1914 } else if (type == PAT_FROM_ROOT) {
1915 if (cur->flags & PAT_FROM_CUR)
1916 streamable = 0;
1917 } else if (type == PAT_FROM_CUR) {
1918 if (cur->flags & PAT_FROM_ROOT)
1919 streamable = 0;
1920 }
1921 }
1922 if (streamable)
1923 xmlStreamCompile(cur);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001924 if (xmlReversePattern(cur) < 0)
1925 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001926 if (tmp != NULL) {
1927 xmlFree(tmp);
1928 tmp = NULL;
1929 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001930 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001931 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001932 if (streamable == 0) {
1933 cur = ret;
1934 while (cur != NULL) {
1935 if (cur->stream != NULL) {
1936 xmlFreeStreamComp(cur->stream);
1937 cur->stream = NULL;
1938 }
1939 cur = cur->next;
1940 }
1941 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00001942
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001943 return(ret);
1944error:
1945 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
1946 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001947 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001948 return(NULL);
1949}
1950
1951/**
1952 * xmlPatternMatch:
1953 * @comp: the precompiled pattern
1954 * @node: a node
1955 *
1956 * Test wether the node matches the pattern
1957 *
1958 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1959 */
1960int
1961xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
1962{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001963 int ret = 0;
1964
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001965 if ((comp == NULL) || (node == NULL))
1966 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001967
1968 while (comp != NULL) {
1969 ret = xmlPatMatch(comp, node);
1970 if (ret != 0)
1971 return(ret);
1972 comp = comp->next;
1973 }
1974 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001975}
1976
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001977/**
1978 * xmlPatternGetStreamCtxt:
1979 * @comp: the precompiled pattern
1980 *
1981 * Get a streaming context for that pattern
1982 * Use xmlFreeStreamCtxt to free the context.
1983 *
1984 * Returns a pointer to the context or NULL in case of failure
1985 */
1986xmlStreamCtxtPtr
1987xmlPatternGetStreamCtxt(xmlPatternPtr comp)
1988{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001989 xmlStreamCtxtPtr ret = NULL, cur;
1990
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001991 if ((comp == NULL) || (comp->stream == NULL))
1992 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001993
1994 while (comp != NULL) {
1995 if (comp->stream == NULL)
1996 goto failed;
1997 cur = xmlNewStreamCtxt(comp->stream);
1998 if (cur == NULL)
1999 goto failed;
2000 if (ret == NULL)
2001 ret = cur;
2002 else {
2003 cur->next = ret->next;
2004 ret->next = cur;
2005 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002006 cur->flags = comp->flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002007 comp = comp->next;
2008 }
2009 return(ret);
2010failed:
2011 xmlFreeStreamCtxt(ret);
2012 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002013}
2014
Daniel Veillard56de87e2005-02-16 00:22:29 +00002015/**
2016 * xmlPatternStreamable:
2017 * @comp: the precompiled pattern
2018 *
2019 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2020 * should work.
2021 *
2022 * Returns 1 if streamable, 0 if not and -1 in case of error.
2023 */
2024int
2025xmlPatternStreamable(xmlPatternPtr comp) {
2026 if (comp == NULL)
2027 return(-1);
2028 while (comp != NULL) {
2029 if (comp->stream == NULL)
2030 return(0);
2031 comp = comp->next;
2032 }
2033 return(1);
2034}
2035
2036/**
2037 * xmlPatternMaxDepth:
2038 * @comp: the precompiled pattern
2039 *
2040 * Check the maximum depth reachable by a pattern
2041 *
2042 * Returns -2 if no limit (using //), otherwise the depth,
2043 * and -1 in case of error
2044 */
2045int
2046xmlPatternMaxDepth(xmlPatternPtr comp) {
2047 int ret = 0, i;
2048 if (comp == NULL)
2049 return(-1);
2050 while (comp != NULL) {
2051 if (comp->stream == NULL)
2052 return(-1);
2053 for (i = 0;i < comp->stream->nbStep;i++)
2054 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2055 return(-2);
2056 if (comp->stream->nbStep > ret)
2057 ret = comp->stream->nbStep;
2058 comp = comp->next;
2059 }
2060 return(ret);
2061
2062}
2063
2064/**
2065 * xmlPatternFromRoot:
2066 * @comp: the precompiled pattern
2067 *
2068 * Check if the pattern must be looked at from the root.
2069 *
2070 * Returns 1 if true, 0 if false and -1 in case of error
2071 */
2072int
2073xmlPatternFromRoot(xmlPatternPtr comp) {
2074 if (comp == NULL)
2075 return(-1);
2076 while (comp != NULL) {
2077 if (comp->stream == NULL)
2078 return(-1);
2079 if (comp->flags & PAT_FROM_ROOT)
2080 return(1);
2081 comp = comp->next;
2082 }
2083 return(0);
2084
2085}
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002086
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002087#define bottom_pattern
2088#include "elfgcchack.h"
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002089#endif /* LIBXML_PATTERN_ENABLED */