blob: 560dc20bd1d8fbc30bd694588084a7511e460c82 [file] [log] [blame]
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001/*
2 * pattern.c: Implemetation of selectors for nodes
3 *
4 * Reference:
5 * http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
6 * to some extent
7 * http://www.w3.org/TR/1999/REC-xml-19991116
8 *
9 * See Copyright for the status of this software.
10 *
11 * daniel@veillard.com
12 */
13
Daniel Veillardf9d16912005-01-30 22:36:30 +000014/*
15 * TODO:
16 * - compilation flags to check for specific syntaxes
17 * using flags of xmlPatterncompile()
18 * - making clear how pattern starting with / or . need to be handled,
19 * currently push(NULL, NULL) means a reset of the streaming context
20 * and indicating we are on / (the document node), probably need
21 * something similar for .
Daniel Veillardd4301ab2005-02-03 22:24:10 +000022 * - get rid of the "compile" starting with lowercase
23 * - get rid of the Strdup/Strndup in case of dictionary
Daniel Veillardf9d16912005-01-30 22:36:30 +000024 */
25
Daniel Veillardb3de70c2003-12-02 22:32:15 +000026#define IN_LIBXML
27#include "libxml.h"
28
29#include <string.h>
30#include <libxml/xmlmemory.h>
31#include <libxml/tree.h>
32#include <libxml/hash.h>
33#include <libxml/dict.h>
34#include <libxml/xmlerror.h>
35#include <libxml/parserInternals.h>
36#include <libxml/pattern.h>
37
Daniel Veillardd4301ab2005-02-03 22:24:10 +000038#ifdef LIBXML_PATTERN_ENABLED
Daniel Veillardb3de70c2003-12-02 22:32:15 +000039
Daniel Veillardd4301ab2005-02-03 22:24:10 +000040/* #define DEBUG_STREAMING */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +000041/* #define SUPPORT_IDC */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000042
Daniel Veillardb3de70c2003-12-02 22:32:15 +000043#define ERROR(a, b, c, d)
44#define ERROR5(a, b, c, d, e)
45
Daniel Veillard2fc6df92005-01-30 18:42:55 +000046#define XML_STREAM_STEP_DESC 1
47#define XML_STREAM_STEP_FINAL 2
48#define XML_STREAM_STEP_ROOT 4
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +000049#define XML_STREAM_STEP_ATTR 8
Daniel Veillard2fc6df92005-01-30 18:42:55 +000050
51typedef struct _xmlStreamStep xmlStreamStep;
52typedef xmlStreamStep *xmlStreamStepPtr;
53struct _xmlStreamStep {
54 int flags; /* properties of that step */
55 const xmlChar *name; /* first string value if NULL accept all */
56 const xmlChar *ns; /* second string value */
57};
58
59typedef struct _xmlStreamComp xmlStreamComp;
60typedef xmlStreamComp *xmlStreamCompPtr;
61struct _xmlStreamComp {
62 xmlDict *dict; /* the dictionnary if any */
63 int nbStep; /* number of steps in the automata */
64 int maxStep; /* allocated number of steps */
65 xmlStreamStepPtr steps; /* the array of steps */
66};
67
68struct _xmlStreamCtxt {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +000069 struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000070 xmlStreamCompPtr comp; /* the compiled stream */
71 int nbState; /* number of state in the automata */
72 int maxState; /* allocated number of state */
73 int level; /* how deep are we ? */
74 int *states; /* the array of step indexes */
75};
76
77static void xmlFreeStreamComp(xmlStreamCompPtr comp);
78
Daniel Veillardb3de70c2003-12-02 22:32:15 +000079/*
80 * Types are private:
81 */
82
83typedef enum {
84 XML_OP_END=0,
85 XML_OP_ROOT,
86 XML_OP_ELEM,
87 XML_OP_CHILD,
88 XML_OP_ATTR,
89 XML_OP_PARENT,
90 XML_OP_ANCESTOR,
91 XML_OP_NS,
92 XML_OP_ALL
93} xmlPatOp;
94
95
Daniel Veillardd4301ab2005-02-03 22:24:10 +000096typedef struct _xmlStepState xmlStepState;
97typedef xmlStepState *xmlStepStatePtr;
98struct _xmlStepState {
99 int step;
100 xmlNodePtr node;
101};
102
103typedef struct _xmlStepStates xmlStepStates;
104typedef xmlStepStates *xmlStepStatesPtr;
105struct _xmlStepStates {
106 int nbstates;
107 int maxstates;
108 xmlStepStatePtr states;
109};
110
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000111typedef struct _xmlStepOp xmlStepOp;
112typedef xmlStepOp *xmlStepOpPtr;
113struct _xmlStepOp {
114 xmlPatOp op;
115 const xmlChar *value;
116 const xmlChar *value2;
117};
118
Daniel Veillard56de87e2005-02-16 00:22:29 +0000119#define PAT_FROM_ROOT 1
120#define PAT_FROM_CUR 2
121
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000122struct _xmlPattern {
123 void *data; /* the associated template */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000124 xmlDictPtr dict; /* the optional dictionnary */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000125 struct _xmlPattern *next; /* next pattern if | is used */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000126 const xmlChar *pattern; /* the pattern */
127
Daniel Veillard56de87e2005-02-16 00:22:29 +0000128 int flags; /* flags */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000129 int nbStep;
130 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000131 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000132 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000133};
134
135typedef struct _xmlPatParserContext xmlPatParserContext;
136typedef xmlPatParserContext *xmlPatParserContextPtr;
137struct _xmlPatParserContext {
138 const xmlChar *cur; /* the current char being parsed */
139 const xmlChar *base; /* the full expression */
140 int error; /* error code */
141 xmlDictPtr dict; /* the dictionnary if any */
142 xmlPatternPtr comp; /* the result */
143 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000144 const xmlChar **namespaces; /* the namespaces definitions */
145 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000146};
147
148/************************************************************************
149 * *
150 * Type functions *
151 * *
152 ************************************************************************/
153
154/**
155 * xmlNewPattern:
156 *
157 * Create a new XSLT Pattern
158 *
159 * Returns the newly allocated xmlPatternPtr or NULL in case of error
160 */
161static xmlPatternPtr
162xmlNewPattern(void) {
163 xmlPatternPtr cur;
164
165 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
166 if (cur == NULL) {
167 ERROR(NULL, NULL, NULL,
168 "xmlNewPattern : malloc failed\n");
169 return(NULL);
170 }
171 memset(cur, 0, sizeof(xmlPattern));
172 cur->maxStep = 10;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000173 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
174 if (cur->steps == NULL) {
175 xmlFree(cur);
176 ERROR(NULL, NULL, NULL,
177 "xmlNewPattern : malloc failed\n");
178 return(NULL);
179 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000180 return(cur);
181}
182
183/**
184 * xmlFreePattern:
185 * @comp: an XSLT comp
186 *
187 * Free up the memory allocated by @comp
188 */
189void
190xmlFreePattern(xmlPatternPtr comp) {
191 xmlStepOpPtr op;
192 int i;
193
194 if (comp == NULL)
195 return;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000196 if (comp->next != NULL)
197 xmlFreePattern(comp->next);
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000198 if (comp->stream != NULL)
199 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000200 if (comp->pattern != NULL)
201 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000202 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000203 if (comp->dict == NULL) {
204 for (i = 0;i < comp->nbStep;i++) {
205 op = &comp->steps[i];
206 if (op->value != NULL)
207 xmlFree((xmlChar *) op->value);
208 if (op->value2 != NULL)
209 xmlFree((xmlChar *) op->value2);
210 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000211 }
212 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000213 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000214 if (comp->dict != NULL)
215 xmlDictFree(comp->dict);
216
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000217 memset(comp, -1, sizeof(xmlPattern));
218 xmlFree(comp);
219}
220
221/**
222 * xmlFreePatternList:
223 * @comp: an XSLT comp list
224 *
225 * Free up the memory allocated by all the elements of @comp
226 */
227void
228xmlFreePatternList(xmlPatternPtr comp) {
229 xmlPatternPtr cur;
230
231 while (comp != NULL) {
232 cur = comp;
233 comp = comp->next;
234 xmlFreePattern(cur);
235 }
236}
237
238/**
239 * xmlNewPatParserContext:
240 * @pattern: the pattern context
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000241 * @dict: the inherited dictionnary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000242 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
243 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000244 *
245 * Create a new XML pattern parser context
246 *
247 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
248 */
249static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000250xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
251 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000252 xmlPatParserContextPtr cur;
253
254 if (pattern == NULL)
255 return(NULL);
256
257 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
258 if (cur == NULL) {
259 ERROR(NULL, NULL, NULL,
260 "xmlNewPatParserContext : malloc failed\n");
261 return(NULL);
262 }
263 memset(cur, 0, sizeof(xmlPatParserContext));
264 cur->dict = dict;
265 cur->cur = pattern;
266 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000267 if (namespaces != NULL) {
268 int i;
269 for (i = 0;namespaces[2 * i] != NULL;i++);
270 cur->nb_namespaces = i;
271 } else {
272 cur->nb_namespaces = 0;
273 }
274 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000275 return(cur);
276}
277
278/**
279 * xmlFreePatParserContext:
280 * @ctxt: an XSLT parser context
281 *
282 * Free up the memory allocated by @ctxt
283 */
284static void
285xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
286 if (ctxt == NULL)
287 return;
288 memset(ctxt, -1, sizeof(xmlPatParserContext));
289 xmlFree(ctxt);
290}
291
292/**
293 * xmlPatternAdd:
294 * @comp: the compiled match expression
295 * @op: an op
296 * @value: the first value
297 * @value2: the second value
298 *
299 * Add an step to an XSLT Compiled Match
300 *
301 * Returns -1 in case of failure, 0 otherwise.
302 */
303static int
304xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
305 xmlPatternPtr comp,
306 xmlPatOp op, xmlChar * value, xmlChar * value2)
307{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000308 if (comp->nbStep >= comp->maxStep) {
309 xmlStepOpPtr temp;
310 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
311 sizeof(xmlStepOp));
312 if (temp == NULL) {
313 ERROR(ctxt, NULL, NULL,
314 "xmlPatternAdd: realloc failed\n");
315 return (-1);
316 }
317 comp->steps = temp;
318 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000319 }
320 comp->steps[comp->nbStep].op = op;
321 comp->steps[comp->nbStep].value = value;
322 comp->steps[comp->nbStep].value2 = value2;
323 comp->nbStep++;
324 return (0);
325}
326
327#if 0
328/**
329 * xsltSwapTopPattern:
330 * @comp: the compiled match expression
331 *
332 * reverse the two top steps.
333 */
334static void
335xsltSwapTopPattern(xmlPatternPtr comp) {
336 int i;
337 int j = comp->nbStep - 1;
338
339 if (j > 0) {
340 register const xmlChar *tmp;
341 register xmlPatOp op;
342 i = j - 1;
343 tmp = comp->steps[i].value;
344 comp->steps[i].value = comp->steps[j].value;
345 comp->steps[j].value = tmp;
346 tmp = comp->steps[i].value2;
347 comp->steps[i].value2 = comp->steps[j].value2;
348 comp->steps[j].value2 = tmp;
349 op = comp->steps[i].op;
350 comp->steps[i].op = comp->steps[j].op;
351 comp->steps[j].op = op;
352 }
353}
354#endif
355
356/**
357 * xmlReversePattern:
358 * @comp: the compiled match expression
359 *
360 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000361 *
362 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000363 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000364static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000365xmlReversePattern(xmlPatternPtr comp) {
Daniel Veillard56de87e2005-02-16 00:22:29 +0000366 int i, j;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000367
Daniel Veillard56de87e2005-02-16 00:22:29 +0000368 /*
369 * remove the leading // for //a or .//a
370 */
371 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
372 for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
373 comp->steps[i].value = comp->steps[j].value;
374 comp->steps[i].value2 = comp->steps[j].value2;
375 comp->steps[i].op = comp->steps[j].op;
376 }
377 comp->nbStep--;
378 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000379 if (comp->nbStep >= comp->maxStep) {
380 xmlStepOpPtr temp;
381 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
382 sizeof(xmlStepOp));
383 if (temp == NULL) {
384 ERROR(ctxt, NULL, NULL,
385 "xmlReversePattern: realloc failed\n");
386 return (-1);
387 }
388 comp->steps = temp;
389 comp->maxStep *= 2;
390 }
Daniel Veillard56de87e2005-02-16 00:22:29 +0000391 i = 0;
392 j = comp->nbStep - 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000393 while (j > i) {
394 register const xmlChar *tmp;
395 register xmlPatOp op;
396 tmp = comp->steps[i].value;
397 comp->steps[i].value = comp->steps[j].value;
398 comp->steps[j].value = tmp;
399 tmp = comp->steps[i].value2;
400 comp->steps[i].value2 = comp->steps[j].value2;
401 comp->steps[j].value2 = tmp;
402 op = comp->steps[i].op;
403 comp->steps[i].op = comp->steps[j].op;
404 comp->steps[j].op = op;
405 j--;
406 i++;
407 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000408 comp->steps[comp->nbStep].value = NULL;
409 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000410 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000411 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000412}
413
414/************************************************************************
415 * *
416 * The interpreter for the precompiled patterns *
417 * *
418 ************************************************************************/
419
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000420static int
421xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
422 if ((states->states == NULL) || (states->maxstates <= 0)) {
423 states->maxstates = 4;
424 states->nbstates = 0;
425 states->states = xmlMalloc(4 * sizeof(xmlStepState));
426 }
427 else if (states->maxstates <= states->nbstates) {
428 xmlStepState *tmp;
429
430 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
431 2 * states->maxstates * sizeof(xmlStepState));
432 if (tmp == NULL)
433 return(-1);
434 states->states = tmp;
435 states->maxstates *= 2;
436 }
437 states->states[states->nbstates].step = step;
438 states->states[states->nbstates++].node = node;
439#if 0
440 fprintf(stderr, "Push: %d, %s\n", step, node->name);
441#endif
442 return(0);
443}
444
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000445/**
446 * xmlPatMatch:
447 * @comp: the precompiled pattern
448 * @node: a node
449 *
450 * Test wether the node matches the pattern
451 *
452 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
453 */
454static int
455xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
456 int i;
457 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000458 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000459
460 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000461 i = 0;
462restart:
463 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000464 step = &comp->steps[i];
465 switch (step->op) {
466 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000467 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000468 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000469 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000470 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000471 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000472 if ((node->type == XML_DOCUMENT_NODE) ||
473#ifdef LIBXML_DOCB_ENABLED
474 (node->type == XML_DOCB_DOCUMENT_NODE) ||
475#endif
476 (node->type == XML_HTML_DOCUMENT_NODE))
477 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000478 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000479 case XML_OP_ELEM:
480 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000481 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000482 if (step->value == NULL)
483 continue;
484 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000485 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000486 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000487 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000488
489 /* Namespace test */
490 if (node->ns == NULL) {
491 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000492 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000493 } else if (node->ns->href != NULL) {
494 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000495 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000496 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000497 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000498 }
499 continue;
500 case XML_OP_CHILD: {
501 xmlNodePtr lst;
502
503 if ((node->type != XML_ELEMENT_NODE) &&
504 (node->type != XML_DOCUMENT_NODE) &&
505#ifdef LIBXML_DOCB_ENABLED
506 (node->type != XML_DOCB_DOCUMENT_NODE) &&
507#endif
508 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000509 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000510
511 lst = node->children;
512
513 if (step->value != NULL) {
514 while (lst != NULL) {
515 if ((lst->type == XML_ELEMENT_NODE) &&
516 (step->value[0] == lst->name[0]) &&
517 (xmlStrEqual(step->value, lst->name)))
518 break;
519 lst = lst->next;
520 }
521 if (lst != NULL)
522 continue;
523 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000524 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000525 }
526 case XML_OP_ATTR:
527 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000528 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000529 if (step->value != NULL) {
530 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000531 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000532 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000533 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000534 }
535 /* Namespace test */
536 if (node->ns == NULL) {
537 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000538 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000539 } else if (step->value2 != NULL) {
540 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000541 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000542 }
543 continue;
544 case XML_OP_PARENT:
545 if ((node->type == XML_DOCUMENT_NODE) ||
546 (node->type == XML_HTML_DOCUMENT_NODE) ||
547#ifdef LIBXML_DOCB_ENABLED
548 (node->type == XML_DOCB_DOCUMENT_NODE) ||
549#endif
550 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000551 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000552 node = node->parent;
553 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000554 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000555 if (step->value == NULL)
556 continue;
557 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000558 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000559 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000560 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000561 /* Namespace test */
562 if (node->ns == NULL) {
563 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000564 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000565 } else if (node->ns->href != NULL) {
566 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000567 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000568 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000569 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000570 }
571 continue;
572 case XML_OP_ANCESTOR:
573 /* TODO: implement coalescing of ANCESTOR/NODE ops */
574 if (step->value == NULL) {
575 i++;
576 step = &comp->steps[i];
577 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000578 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000579 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000580 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000581 if (step->value == NULL)
582 return(-1);
583 }
584 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000585 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000586 if ((node->type == XML_DOCUMENT_NODE) ||
587 (node->type == XML_HTML_DOCUMENT_NODE) ||
588#ifdef LIBXML_DOCB_ENABLED
589 (node->type == XML_DOCB_DOCUMENT_NODE) ||
590#endif
591 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000592 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000593 node = node->parent;
594 while (node != NULL) {
595 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000596 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000597 if ((node->type == XML_ELEMENT_NODE) &&
598 (step->value[0] == node->name[0]) &&
599 (xmlStrEqual(step->value, node->name))) {
600 /* Namespace test */
601 if (node->ns == NULL) {
602 if (step->value2 == NULL)
603 break;
604 } else if (node->ns->href != NULL) {
605 if ((step->value2 != NULL) &&
606 (xmlStrEqual(step->value2, node->ns->href)))
607 break;
608 }
609 }
610 node = node->parent;
611 }
612 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000613 goto rollback;
614 /*
615 * prepare a potential rollback from here
616 * for ancestors of that node.
617 */
618 if (step->op == XML_OP_ANCESTOR)
619 xmlPatPushState(&states, i, node);
620 else
621 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000622 continue;
623 case XML_OP_NS:
624 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000625 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000626 if (node->ns == NULL) {
627 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000628 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000629 } else if (node->ns->href != NULL) {
630 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000631 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000632 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000633 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000634 }
635 break;
636 case XML_OP_ALL:
637 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000638 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000639 break;
640 }
641 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000642found:
643 if (states.states != NULL) {
644 /* Free the rollback states */
645 xmlFree(states.states);
646 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000647 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000648rollback:
649 /* got an error try to rollback */
650 if (states.states == NULL)
651 return(0);
652 if (states.nbstates <= 0) {
653 xmlFree(states.states);
654 return(0);
655 }
656 states.nbstates--;
657 i = states.states[states.nbstates].step;
658 node = states.states[states.nbstates].node;
659#if 0
660 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
661#endif
662 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000663}
664
665/************************************************************************
666 * *
667 * Dedicated parser for templates *
668 * *
669 ************************************************************************/
670
671#define TODO \
672 xmlGenericError(xmlGenericErrorContext, \
673 "Unimplemented block at %s:%d\n", \
674 __FILE__, __LINE__);
675#define CUR (*ctxt->cur)
676#define SKIP(val) ctxt->cur += (val)
677#define NXT(val) ctxt->cur[(val)]
678#define CUR_PTR ctxt->cur
679
680#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000681 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000682
683#define CURRENT (*ctxt->cur)
684#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
685
686
687#define PUSH(op, val, val2) \
688 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
689
690#define XSLT_ERROR(X) \
691 { xsltError(ctxt, __FILE__, __LINE__, X); \
692 ctxt->error = (X); return; }
693
694#define XSLT_ERROR0(X) \
695 { xsltError(ctxt, __FILE__, __LINE__, X); \
696 ctxt->error = (X); return(0); }
697
698#if 0
699/**
700 * xmlPatScanLiteral:
701 * @ctxt: the XPath Parser context
702 *
703 * Parse an XPath Litteral:
704 *
705 * [29] Literal ::= '"' [^"]* '"'
706 * | "'" [^']* "'"
707 *
708 * Returns the Literal parsed or NULL
709 */
710
711static xmlChar *
712xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
713 const xmlChar *q, *cur;
714 xmlChar *ret = NULL;
715 int val, len;
716
717 SKIP_BLANKS;
718 if (CUR == '"') {
719 NEXT;
720 cur = q = CUR_PTR;
721 val = xmlStringCurrentChar(NULL, cur, &len);
722 while ((IS_CHAR(val)) && (val != '"')) {
723 cur += len;
724 val = xmlStringCurrentChar(NULL, cur, &len);
725 }
726 if (!IS_CHAR(val)) {
727 ctxt->error = 1;
728 return(NULL);
729 } else {
730 ret = xmlStrndup(q, cur - q);
731 }
732 cur += len;
733 CUR_PTR = cur;
734 } else if (CUR == '\'') {
735 NEXT;
736 cur = q = CUR_PTR;
737 val = xmlStringCurrentChar(NULL, cur, &len);
738 while ((IS_CHAR(val)) && (val != '\'')) {
739 cur += len;
740 val = xmlStringCurrentChar(NULL, cur, &len);
741 }
742 if (!IS_CHAR(val)) {
743 ctxt->error = 1;
744 return(NULL);
745 } else {
746 ret = xmlStrndup(q, cur - q);
747 }
748 cur += len;
749 CUR_PTR = cur;
750 } else {
751 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
752 ctxt->error = 1;
753 return(NULL);
754 }
755 return(ret);
756}
757#endif
758
759/**
760 * xmlPatScanName:
761 * @ctxt: the XPath Parser context
762 *
763 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
764 * CombiningChar | Extender
765 *
766 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
767 *
768 * [6] Names ::= Name (S Name)*
769 *
770 * Returns the Name parsed or NULL
771 */
772
773static xmlChar *
774xmlPatScanName(xmlPatParserContextPtr ctxt) {
775 const xmlChar *q, *cur;
776 xmlChar *ret = NULL;
777 int val, len;
778
779 SKIP_BLANKS;
780
781 cur = q = CUR_PTR;
782 val = xmlStringCurrentChar(NULL, cur, &len);
783 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
784 return(NULL);
785
786 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
787 (val == '.') || (val == '-') ||
788 (val == '_') ||
789 (IS_COMBINING(val)) ||
790 (IS_EXTENDER(val))) {
791 cur += len;
792 val = xmlStringCurrentChar(NULL, cur, &len);
793 }
794 ret = xmlStrndup(q, cur - q);
795 CUR_PTR = cur;
796 return(ret);
797}
798
799/**
800 * xmlPatScanNCName:
801 * @ctxt: the XPath Parser context
802 *
803 * Parses a non qualified name
804 *
805 * Returns the Name parsed or NULL
806 */
807
808static xmlChar *
809xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
810 const xmlChar *q, *cur;
811 xmlChar *ret = NULL;
812 int val, len;
813
814 SKIP_BLANKS;
815
816 cur = q = CUR_PTR;
817 val = xmlStringCurrentChar(NULL, cur, &len);
818 if (!IS_LETTER(val) && (val != '_'))
819 return(NULL);
820
821 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
822 (val == '.') || (val == '-') ||
823 (val == '_') ||
824 (IS_COMBINING(val)) ||
825 (IS_EXTENDER(val))) {
826 cur += len;
827 val = xmlStringCurrentChar(NULL, cur, &len);
828 }
829 ret = xmlStrndup(q, cur - q);
830 CUR_PTR = cur;
831 return(ret);
832}
833
834#if 0
835/**
836 * xmlPatScanQName:
837 * @ctxt: the XPath Parser context
838 * @prefix: the place to store the prefix
839 *
840 * Parse a qualified name
841 *
842 * Returns the Name parsed or NULL
843 */
844
845static xmlChar *
846xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
847 xmlChar *ret = NULL;
848
849 *prefix = NULL;
850 ret = xmlPatScanNCName(ctxt);
851 if (CUR == ':') {
852 *prefix = ret;
853 NEXT;
854 ret = xmlPatScanNCName(ctxt);
855 }
856 return(ret);
857}
858#endif
859
860/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000861 * xmlCompileAttributeTest:
862 * @ctxt: the compilation context
863 *
864 * Compile an attribute test.
865 */
866static void
867xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
868 xmlChar *token = NULL;
869 xmlChar *name = NULL;
870 xmlChar *URL = NULL;
871
872 name = xmlPatScanNCName(ctxt);
873 if (name == NULL) {
874 if (CUR == '*') {
875 PUSH(XML_OP_ATTR, NULL, NULL);
876 } else {
877 ERROR(NULL, NULL, NULL,
878 "xmlCompileAttributeTest : Name expected\n");
879 ctxt->error = 1;
880 }
881 return;
882 }
883 if (CUR == ':') {
884 int i;
885 xmlChar *prefix = name;
886
887 NEXT;
888 /*
889 * This is a namespace match
890 */
891 token = xmlPatScanName(ctxt);
892 for (i = 0;i < ctxt->nb_namespaces;i++) {
893 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
894 URL = xmlStrdup(ctxt->namespaces[2 * i]);
895 break;
896 }
897 }
898 if (i >= ctxt->nb_namespaces) {
899 ERROR5(NULL, NULL, NULL,
900 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
901 prefix);
902 ctxt->error = 1;
903 goto error;
904 }
905
906 xmlFree(prefix);
907 if (token == NULL) {
908 if (CUR == '*') {
909 NEXT;
910 PUSH(XML_OP_ATTR, NULL, URL);
911 } else {
912 ERROR(NULL, NULL, NULL,
913 "xmlCompileAttributeTest : Name expected\n");
914 ctxt->error = 1;
915 goto error;
916 }
917 } else {
918 PUSH(XML_OP_ATTR, token, URL);
919 }
920 } else {
921 PUSH(XML_OP_ATTR, name, NULL);
922 }
923 return;
924error:
925 if (URL != NULL)
926 xmlFree(URL);
927 if (token != NULL)
928 xmlFree(token);
929}
930
931
932/**
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000933 * xmlCompileStepPattern:
934 * @ctxt: the compilation context
935 *
936 * Compile the Step Pattern and generates a precompiled
937 * form suitable for fast matching.
938 *
939 * [3] Step ::= '.' | NameTest
940 * [4] NameTest ::= QName | '*' | NCName ':' '*'
941 */
942
943static void
944xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
945 xmlChar *token = NULL;
946 xmlChar *name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000947 xmlChar *URL = NULL;
948
949 SKIP_BLANKS;
950 if (CUR == '.') {
951 NEXT;
952 PUSH(XML_OP_ELEM, NULL, NULL);
953 return;
954 }
955 name = xmlPatScanNCName(ctxt);
956 if (name == NULL) {
957 if (CUR == '*') {
958 NEXT;
959 PUSH(XML_OP_ALL, NULL, NULL);
960 return;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000961 } else if (CUR == '@') {
962 NEXT;
963 xmlCompileAttributeTest(ctxt);
964 if (ctxt->error != 0)
965 goto error;
966 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000967 } else {
968 ERROR(NULL, NULL, NULL,
969 "xmlCompileStepPattern : Name expected\n");
970 ctxt->error = 1;
971 return;
972 }
973 }
974 SKIP_BLANKS;
975 if (CUR == ':') {
976 NEXT;
977 if (CUR != ':') {
978 xmlChar *prefix = name;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000979 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000980
981 /*
982 * This is a namespace match
983 */
984 token = xmlPatScanName(ctxt);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000985 for (i = 0;i < ctxt->nb_namespaces;i++) {
986 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Daniel Veillard0996a162005-02-05 14:00:10 +0000987 URL = xmlStrdup(ctxt->namespaces[2 * i]);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000988 break;
989 }
990 }
991 if (i >= ctxt->nb_namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000992 ERROR5(NULL, NULL, NULL,
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000993 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
994 prefix);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000995 ctxt->error = 1;
996 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000997 }
998 xmlFree(prefix);
999 if (token == NULL) {
1000 if (CUR == '*') {
1001 NEXT;
1002 PUSH(XML_OP_NS, URL, NULL);
1003 } else {
1004 ERROR(NULL, NULL, NULL,
1005 "xmlCompileStepPattern : Name expected\n");
1006 ctxt->error = 1;
1007 goto error;
1008 }
1009 } else {
1010 PUSH(XML_OP_ELEM, token, URL);
1011 }
1012 } else {
1013 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001014 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1015 xmlFree(name);
1016 name = xmlPatScanName(ctxt);
1017 if (name == NULL) {
1018 if (CUR == '*') {
1019 NEXT;
1020 PUSH(XML_OP_ALL, NULL, NULL);
1021 return;
1022 } else {
1023 ERROR(NULL, NULL, NULL,
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001024 "xmlCompileStepPattern : QName expected\n");
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001025 ctxt->error = 1;
1026 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001027 }
1028 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001029 if (CUR == ':') {
1030 xmlChar *prefix = name;
1031 int i;
1032
1033 NEXT;
1034 /*
1035 * This is a namespace match
1036 */
1037 token = xmlPatScanName(ctxt);
1038 for (i = 0;i < ctxt->nb_namespaces;i++) {
1039 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1040 URL = xmlStrdup(ctxt->namespaces[2 * i]);
1041 break;
1042 }
1043 }
1044 if (i >= ctxt->nb_namespaces) {
1045 ERROR5(NULL, NULL, NULL,
1046 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1047 prefix);
1048 ctxt->error = 1;
1049 goto error;
1050 }
1051 xmlFree(prefix);
1052 if (token == NULL) {
1053 if (CUR == '*') {
1054 NEXT;
1055 PUSH(XML_OP_NS, URL, NULL);
1056 } else {
1057 ERROR(NULL, NULL, NULL,
1058 "xmlCompileStepPattern : Name expected\n");
1059 ctxt->error = 1;
1060 goto error;
1061 }
1062 } else {
1063 PUSH(XML_OP_CHILD, token, URL);
1064 }
1065 } else
1066 PUSH(XML_OP_CHILD, name, NULL);
1067 return;
1068 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1069 xmlFree(name);
1070 name = NULL;
1071 xmlCompileAttributeTest(ctxt);
1072 if (ctxt->error != 0)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001073 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001074 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001075 } else {
1076 ERROR(NULL, NULL, NULL,
1077 "xmlCompileStepPattern : 'child' or 'attribute' expected\n");
1078 ctxt->error = 1;
1079 goto error;
1080 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001081 xmlFree(name);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001082 }
1083 } else if (CUR == '*') {
1084 NEXT;
1085 PUSH(XML_OP_ALL, token, NULL);
1086 } else {
1087 if (name == NULL) {
1088 ctxt->error = 1;
1089 goto error;
1090 }
1091 PUSH(XML_OP_ELEM, name, NULL);
1092 }
1093 return;
1094error:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001095 if (URL != NULL)
1096 xmlFree(URL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001097 if (token != NULL)
1098 xmlFree(token);
1099 if (name != NULL)
1100 xmlFree(name);
1101}
1102
1103/**
1104 * xmlCompilePathPattern:
1105 * @ctxt: the compilation context
1106 *
1107 * Compile the Path Pattern and generates a precompiled
1108 * form suitable for fast matching.
1109 *
1110 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1111 */
1112static void
1113xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1114 SKIP_BLANKS;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001115 if (CUR == '/') {
1116 ctxt->comp->flags |= PAT_FROM_ROOT;
1117 } else if (CUR == '.') {
1118 ctxt->comp->flags |= PAT_FROM_CUR;
1119 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001120 if ((CUR == '/') && (NXT(1) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001121 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001122 NEXT;
1123 NEXT;
1124 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001125 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001126 NEXT;
1127 NEXT;
1128 NEXT;
1129 }
1130 if (CUR == '@') {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001131 NEXT;
1132 xmlCompileAttributeTest(ctxt);
1133 SKIP_BLANKS;
1134 if ((CUR != 0) || (CUR == '|')) {
1135 xmlCompileStepPattern(ctxt);
1136 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001137 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001138 if (CUR == '/') {
1139 PUSH(XML_OP_ROOT, NULL, NULL);
1140 NEXT;
1141 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001142 xmlCompileStepPattern(ctxt);
1143 SKIP_BLANKS;
1144 while (CUR == '/') {
1145 if ((CUR == '/') && (NXT(1) == '/')) {
1146 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1147 NEXT;
1148 NEXT;
1149 SKIP_BLANKS;
1150 xmlCompileStepPattern(ctxt);
1151 } else {
1152 PUSH(XML_OP_PARENT, NULL, NULL);
1153 NEXT;
1154 SKIP_BLANKS;
1155 if ((CUR != 0) || (CUR == '|')) {
1156 xmlCompileStepPattern(ctxt);
1157 }
1158 }
1159 }
1160 }
Daniel Veillard56de87e2005-02-16 00:22:29 +00001161 if (CUR != 0) {
1162 ERROR5(NULL, NULL, NULL,
1163 "Failed to compile pattern %s\n", ctxt->base);
1164 ctxt->error = 1;
1165 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001166error:
1167 return;
1168}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001169
1170/************************************************************************
1171 * *
1172 * The streaming code *
1173 * *
1174 ************************************************************************/
1175
1176#ifdef DEBUG_STREAMING
1177static void
1178xmlDebugStreamComp(xmlStreamCompPtr stream) {
1179 int i;
1180
1181 if (stream == NULL) {
1182 printf("Stream: NULL\n");
1183 return;
1184 }
1185 printf("Stream: %d steps\n", stream->nbStep);
1186 for (i = 0;i < stream->nbStep;i++) {
1187 if (stream->steps[i].ns != NULL) {
1188 printf("{%s}", stream->steps[i].ns);
1189 }
1190 if (stream->steps[i].name == NULL) {
1191 printf("* ");
1192 } else {
1193 printf("%s ", stream->steps[i].name);
1194 }
1195 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1196 printf("root ");
1197 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1198 printf("// ");
1199 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1200 printf("final ");
1201 printf("\n");
1202 }
1203}
1204static void
1205xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1206 int i;
1207
1208 if (ctxt == NULL) {
1209 printf("Stream: NULL\n");
1210 return;
1211 }
1212 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1213 if (match)
1214 printf("matches\n");
1215 else
1216 printf("\n");
1217 for (i = 0;i < ctxt->nbState;i++) {
1218 if (ctxt->states[2 * i] < 0)
1219 printf(" %d: free\n", i);
1220 else {
1221 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1222 ctxt->states[(2 * i) + 1]);
1223 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1224 XML_STREAM_STEP_DESC)
1225 printf(" //\n");
1226 else
1227 printf("\n");
1228 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001229 }
1230}
1231#endif
1232/**
1233 * xmlNewStreamComp:
1234 * @size: the number of expected steps
1235 *
1236 * build a new compiled pattern for streaming
1237 *
1238 * Returns the new structure or NULL in case of error.
1239 */
1240static xmlStreamCompPtr
1241xmlNewStreamComp(int size) {
1242 xmlStreamCompPtr cur;
1243
1244 if (size < 4)
1245 size = 4;
1246
1247 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1248 if (cur == NULL) {
1249 ERROR(NULL, NULL, NULL,
1250 "xmlNewStreamComp: malloc failed\n");
1251 return(NULL);
1252 }
1253 memset(cur, 0, sizeof(xmlStreamComp));
1254 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1255 if (cur->steps == NULL) {
1256 xmlFree(cur);
1257 ERROR(NULL, NULL, NULL,
1258 "xmlNewStreamComp: malloc failed\n");
1259 return(NULL);
1260 }
1261 cur->nbStep = 0;
1262 cur->maxStep = size;
1263 return(cur);
1264}
1265
1266/**
1267 * xmlFreeStreamComp:
1268 * @comp: the compiled pattern for streaming
1269 *
1270 * Free the compiled pattern for streaming
1271 */
1272static void
1273xmlFreeStreamComp(xmlStreamCompPtr comp) {
1274 if (comp != NULL) {
1275 if (comp->steps != NULL)
1276 xmlFree(comp->steps);
1277 if (comp->dict != NULL)
1278 xmlDictFree(comp->dict);
1279 xmlFree(comp);
1280 }
1281}
1282
1283/**
1284 * xmlStreamCompAddStep:
1285 * @comp: the compiled pattern for streaming
1286 * @name: the first string, the name, or NULL for *
1287 * @ns: the second step, the namespace name
1288 * @flags: the flags for that step
1289 *
1290 * Add a new step to the compiled pattern
1291 *
1292 * Returns -1 in case of error or the step index if successful
1293 */
1294static int
1295xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1296 const xmlChar *ns, int flags) {
1297 xmlStreamStepPtr cur;
1298
1299 if (comp->nbStep >= comp->maxStep) {
1300 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1301 comp->maxStep * 2 * sizeof(xmlStreamStep));
1302 if (cur == NULL) {
1303 ERROR(NULL, NULL, NULL,
1304 "xmlNewStreamComp: malloc failed\n");
1305 return(-1);
1306 }
1307 comp->steps = cur;
1308 comp->maxStep *= 2;
1309 }
1310 cur = &comp->steps[comp->nbStep++];
1311 cur->flags = flags;
1312 cur->name = name;
1313 cur->ns = ns;
1314 return(comp->nbStep - 1);
1315}
1316
1317/**
1318 * xmlStreamCompile:
1319 * @comp: the precompiled pattern
1320 *
1321 * Tries to stream compile a pattern
1322 *
1323 * Returns -1 in case of failure and 0 in case of success.
1324 */
1325static int
1326xmlStreamCompile(xmlPatternPtr comp) {
1327 xmlStreamCompPtr stream;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001328 int i, s = 0, root = 0, flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001329
1330 if ((comp == NULL) || (comp->steps == NULL))
1331 return(-1);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001332 /*
1333 * special case for .
1334 */
1335 if ((comp->nbStep == 1) &&
1336 (comp->steps[0].op == XML_OP_ELEM) &&
1337 (comp->steps[0].value == NULL) &&
1338 (comp->steps[0].value2 == NULL)) {
1339 stream = xmlNewStreamComp(0);
1340 if (stream == NULL)
1341 return(-1);
1342 comp->stream = stream;
1343 return(0);
1344 }
1345
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001346 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1347 if (stream == NULL)
1348 return(-1);
1349 if (comp->dict != NULL) {
1350 stream->dict = comp->dict;
1351 xmlDictReference(stream->dict);
1352 }
1353 for (i = 0;i < comp->nbStep;i++) {
1354 switch (comp->steps[i].op) {
1355 case XML_OP_END:
1356 break;
1357 case XML_OP_ROOT:
1358 if (i != 0)
1359 goto error;
1360 root = 1;
1361 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001362 case XML_OP_NS:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001363 s = xmlStreamCompAddStep(stream, NULL,
1364 comp->steps[i].value, flags);
1365 flags = 0;
1366 if (s < 0)
1367 goto error;
1368 break;
1369 case XML_OP_ATTR:
1370 flags |= XML_STREAM_STEP_ATTR;
1371 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1372 comp->steps[i].value2, flags);
1373 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001374 if (s < 0)
1375 goto error;
1376 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001377 case XML_OP_CHILD:
1378 case XML_OP_ELEM:
1379 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1380 comp->steps[i].value2, flags);
1381 flags = 0;
1382 if (s < 0)
1383 goto error;
1384 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001385 case XML_OP_ALL:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001386 s = xmlStreamCompAddStep(stream, NULL, NULL, flags);
1387 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001388 if (s < 0)
1389 goto error;
1390 break;
1391 case XML_OP_PARENT:
1392 break;
1393 case XML_OP_ANCESTOR:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001394 flags |= XML_STREAM_STEP_DESC;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001395 break;
1396 }
1397 }
1398 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1399 if (root)
1400 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1401#ifdef DEBUG_STREAMING
1402 xmlDebugStreamComp(stream);
1403#endif
1404 comp->stream = stream;
1405 return(0);
1406error:
1407 xmlFreeStreamComp(stream);
1408 return(0);
1409}
1410
1411/**
1412 * xmlNewStreamCtxt:
1413 * @size: the number of expected states
1414 *
1415 * build a new stream context
1416 *
1417 * Returns the new structure or NULL in case of error.
1418 */
1419static xmlStreamCtxtPtr
1420xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1421 xmlStreamCtxtPtr cur;
1422
1423 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1424 if (cur == NULL) {
1425 ERROR(NULL, NULL, NULL,
1426 "xmlNewStreamCtxt: malloc failed\n");
1427 return(NULL);
1428 }
1429 memset(cur, 0, sizeof(xmlStreamCtxt));
1430 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1431 if (cur->states == NULL) {
1432 xmlFree(cur);
1433 ERROR(NULL, NULL, NULL,
1434 "xmlNewStreamCtxt: malloc failed\n");
1435 return(NULL);
1436 }
1437 cur->nbState = 0;
1438 cur->maxState = 4;
1439 cur->level = 0;
1440 cur->comp = stream;
1441 return(cur);
1442}
1443
1444/**
1445 * xmlFreeStreamCtxt:
1446 * @stream: the stream context
1447 *
1448 * Free the stream context
1449 */
1450void
1451xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001452 xmlStreamCtxtPtr next;
1453
1454 while (stream != NULL) {
1455 next = stream->next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001456 if (stream->states != NULL)
1457 xmlFree(stream->states);
1458 xmlFree(stream);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001459 stream = next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001460 }
1461}
1462
1463/**
1464 * xmlStreamCtxtAddState:
1465 * @comp: the stream context
1466 * @idx: the step index for that streaming state
1467 *
1468 * Add a new state to the stream context
1469 *
1470 * Returns -1 in case of error or the state index if successful
1471 */
1472static int
1473xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1474 int i;
1475 for (i = 0;i < comp->nbState;i++) {
1476 if (comp->states[2 * i] < 0) {
1477 comp->states[2 * i] = idx;
1478 comp->states[2 * i + 1] = level;
1479 return(i);
1480 }
1481 }
1482 if (comp->nbState >= comp->maxState) {
1483 int *cur;
1484
1485 cur = (int *) xmlRealloc(comp->states,
1486 comp->maxState * 4 * sizeof(int));
1487 if (cur == NULL) {
1488 ERROR(NULL, NULL, NULL,
1489 "xmlNewStreamCtxt: malloc failed\n");
1490 return(-1);
1491 }
1492 comp->states = cur;
1493 comp->maxState *= 2;
1494 }
1495 comp->states[2 * comp->nbState] = idx;
1496 comp->states[2 * comp->nbState++ + 1] = level;
1497 return(comp->nbState - 1);
1498}
1499
1500/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001501 * xmlStreamPushInternal:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001502 * @stream: the stream context
1503 * @name: the current name
1504 * @ns: the namespace name
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001505 * @nodeType: the type of the node
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001506 *
1507 * push new data onto the stream. NOTE: if the call xmlPatterncompile()
1508 * indicated a dictionnary, then strings for name and ns will be expected
1509 * to come from the dictionary.
1510 * Both @name and @ns being NULL means the / i.e. the root of the document.
1511 * This can also act as a reset.
1512 *
1513 * Returns: -1 in case of error, 1 if the current state in the stream is a
1514 * match and 0 otherwise.
1515 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001516static int
1517xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1518 const xmlChar *name, const xmlChar *ns,
1519 xmlElementType nodeType) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001520 int ret = 0, err = 0, tmp, i, m, match, step, desc, final;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001521 xmlStreamCompPtr comp;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001522#ifdef DEBUG_STREAMING
1523 xmlStreamCtxtPtr orig = stream;
1524#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001525
1526 if ((stream == NULL) || (stream->nbState < 0))
1527 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001528
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001529 while (stream != NULL) {
1530 comp = stream->comp;
1531 if ((name == NULL) && (ns == NULL)) {
1532 stream->nbState = 0;
1533 stream->level = 0;
1534 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1535 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1536 if (tmp < 0)
1537 err++;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001538 if (comp->nbStep == 0)
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001539 ret = 1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001540 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001541 continue; /* while */
1542 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001543 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001544 continue; /* while */
1545 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001546
1547 /*
1548 * Fast check for ".".
1549 */
1550 if (comp->nbStep == 0) {
1551 if (nodeType == XML_ELEMENT_NODE)
1552 ret = 1;
1553 goto stream_next;
1554 }
1555
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001556 /*
1557 * Check evolution of existing states
1558 */
1559 m = stream->nbState;
1560 for (i = 0;i < m;i++) {
1561 match = 0;
1562 step = stream->states[2 * i];
1563 /* dead states */
1564 if (step < 0) continue;
1565 /* skip new states just added */
1566 if (stream->states[(2 * i) + 1] > stream->level)
1567 continue;
1568 /* skip continuations */
1569 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1570 if ((stream->states[(2 * i) + 1] < stream->level) && (!desc))
1571 continue;
1572
1573 /* discard old states */
1574 /* something needed about old level discarded */
1575
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001576 /*
1577 * Check for correct node-type.
1578 */
1579 if ((comp->steps[step].flags & XML_STREAM_STEP_ATTR) &&
1580 (nodeType != XML_ATTRIBUTE_NODE))
1581 continue;
1582
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001583 if (comp->dict) {
1584 if (comp->steps[step].name == NULL) {
1585 if (comp->steps[step].ns == NULL)
1586 match = 1;
1587 else
1588 match = (comp->steps[step].ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001589 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001590 match = ((comp->steps[step].name == name) &&
1591 (comp->steps[step].ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001592 }
1593 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001594 if (comp->steps[step].name == NULL) {
1595 if (comp->steps[step].ns == NULL)
1596 match = 1;
1597 else
1598 match = xmlStrEqual(comp->steps[step].ns, ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001599 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001600 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
1601 (xmlStrEqual(comp->steps[step].ns, ns)));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001602 }
1603 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001604 if (match) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001605 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
1606 if (desc) {
1607 if (final) {
1608 ret = 1;
1609 } else {
1610 /* descending match create a new state */
1611 xmlStreamCtxtAddState(stream, step + 1,
1612 stream->level + 1);
1613 }
1614 } else {
1615 if (final) {
1616 ret = 1;
1617 } else {
1618 xmlStreamCtxtAddState(stream, step + 1,
1619 stream->level + 1);
1620 }
1621 }
1622 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001623 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001624
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001625 /*
1626 * Check creating a new state.
1627 */
1628 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001629
1630 /*
1631 * Check the start only if this is a "desc" evaluation
1632 * of if we are at the first level of evaluation.
1633 */
1634#ifdef SUPPORT_IDC
1635 desc = comp->steps[0].flags & XML_STREAM_STEP_DESC;
1636 if ((desc || (stream->level == 1)) &&
1637 (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT))) {
1638
1639 /*
1640 * Workaround for missing "self::node()" on "@foo".
1641 */
1642 if (comp->steps[0].flags & XML_STREAM_STEP_ATTR) {
1643 xmlStreamCtxtAddState(stream, 0, stream->level);
1644 goto stream_next;
1645 }
1646#else
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001647 if (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT)) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001648#endif
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001649 match = 0;
1650 if (comp->dict) {
1651 if (comp->steps[0].name == NULL) {
1652 if (comp->steps[0].ns == NULL)
1653 match = 1;
1654 else
1655 match = (comp->steps[0].ns == ns);
1656 } else {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001657#ifdef SUPPORT_IDC
1658 /*
1659 * Workaround for missing "self::node() on "foo".
1660 */
1661 if (!desc) {
1662 xmlStreamCtxtAddState(stream, 0, stream->level);
1663 goto stream_next;
1664 } else {
1665 match = ((comp->steps[0].name == name) &&
1666 (comp->steps[0].ns == ns));
1667 }
1668#else
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001669 match = ((comp->steps[0].name == name) &&
1670 (comp->steps[0].ns == ns));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001671#endif
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001672 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001673 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001674 if (comp->steps[0].name == NULL) {
1675 if (comp->steps[0].ns == NULL)
1676 match = 1;
1677 else
1678 match = xmlStrEqual(comp->steps[0].ns, ns);
1679 } else {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001680#ifdef SUPPORT_IDC
1681 /*
1682 * Workaround for missing "self::node() on "foo".
1683 */
1684 if (!desc) {
1685 xmlStreamCtxtAddState(stream, 0, stream->level);
1686 goto stream_next;
1687 } else {
1688 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1689 (xmlStrEqual(comp->steps[0].ns, ns)));
1690 }
1691#else
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001692 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1693 (xmlStrEqual(comp->steps[0].ns, ns)));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001694#endif
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001695 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001696 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001697 if (match) {
1698 if (comp->steps[0].flags & XML_STREAM_STEP_FINAL)
1699 ret = 1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001700 else
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001701 xmlStreamCtxtAddState(stream, 1, stream->level);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001702 }
1703 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001704stream_next:
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001705 stream = stream->next;
1706 } /* while stream != NULL */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001707
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001708 if (err > 0)
1709 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001710#ifdef DEBUG_STREAMING
1711 xmlDebugStreamCtxt(orig, ret);
1712#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001713 return(ret);
1714}
1715
1716/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001717 * xmlStreamPush:
1718 * @stream: the stream context
1719 * @name: the current name
1720 * @ns: the namespace name
1721 *
1722 * push new data onto the stream. NOTE: if the call xmlPatterncompile()
1723 * indicated a dictionnary, then strings for name and ns will be expected
1724 * to come from the dictionary.
1725 * Both @name and @ns being NULL means the / i.e. the root of the document.
1726 * This can also act as a reset.
1727 *
1728 * Returns: -1 in case of error, 1 if the current state in the stream is a
1729 * match and 0 otherwise.
1730 */
1731int
1732xmlStreamPush(xmlStreamCtxtPtr stream,
1733 const xmlChar *name, const xmlChar *ns) {
1734 return (xmlStreamPushInternal(stream, name, ns, XML_ELEMENT_NODE));
1735}
1736
1737/**
1738* xmlStreamPushAttr:
1739* @stream: the stream context
1740* @name: the current name
1741* @ns: the namespace name
1742*
1743* push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
1744* indicated a dictionnary, then strings for name and ns will be expected
1745* to come from the dictionary.
1746* Both @name and @ns being NULL means the / i.e. the root of the document.
1747* This can also act as a reset.
1748*
1749* Returns: -1 in case of error, 1 if the current state in the stream is a
1750* match and 0 otherwise.
1751*/
1752int
1753xmlStreamPushAttr(xmlStreamCtxtPtr stream,
1754 const xmlChar *name, const xmlChar *ns) {
1755 return (xmlStreamPushInternal(stream, name, ns, XML_ATTRIBUTE_NODE));
1756}
1757
1758/**
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001759 * xmlStreamPop:
1760 * @stream: the stream context
1761 *
1762 * push one level from the stream.
1763 *
1764 * Returns: -1 in case of error, 0 otherwise.
1765 */
1766int
1767xmlStreamPop(xmlStreamCtxtPtr stream) {
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001768 int i, m;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001769 int ret;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001770
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001771 if (stream == NULL)
1772 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001773 ret = 0;
1774 while (stream != NULL) {
1775 stream->level--;
1776 if (stream->level < 0)
1777 ret = -1;
1778
1779 /*
1780 * Check evolution of existing states
1781 */
1782 m = stream->nbState;
1783 for (i = 0;i < m;i++) {
1784 if (stream->states[(2 * i)] < 0) break;
1785 /* discard obsoleted states */
1786 if (stream->states[(2 * i) + 1] > stream->level)
1787 stream->states[(2 * i)] = -1;
1788 }
1789 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001790 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001791 return(0);
1792}
1793
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001794/************************************************************************
1795 * *
1796 * The public interfaces *
1797 * *
1798 ************************************************************************/
1799
1800/**
1801 * xmlPatterncompile:
1802 * @pattern: the pattern to compile
1803 * @dict: an optional dictionnary for interned strings
1804 * @flags: compilation flags, undefined yet
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001805 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001806 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001807 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001808 *
1809 * Returns the compiled for of the pattern or NULL in case of error
1810 */
1811xmlPatternPtr
Daniel Veillard427174f2003-12-10 10:42:59 +00001812xmlPatterncompile(const xmlChar *pattern, xmlDict *dict,
1813 int flags ATTRIBUTE_UNUSED,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001814 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001815 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001816 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001817 const xmlChar *or, *start;
1818 xmlChar *tmp = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001819
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001820 if (pattern == NULL)
1821 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001822
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001823 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001824 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001825 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001826 tmp = NULL;
1827 while ((*or != 0) && (*or != '|')) or++;
1828 if (*or == 0)
1829 ctxt = xmlNewPatParserContext(start, dict, namespaces);
1830 else {
1831 tmp = xmlStrndup(start, or - start);
1832 if (tmp != NULL) {
1833 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
1834 }
1835 or++;
1836 }
1837 if (ctxt == NULL) goto error;
1838 cur = xmlNewPattern();
1839 if (cur == NULL) goto error;
1840 if (ret == NULL)
1841 ret = cur;
1842 else {
1843 cur->next = ret->next;
1844 ret->next = cur;
1845 }
1846 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001847
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001848 xmlCompilePathPattern(ctxt);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001849 if (ctxt->error != 0)
1850 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001851 xmlFreePatParserContext(ctxt);
1852
1853
1854 xmlStreamCompile(cur);
1855 if (xmlReversePattern(cur) < 0)
1856 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001857 if (tmp != NULL) {
1858 xmlFree(tmp);
1859 tmp = NULL;
1860 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001861 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001862 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001863 return(ret);
1864error:
1865 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
1866 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001867 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001868 return(NULL);
1869}
1870
1871/**
1872 * xmlPatternMatch:
1873 * @comp: the precompiled pattern
1874 * @node: a node
1875 *
1876 * Test wether the node matches the pattern
1877 *
1878 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1879 */
1880int
1881xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
1882{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001883 int ret = 0;
1884
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001885 if ((comp == NULL) || (node == NULL))
1886 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001887
1888 while (comp != NULL) {
1889 ret = xmlPatMatch(comp, node);
1890 if (ret != 0)
1891 return(ret);
1892 comp = comp->next;
1893 }
1894 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001895}
1896
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001897/**
1898 * xmlPatternGetStreamCtxt:
1899 * @comp: the precompiled pattern
1900 *
1901 * Get a streaming context for that pattern
1902 * Use xmlFreeStreamCtxt to free the context.
1903 *
1904 * Returns a pointer to the context or NULL in case of failure
1905 */
1906xmlStreamCtxtPtr
1907xmlPatternGetStreamCtxt(xmlPatternPtr comp)
1908{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001909 xmlStreamCtxtPtr ret = NULL, cur;
1910
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001911 if ((comp == NULL) || (comp->stream == NULL))
1912 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001913
1914 while (comp != NULL) {
1915 if (comp->stream == NULL)
1916 goto failed;
1917 cur = xmlNewStreamCtxt(comp->stream);
1918 if (cur == NULL)
1919 goto failed;
1920 if (ret == NULL)
1921 ret = cur;
1922 else {
1923 cur->next = ret->next;
1924 ret->next = cur;
1925 }
1926 comp = comp->next;
1927 }
1928 return(ret);
1929failed:
1930 xmlFreeStreamCtxt(ret);
1931 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001932}
1933
Daniel Veillard56de87e2005-02-16 00:22:29 +00001934/**
1935 * xmlPatternStreamable:
1936 * @comp: the precompiled pattern
1937 *
1938 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
1939 * should work.
1940 *
1941 * Returns 1 if streamable, 0 if not and -1 in case of error.
1942 */
1943int
1944xmlPatternStreamable(xmlPatternPtr comp) {
1945 if (comp == NULL)
1946 return(-1);
1947 while (comp != NULL) {
1948 if (comp->stream == NULL)
1949 return(0);
1950 comp = comp->next;
1951 }
1952 return(1);
1953}
1954
1955/**
1956 * xmlPatternMaxDepth:
1957 * @comp: the precompiled pattern
1958 *
1959 * Check the maximum depth reachable by a pattern
1960 *
1961 * Returns -2 if no limit (using //), otherwise the depth,
1962 * and -1 in case of error
1963 */
1964int
1965xmlPatternMaxDepth(xmlPatternPtr comp) {
1966 int ret = 0, i;
1967 if (comp == NULL)
1968 return(-1);
1969 while (comp != NULL) {
1970 if (comp->stream == NULL)
1971 return(-1);
1972 for (i = 0;i < comp->stream->nbStep;i++)
1973 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
1974 return(-2);
1975 if (comp->stream->nbStep > ret)
1976 ret = comp->stream->nbStep;
1977 comp = comp->next;
1978 }
1979 return(ret);
1980
1981}
1982
1983/**
1984 * xmlPatternFromRoot:
1985 * @comp: the precompiled pattern
1986 *
1987 * Check if the pattern must be looked at from the root.
1988 *
1989 * Returns 1 if true, 0 if false and -1 in case of error
1990 */
1991int
1992xmlPatternFromRoot(xmlPatternPtr comp) {
1993 if (comp == NULL)
1994 return(-1);
1995 while (comp != NULL) {
1996 if (comp->stream == NULL)
1997 return(-1);
1998 if (comp->flags & PAT_FROM_ROOT)
1999 return(1);
2000 comp = comp->next;
2001 }
2002 return(0);
2003
2004}
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002005
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002006#endif /* LIBXML_PATTERN_ENABLED */