blob: c28bf29f11599334a512d3fefde17996021e84a9 [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 Veillardf9d16912005-01-30 22:36:30 +000022 * - handling of disjunction "pattern1 | pattern2" mean needed to build
23 * and check a list internally
Daniel Veillardd4301ab2005-02-03 22:24:10 +000024 * - get rid of the "compile" starting with lowercase
25 * - get rid of the Strdup/Strndup in case of dictionary
Daniel Veillardf9d16912005-01-30 22:36:30 +000026 */
27
Daniel Veillardb3de70c2003-12-02 22:32:15 +000028#define IN_LIBXML
29#include "libxml.h"
30
31#include <string.h>
32#include <libxml/xmlmemory.h>
33#include <libxml/tree.h>
34#include <libxml/hash.h>
35#include <libxml/dict.h>
36#include <libxml/xmlerror.h>
37#include <libxml/parserInternals.h>
38#include <libxml/pattern.h>
39
Daniel Veillardd4301ab2005-02-03 22:24:10 +000040#ifdef LIBXML_PATTERN_ENABLED
Daniel Veillardb3de70c2003-12-02 22:32:15 +000041
Daniel Veillardd4301ab2005-02-03 22:24:10 +000042/* #define DEBUG_STREAMING */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000043
Daniel Veillardb3de70c2003-12-02 22:32:15 +000044#define ERROR(a, b, c, d)
45#define ERROR5(a, b, c, d, e)
46
Daniel Veillard2fc6df92005-01-30 18:42:55 +000047#define XML_STREAM_STEP_DESC 1
48#define XML_STREAM_STEP_FINAL 2
49#define XML_STREAM_STEP_ROOT 4
50
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
119struct _xmlPattern {
120 void *data; /* the associated template */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000121 xmlDictPtr dict; /* the optional dictionnary */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000122 struct _xmlPattern *next; /* next pattern if | is used */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000123 const xmlChar *pattern; /* the pattern */
124
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000125 int nbStep;
126 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000127 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000128 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000129};
130
131typedef struct _xmlPatParserContext xmlPatParserContext;
132typedef xmlPatParserContext *xmlPatParserContextPtr;
133struct _xmlPatParserContext {
134 const xmlChar *cur; /* the current char being parsed */
135 const xmlChar *base; /* the full expression */
136 int error; /* error code */
137 xmlDictPtr dict; /* the dictionnary if any */
138 xmlPatternPtr comp; /* the result */
139 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000140 const xmlChar **namespaces; /* the namespaces definitions */
141 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000142};
143
144/************************************************************************
145 * *
146 * Type functions *
147 * *
148 ************************************************************************/
149
150/**
151 * xmlNewPattern:
152 *
153 * Create a new XSLT Pattern
154 *
155 * Returns the newly allocated xmlPatternPtr or NULL in case of error
156 */
157static xmlPatternPtr
158xmlNewPattern(void) {
159 xmlPatternPtr cur;
160
161 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
162 if (cur == NULL) {
163 ERROR(NULL, NULL, NULL,
164 "xmlNewPattern : malloc failed\n");
165 return(NULL);
166 }
167 memset(cur, 0, sizeof(xmlPattern));
168 cur->maxStep = 10;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000169 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
170 if (cur->steps == NULL) {
171 xmlFree(cur);
172 ERROR(NULL, NULL, NULL,
173 "xmlNewPattern : malloc failed\n");
174 return(NULL);
175 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000176 return(cur);
177}
178
179/**
180 * xmlFreePattern:
181 * @comp: an XSLT comp
182 *
183 * Free up the memory allocated by @comp
184 */
185void
186xmlFreePattern(xmlPatternPtr comp) {
187 xmlStepOpPtr op;
188 int i;
189
190 if (comp == NULL)
191 return;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000192 if (comp->next != NULL)
193 xmlFreePattern(comp->next);
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000194 if (comp->stream != NULL)
195 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000196 if (comp->pattern != NULL)
197 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000198 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000199 if (comp->dict == NULL) {
200 for (i = 0;i < comp->nbStep;i++) {
201 op = &comp->steps[i];
202 if (op->value != NULL)
203 xmlFree((xmlChar *) op->value);
204 if (op->value2 != NULL)
205 xmlFree((xmlChar *) op->value2);
206 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000207 }
208 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000209 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000210 if (comp->dict != NULL)
211 xmlDictFree(comp->dict);
212
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000213 memset(comp, -1, sizeof(xmlPattern));
214 xmlFree(comp);
215}
216
217/**
218 * xmlFreePatternList:
219 * @comp: an XSLT comp list
220 *
221 * Free up the memory allocated by all the elements of @comp
222 */
223void
224xmlFreePatternList(xmlPatternPtr comp) {
225 xmlPatternPtr cur;
226
227 while (comp != NULL) {
228 cur = comp;
229 comp = comp->next;
230 xmlFreePattern(cur);
231 }
232}
233
234/**
235 * xmlNewPatParserContext:
236 * @pattern: the pattern context
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000237 * @dict: the inherited dictionnary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000238 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
239 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000240 *
241 * Create a new XML pattern parser context
242 *
243 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
244 */
245static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000246xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
247 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000248 xmlPatParserContextPtr cur;
249
250 if (pattern == NULL)
251 return(NULL);
252
253 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
254 if (cur == NULL) {
255 ERROR(NULL, NULL, NULL,
256 "xmlNewPatParserContext : malloc failed\n");
257 return(NULL);
258 }
259 memset(cur, 0, sizeof(xmlPatParserContext));
260 cur->dict = dict;
261 cur->cur = pattern;
262 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000263 if (namespaces != NULL) {
264 int i;
265 for (i = 0;namespaces[2 * i] != NULL;i++);
266 cur->nb_namespaces = i;
267 } else {
268 cur->nb_namespaces = 0;
269 }
270 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000271 return(cur);
272}
273
274/**
275 * xmlFreePatParserContext:
276 * @ctxt: an XSLT parser context
277 *
278 * Free up the memory allocated by @ctxt
279 */
280static void
281xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
282 if (ctxt == NULL)
283 return;
284 memset(ctxt, -1, sizeof(xmlPatParserContext));
285 xmlFree(ctxt);
286}
287
288/**
289 * xmlPatternAdd:
290 * @comp: the compiled match expression
291 * @op: an op
292 * @value: the first value
293 * @value2: the second value
294 *
295 * Add an step to an XSLT Compiled Match
296 *
297 * Returns -1 in case of failure, 0 otherwise.
298 */
299static int
300xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
301 xmlPatternPtr comp,
302 xmlPatOp op, xmlChar * value, xmlChar * value2)
303{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000304 if (comp->nbStep >= comp->maxStep) {
305 xmlStepOpPtr temp;
306 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
307 sizeof(xmlStepOp));
308 if (temp == NULL) {
309 ERROR(ctxt, NULL, NULL,
310 "xmlPatternAdd: realloc failed\n");
311 return (-1);
312 }
313 comp->steps = temp;
314 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000315 }
316 comp->steps[comp->nbStep].op = op;
317 comp->steps[comp->nbStep].value = value;
318 comp->steps[comp->nbStep].value2 = value2;
319 comp->nbStep++;
320 return (0);
321}
322
323#if 0
324/**
325 * xsltSwapTopPattern:
326 * @comp: the compiled match expression
327 *
328 * reverse the two top steps.
329 */
330static void
331xsltSwapTopPattern(xmlPatternPtr comp) {
332 int i;
333 int j = comp->nbStep - 1;
334
335 if (j > 0) {
336 register const xmlChar *tmp;
337 register xmlPatOp op;
338 i = j - 1;
339 tmp = comp->steps[i].value;
340 comp->steps[i].value = comp->steps[j].value;
341 comp->steps[j].value = tmp;
342 tmp = comp->steps[i].value2;
343 comp->steps[i].value2 = comp->steps[j].value2;
344 comp->steps[j].value2 = tmp;
345 op = comp->steps[i].op;
346 comp->steps[i].op = comp->steps[j].op;
347 comp->steps[j].op = op;
348 }
349}
350#endif
351
352/**
353 * xmlReversePattern:
354 * @comp: the compiled match expression
355 *
356 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000357 *
358 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000359 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000360static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000361xmlReversePattern(xmlPatternPtr comp) {
362 int i = 0;
363 int j = comp->nbStep - 1;
364
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000365 if (comp->nbStep >= comp->maxStep) {
366 xmlStepOpPtr temp;
367 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
368 sizeof(xmlStepOp));
369 if (temp == NULL) {
370 ERROR(ctxt, NULL, NULL,
371 "xmlReversePattern: realloc failed\n");
372 return (-1);
373 }
374 comp->steps = temp;
375 comp->maxStep *= 2;
376 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000377 while (j > i) {
378 register const xmlChar *tmp;
379 register xmlPatOp op;
380 tmp = comp->steps[i].value;
381 comp->steps[i].value = comp->steps[j].value;
382 comp->steps[j].value = tmp;
383 tmp = comp->steps[i].value2;
384 comp->steps[i].value2 = comp->steps[j].value2;
385 comp->steps[j].value2 = tmp;
386 op = comp->steps[i].op;
387 comp->steps[i].op = comp->steps[j].op;
388 comp->steps[j].op = op;
389 j--;
390 i++;
391 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000392 comp->steps[comp->nbStep].value = NULL;
393 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000394 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000395 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000396}
397
398/************************************************************************
399 * *
400 * The interpreter for the precompiled patterns *
401 * *
402 ************************************************************************/
403
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000404static int
405xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
406 if ((states->states == NULL) || (states->maxstates <= 0)) {
407 states->maxstates = 4;
408 states->nbstates = 0;
409 states->states = xmlMalloc(4 * sizeof(xmlStepState));
410 }
411 else if (states->maxstates <= states->nbstates) {
412 xmlStepState *tmp;
413
414 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
415 2 * states->maxstates * sizeof(xmlStepState));
416 if (tmp == NULL)
417 return(-1);
418 states->states = tmp;
419 states->maxstates *= 2;
420 }
421 states->states[states->nbstates].step = step;
422 states->states[states->nbstates++].node = node;
423#if 0
424 fprintf(stderr, "Push: %d, %s\n", step, node->name);
425#endif
426 return(0);
427}
428
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000429/**
430 * xmlPatMatch:
431 * @comp: the precompiled pattern
432 * @node: a node
433 *
434 * Test wether the node matches the pattern
435 *
436 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
437 */
438static int
439xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
440 int i;
441 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000442 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000443
444 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000445 i = 0;
446restart:
447 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000448 step = &comp->steps[i];
449 switch (step->op) {
450 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000451 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000452 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000453 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000454 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000455 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000456 if ((node->type == XML_DOCUMENT_NODE) ||
457#ifdef LIBXML_DOCB_ENABLED
458 (node->type == XML_DOCB_DOCUMENT_NODE) ||
459#endif
460 (node->type == XML_HTML_DOCUMENT_NODE))
461 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000462 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000463 case XML_OP_ELEM:
464 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000465 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000466 if (step->value == NULL)
467 continue;
468 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000469 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000470 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000471 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000472
473 /* Namespace test */
474 if (node->ns == NULL) {
475 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000476 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000477 } else if (node->ns->href != NULL) {
478 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000479 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000480 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000481 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000482 }
483 continue;
484 case XML_OP_CHILD: {
485 xmlNodePtr lst;
486
487 if ((node->type != XML_ELEMENT_NODE) &&
488 (node->type != XML_DOCUMENT_NODE) &&
489#ifdef LIBXML_DOCB_ENABLED
490 (node->type != XML_DOCB_DOCUMENT_NODE) &&
491#endif
492 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000493 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000494
495 lst = node->children;
496
497 if (step->value != NULL) {
498 while (lst != NULL) {
499 if ((lst->type == XML_ELEMENT_NODE) &&
500 (step->value[0] == lst->name[0]) &&
501 (xmlStrEqual(step->value, lst->name)))
502 break;
503 lst = lst->next;
504 }
505 if (lst != NULL)
506 continue;
507 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000508 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000509 }
510 case XML_OP_ATTR:
511 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000512 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000513 if (step->value != NULL) {
514 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000515 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000516 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000517 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000518 }
519 /* Namespace test */
520 if (node->ns == NULL) {
521 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000522 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000523 } else if (step->value2 != NULL) {
524 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000525 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000526 }
527 continue;
528 case XML_OP_PARENT:
529 if ((node->type == XML_DOCUMENT_NODE) ||
530 (node->type == XML_HTML_DOCUMENT_NODE) ||
531#ifdef LIBXML_DOCB_ENABLED
532 (node->type == XML_DOCB_DOCUMENT_NODE) ||
533#endif
534 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000535 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000536 node = node->parent;
537 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000538 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000539 if (step->value == NULL)
540 continue;
541 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000542 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000543 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000544 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000545 /* Namespace test */
546 if (node->ns == NULL) {
547 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000548 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000549 } else if (node->ns->href != NULL) {
550 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000551 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000552 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000553 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000554 }
555 continue;
556 case XML_OP_ANCESTOR:
557 /* TODO: implement coalescing of ANCESTOR/NODE ops */
558 if (step->value == NULL) {
559 i++;
560 step = &comp->steps[i];
561 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000562 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000563 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000564 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000565 if (step->value == NULL)
566 return(-1);
567 }
568 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000569 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000570 if ((node->type == XML_DOCUMENT_NODE) ||
571 (node->type == XML_HTML_DOCUMENT_NODE) ||
572#ifdef LIBXML_DOCB_ENABLED
573 (node->type == XML_DOCB_DOCUMENT_NODE) ||
574#endif
575 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000576 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000577 node = node->parent;
578 while (node != NULL) {
579 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000580 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000581 if ((node->type == XML_ELEMENT_NODE) &&
582 (step->value[0] == node->name[0]) &&
583 (xmlStrEqual(step->value, node->name))) {
584 /* Namespace test */
585 if (node->ns == NULL) {
586 if (step->value2 == NULL)
587 break;
588 } else if (node->ns->href != NULL) {
589 if ((step->value2 != NULL) &&
590 (xmlStrEqual(step->value2, node->ns->href)))
591 break;
592 }
593 }
594 node = node->parent;
595 }
596 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000597 goto rollback;
598 /*
599 * prepare a potential rollback from here
600 * for ancestors of that node.
601 */
602 if (step->op == XML_OP_ANCESTOR)
603 xmlPatPushState(&states, i, node);
604 else
605 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000606 continue;
607 case XML_OP_NS:
608 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000609 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000610 if (node->ns == NULL) {
611 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000612 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000613 } else if (node->ns->href != NULL) {
614 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000615 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000616 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000617 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000618 }
619 break;
620 case XML_OP_ALL:
621 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000622 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000623 break;
624 }
625 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000626found:
627 if (states.states != NULL) {
628 /* Free the rollback states */
629 xmlFree(states.states);
630 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000631 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000632rollback:
633 /* got an error try to rollback */
634 if (states.states == NULL)
635 return(0);
636 if (states.nbstates <= 0) {
637 xmlFree(states.states);
638 return(0);
639 }
640 states.nbstates--;
641 i = states.states[states.nbstates].step;
642 node = states.states[states.nbstates].node;
643#if 0
644 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
645#endif
646 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000647}
648
649/************************************************************************
650 * *
651 * Dedicated parser for templates *
652 * *
653 ************************************************************************/
654
655#define TODO \
656 xmlGenericError(xmlGenericErrorContext, \
657 "Unimplemented block at %s:%d\n", \
658 __FILE__, __LINE__);
659#define CUR (*ctxt->cur)
660#define SKIP(val) ctxt->cur += (val)
661#define NXT(val) ctxt->cur[(val)]
662#define CUR_PTR ctxt->cur
663
664#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000665 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000666
667#define CURRENT (*ctxt->cur)
668#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
669
670
671#define PUSH(op, val, val2) \
672 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
673
674#define XSLT_ERROR(X) \
675 { xsltError(ctxt, __FILE__, __LINE__, X); \
676 ctxt->error = (X); return; }
677
678#define XSLT_ERROR0(X) \
679 { xsltError(ctxt, __FILE__, __LINE__, X); \
680 ctxt->error = (X); return(0); }
681
682#if 0
683/**
684 * xmlPatScanLiteral:
685 * @ctxt: the XPath Parser context
686 *
687 * Parse an XPath Litteral:
688 *
689 * [29] Literal ::= '"' [^"]* '"'
690 * | "'" [^']* "'"
691 *
692 * Returns the Literal parsed or NULL
693 */
694
695static xmlChar *
696xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
697 const xmlChar *q, *cur;
698 xmlChar *ret = NULL;
699 int val, len;
700
701 SKIP_BLANKS;
702 if (CUR == '"') {
703 NEXT;
704 cur = q = CUR_PTR;
705 val = xmlStringCurrentChar(NULL, cur, &len);
706 while ((IS_CHAR(val)) && (val != '"')) {
707 cur += len;
708 val = xmlStringCurrentChar(NULL, cur, &len);
709 }
710 if (!IS_CHAR(val)) {
711 ctxt->error = 1;
712 return(NULL);
713 } else {
714 ret = xmlStrndup(q, cur - q);
715 }
716 cur += len;
717 CUR_PTR = cur;
718 } else 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 {
735 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
736 ctxt->error = 1;
737 return(NULL);
738 }
739 return(ret);
740}
741#endif
742
743/**
744 * xmlPatScanName:
745 * @ctxt: the XPath Parser context
746 *
747 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
748 * CombiningChar | Extender
749 *
750 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
751 *
752 * [6] Names ::= Name (S Name)*
753 *
754 * Returns the Name parsed or NULL
755 */
756
757static xmlChar *
758xmlPatScanName(xmlPatParserContextPtr ctxt) {
759 const xmlChar *q, *cur;
760 xmlChar *ret = NULL;
761 int val, len;
762
763 SKIP_BLANKS;
764
765 cur = q = CUR_PTR;
766 val = xmlStringCurrentChar(NULL, cur, &len);
767 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
768 return(NULL);
769
770 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
771 (val == '.') || (val == '-') ||
772 (val == '_') ||
773 (IS_COMBINING(val)) ||
774 (IS_EXTENDER(val))) {
775 cur += len;
776 val = xmlStringCurrentChar(NULL, cur, &len);
777 }
778 ret = xmlStrndup(q, cur - q);
779 CUR_PTR = cur;
780 return(ret);
781}
782
783/**
784 * xmlPatScanNCName:
785 * @ctxt: the XPath Parser context
786 *
787 * Parses a non qualified name
788 *
789 * Returns the Name parsed or NULL
790 */
791
792static xmlChar *
793xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
794 const xmlChar *q, *cur;
795 xmlChar *ret = NULL;
796 int val, len;
797
798 SKIP_BLANKS;
799
800 cur = q = CUR_PTR;
801 val = xmlStringCurrentChar(NULL, cur, &len);
802 if (!IS_LETTER(val) && (val != '_'))
803 return(NULL);
804
805 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
806 (val == '.') || (val == '-') ||
807 (val == '_') ||
808 (IS_COMBINING(val)) ||
809 (IS_EXTENDER(val))) {
810 cur += len;
811 val = xmlStringCurrentChar(NULL, cur, &len);
812 }
813 ret = xmlStrndup(q, cur - q);
814 CUR_PTR = cur;
815 return(ret);
816}
817
818#if 0
819/**
820 * xmlPatScanQName:
821 * @ctxt: the XPath Parser context
822 * @prefix: the place to store the prefix
823 *
824 * Parse a qualified name
825 *
826 * Returns the Name parsed or NULL
827 */
828
829static xmlChar *
830xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
831 xmlChar *ret = NULL;
832
833 *prefix = NULL;
834 ret = xmlPatScanNCName(ctxt);
835 if (CUR == ':') {
836 *prefix = ret;
837 NEXT;
838 ret = xmlPatScanNCName(ctxt);
839 }
840 return(ret);
841}
842#endif
843
844/**
845 * xmlCompileStepPattern:
846 * @ctxt: the compilation context
847 *
848 * Compile the Step Pattern and generates a precompiled
849 * form suitable for fast matching.
850 *
851 * [3] Step ::= '.' | NameTest
852 * [4] NameTest ::= QName | '*' | NCName ':' '*'
853 */
854
855static void
856xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
857 xmlChar *token = NULL;
858 xmlChar *name = NULL;
859 const xmlChar *URI = NULL;
860 xmlChar *URL = NULL;
861
862 SKIP_BLANKS;
863 if (CUR == '.') {
864 NEXT;
865 PUSH(XML_OP_ELEM, NULL, NULL);
866 return;
867 }
868 name = xmlPatScanNCName(ctxt);
869 if (name == NULL) {
870 if (CUR == '*') {
871 NEXT;
872 PUSH(XML_OP_ALL, NULL, NULL);
873 return;
874 } else {
875 ERROR(NULL, NULL, NULL,
876 "xmlCompileStepPattern : Name expected\n");
877 ctxt->error = 1;
878 return;
879 }
880 }
881 SKIP_BLANKS;
882 if (CUR == ':') {
883 NEXT;
884 if (CUR != ':') {
885 xmlChar *prefix = name;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000886 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000887
888 /*
889 * This is a namespace match
890 */
891 token = xmlPatScanName(ctxt);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000892 for (i = 0;i < ctxt->nb_namespaces;i++) {
893 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Daniel Veillard0996a162005-02-05 14:00:10 +0000894 URL = xmlStrdup(ctxt->namespaces[2 * i]);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000895 break;
896 }
897 }
898 if (i >= ctxt->nb_namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000899 ERROR5(NULL, NULL, NULL,
900 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
901 prefix);
902 ctxt->error = 1;
903 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000904 }
905 xmlFree(prefix);
906 if (token == NULL) {
907 if (CUR == '*') {
908 NEXT;
909 PUSH(XML_OP_NS, URL, NULL);
910 } else {
911 ERROR(NULL, NULL, NULL,
912 "xmlCompileStepPattern : Name expected\n");
913 ctxt->error = 1;
914 goto error;
915 }
916 } else {
917 PUSH(XML_OP_ELEM, token, URL);
918 }
919 } else {
920 NEXT;
921 if (xmlStrEqual(token, (const xmlChar *) "child")) {
922 xmlFree(token);
923 token = xmlPatScanName(ctxt);
924 if (token == NULL) {
925 if (CUR == '*') {
926 NEXT;
927 PUSH(XML_OP_ALL, token, NULL);
928 return;
929 } else {
930 ERROR(NULL, NULL, NULL,
931 "xmlCompileStepPattern : QName expected\n");
932 ctxt->error = 1;
933 goto error;
934 }
935 }
936 TODO
937 /* URI = xsltGetQNameURI(ctxt->elem, &token); */
938 if (token == NULL) {
939 ctxt->error = 1;
940 goto error;
941 } else {
942 name = xmlStrdup(token);
943 if (URI != NULL)
944 URL = xmlStrdup(URI);
945 }
946 PUSH(XML_OP_CHILD, name, URL);
947 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
948 xmlFree(token);
949 token = xmlPatScanName(ctxt);
950 if (token == NULL) {
951 ERROR(NULL, NULL, NULL,
952 "xmlCompileStepPattern : QName expected\n");
953 ctxt->error = 1;
954 goto error;
955 }
956 TODO
957 /* URI = xsltGetQNameURI(ctxt->elem, &token); */
958 if (token == NULL) {
959 ctxt->error = 1;
960 goto error;
961 } else {
962 name = xmlStrdup(token);
963 if (URI != NULL)
964 URL = xmlStrdup(URI);
965 }
966 PUSH(XML_OP_ATTR, name, URL);
967 } else {
968 ERROR(NULL, NULL, NULL,
969 "xmlCompileStepPattern : 'child' or 'attribute' expected\n");
970 ctxt->error = 1;
971 goto error;
972 }
973 xmlFree(token);
974 }
975 } else if (CUR == '*') {
976 NEXT;
977 PUSH(XML_OP_ALL, token, NULL);
978 } else {
979 if (name == NULL) {
980 ctxt->error = 1;
981 goto error;
982 }
983 PUSH(XML_OP_ELEM, name, NULL);
984 }
985 return;
986error:
987 if (token != NULL)
988 xmlFree(token);
989 if (name != NULL)
990 xmlFree(name);
991}
992
993/**
994 * xmlCompilePathPattern:
995 * @ctxt: the compilation context
996 *
997 * Compile the Path Pattern and generates a precompiled
998 * form suitable for fast matching.
999 *
1000 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1001 */
1002static void
1003xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1004 SKIP_BLANKS;
1005 if ((CUR == '/') && (NXT(1) == '/')) {
1006 /*
1007 * since we reverse the query
1008 * a leading // can be safely ignored
1009 */
1010 NEXT;
1011 NEXT;
1012 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1013 /*
1014 * a leading .// can be safely ignored
1015 */
1016 NEXT;
1017 NEXT;
1018 NEXT;
1019 }
1020 if (CUR == '@') {
1021 TODO
1022 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001023 if (CUR == '/') {
1024 PUSH(XML_OP_ROOT, NULL, NULL);
1025 NEXT;
1026 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001027 xmlCompileStepPattern(ctxt);
1028 SKIP_BLANKS;
1029 while (CUR == '/') {
1030 if ((CUR == '/') && (NXT(1) == '/')) {
1031 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1032 NEXT;
1033 NEXT;
1034 SKIP_BLANKS;
1035 xmlCompileStepPattern(ctxt);
1036 } else {
1037 PUSH(XML_OP_PARENT, NULL, NULL);
1038 NEXT;
1039 SKIP_BLANKS;
1040 if ((CUR != 0) || (CUR == '|')) {
1041 xmlCompileStepPattern(ctxt);
1042 }
1043 }
1044 }
1045 }
1046error:
1047 return;
1048}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001049
1050/************************************************************************
1051 * *
1052 * The streaming code *
1053 * *
1054 ************************************************************************/
1055
1056#ifdef DEBUG_STREAMING
1057static void
1058xmlDebugStreamComp(xmlStreamCompPtr stream) {
1059 int i;
1060
1061 if (stream == NULL) {
1062 printf("Stream: NULL\n");
1063 return;
1064 }
1065 printf("Stream: %d steps\n", stream->nbStep);
1066 for (i = 0;i < stream->nbStep;i++) {
1067 if (stream->steps[i].ns != NULL) {
1068 printf("{%s}", stream->steps[i].ns);
1069 }
1070 if (stream->steps[i].name == NULL) {
1071 printf("* ");
1072 } else {
1073 printf("%s ", stream->steps[i].name);
1074 }
1075 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1076 printf("root ");
1077 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1078 printf("// ");
1079 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1080 printf("final ");
1081 printf("\n");
1082 }
1083}
1084static void
1085xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1086 int i;
1087
1088 if (ctxt == NULL) {
1089 printf("Stream: NULL\n");
1090 return;
1091 }
1092 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1093 if (match)
1094 printf("matches\n");
1095 else
1096 printf("\n");
1097 for (i = 0;i < ctxt->nbState;i++) {
1098 if (ctxt->states[2 * i] < 0)
1099 printf(" %d: free\n", i);
1100 else {
1101 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1102 ctxt->states[(2 * i) + 1]);
1103 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1104 XML_STREAM_STEP_DESC)
1105 printf(" //\n");
1106 else
1107 printf("\n");
1108 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001109 }
1110}
1111#endif
1112/**
1113 * xmlNewStreamComp:
1114 * @size: the number of expected steps
1115 *
1116 * build a new compiled pattern for streaming
1117 *
1118 * Returns the new structure or NULL in case of error.
1119 */
1120static xmlStreamCompPtr
1121xmlNewStreamComp(int size) {
1122 xmlStreamCompPtr cur;
1123
1124 if (size < 4)
1125 size = 4;
1126
1127 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1128 if (cur == NULL) {
1129 ERROR(NULL, NULL, NULL,
1130 "xmlNewStreamComp: malloc failed\n");
1131 return(NULL);
1132 }
1133 memset(cur, 0, sizeof(xmlStreamComp));
1134 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1135 if (cur->steps == NULL) {
1136 xmlFree(cur);
1137 ERROR(NULL, NULL, NULL,
1138 "xmlNewStreamComp: malloc failed\n");
1139 return(NULL);
1140 }
1141 cur->nbStep = 0;
1142 cur->maxStep = size;
1143 return(cur);
1144}
1145
1146/**
1147 * xmlFreeStreamComp:
1148 * @comp: the compiled pattern for streaming
1149 *
1150 * Free the compiled pattern for streaming
1151 */
1152static void
1153xmlFreeStreamComp(xmlStreamCompPtr comp) {
1154 if (comp != NULL) {
1155 if (comp->steps != NULL)
1156 xmlFree(comp->steps);
1157 if (comp->dict != NULL)
1158 xmlDictFree(comp->dict);
1159 xmlFree(comp);
1160 }
1161}
1162
1163/**
1164 * xmlStreamCompAddStep:
1165 * @comp: the compiled pattern for streaming
1166 * @name: the first string, the name, or NULL for *
1167 * @ns: the second step, the namespace name
1168 * @flags: the flags for that step
1169 *
1170 * Add a new step to the compiled pattern
1171 *
1172 * Returns -1 in case of error or the step index if successful
1173 */
1174static int
1175xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1176 const xmlChar *ns, int flags) {
1177 xmlStreamStepPtr cur;
1178
1179 if (comp->nbStep >= comp->maxStep) {
1180 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1181 comp->maxStep * 2 * sizeof(xmlStreamStep));
1182 if (cur == NULL) {
1183 ERROR(NULL, NULL, NULL,
1184 "xmlNewStreamComp: malloc failed\n");
1185 return(-1);
1186 }
1187 comp->steps = cur;
1188 comp->maxStep *= 2;
1189 }
1190 cur = &comp->steps[comp->nbStep++];
1191 cur->flags = flags;
1192 cur->name = name;
1193 cur->ns = ns;
1194 return(comp->nbStep - 1);
1195}
1196
1197/**
1198 * xmlStreamCompile:
1199 * @comp: the precompiled pattern
1200 *
1201 * Tries to stream compile a pattern
1202 *
1203 * Returns -1 in case of failure and 0 in case of success.
1204 */
1205static int
1206xmlStreamCompile(xmlPatternPtr comp) {
1207 xmlStreamCompPtr stream;
1208 int i, s = 0, root = 0, desc = 0;
1209
1210 if ((comp == NULL) || (comp->steps == NULL))
1211 return(-1);
1212 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1213 if (stream == NULL)
1214 return(-1);
1215 if (comp->dict != NULL) {
1216 stream->dict = comp->dict;
1217 xmlDictReference(stream->dict);
1218 }
1219 for (i = 0;i < comp->nbStep;i++) {
1220 switch (comp->steps[i].op) {
1221 case XML_OP_END:
1222 break;
1223 case XML_OP_ROOT:
1224 if (i != 0)
1225 goto error;
1226 root = 1;
1227 break;
1228 case XML_OP_CHILD:
1229 case XML_OP_ATTR:
1230 case XML_OP_NS:
1231 goto error;
1232 case XML_OP_ELEM:
1233 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1234 comp->steps[i].value2, desc);
1235 desc = 0;
1236 if (s < 0)
1237 goto error;
1238 break;
1239 case XML_OP_ALL:
1240 s = xmlStreamCompAddStep(stream, NULL, NULL, desc);
1241 desc = 0;
1242 if (s < 0)
1243 goto error;
1244 break;
1245 case XML_OP_PARENT:
1246 break;
1247 case XML_OP_ANCESTOR:
1248 desc = XML_STREAM_STEP_DESC;
1249 break;
1250 }
1251 }
1252 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1253 if (root)
1254 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1255#ifdef DEBUG_STREAMING
1256 xmlDebugStreamComp(stream);
1257#endif
1258 comp->stream = stream;
1259 return(0);
1260error:
1261 xmlFreeStreamComp(stream);
1262 return(0);
1263}
1264
1265/**
1266 * xmlNewStreamCtxt:
1267 * @size: the number of expected states
1268 *
1269 * build a new stream context
1270 *
1271 * Returns the new structure or NULL in case of error.
1272 */
1273static xmlStreamCtxtPtr
1274xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1275 xmlStreamCtxtPtr cur;
1276
1277 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1278 if (cur == NULL) {
1279 ERROR(NULL, NULL, NULL,
1280 "xmlNewStreamCtxt: malloc failed\n");
1281 return(NULL);
1282 }
1283 memset(cur, 0, sizeof(xmlStreamCtxt));
1284 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1285 if (cur->states == NULL) {
1286 xmlFree(cur);
1287 ERROR(NULL, NULL, NULL,
1288 "xmlNewStreamCtxt: malloc failed\n");
1289 return(NULL);
1290 }
1291 cur->nbState = 0;
1292 cur->maxState = 4;
1293 cur->level = 0;
1294 cur->comp = stream;
1295 return(cur);
1296}
1297
1298/**
1299 * xmlFreeStreamCtxt:
1300 * @stream: the stream context
1301 *
1302 * Free the stream context
1303 */
1304void
1305xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
1306 if (stream != NULL) {
1307 if (stream->states != NULL)
1308 xmlFree(stream->states);
1309 xmlFree(stream);
1310 }
1311}
1312
1313/**
1314 * xmlStreamCtxtAddState:
1315 * @comp: the stream context
1316 * @idx: the step index for that streaming state
1317 *
1318 * Add a new state to the stream context
1319 *
1320 * Returns -1 in case of error or the state index if successful
1321 */
1322static int
1323xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1324 int i;
1325 for (i = 0;i < comp->nbState;i++) {
1326 if (comp->states[2 * i] < 0) {
1327 comp->states[2 * i] = idx;
1328 comp->states[2 * i + 1] = level;
1329 return(i);
1330 }
1331 }
1332 if (comp->nbState >= comp->maxState) {
1333 int *cur;
1334
1335 cur = (int *) xmlRealloc(comp->states,
1336 comp->maxState * 4 * sizeof(int));
1337 if (cur == NULL) {
1338 ERROR(NULL, NULL, NULL,
1339 "xmlNewStreamCtxt: malloc failed\n");
1340 return(-1);
1341 }
1342 comp->states = cur;
1343 comp->maxState *= 2;
1344 }
1345 comp->states[2 * comp->nbState] = idx;
1346 comp->states[2 * comp->nbState++ + 1] = level;
1347 return(comp->nbState - 1);
1348}
1349
1350/**
1351 * xmlStreamPush:
1352 * @stream: the stream context
1353 * @name: the current name
1354 * @ns: the namespace name
1355 *
1356 * push new data onto the stream. NOTE: if the call xmlPatterncompile()
1357 * indicated a dictionnary, then strings for name and ns will be expected
1358 * to come from the dictionary.
1359 * Both @name and @ns being NULL means the / i.e. the root of the document.
1360 * This can also act as a reset.
1361 *
1362 * Returns: -1 in case of error, 1 if the current state in the stream is a
1363 * match and 0 otherwise.
1364 */
1365int
1366xmlStreamPush(xmlStreamCtxtPtr stream,
1367 const xmlChar *name, const xmlChar *ns) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001368 int ret = 0, err = 0, tmp, i, m, match, step, desc, final;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001369 xmlStreamCompPtr comp;
1370
1371 if ((stream == NULL) || (stream->nbState < 0))
1372 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001373
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001374 while (stream != NULL) {
1375 comp = stream->comp;
1376 if ((name == NULL) && (ns == NULL)) {
1377 stream->nbState = 0;
1378 stream->level = 0;
1379 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1380 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1381 if (tmp < 0)
1382 err++;
1383 if (comp->steps[tmp].flags & XML_STREAM_STEP_FINAL)
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001384 ret = 1;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001385 continue; /* while */
1386 }
1387 continue; /* while */
1388 }
1389 /*
1390 * Check evolution of existing states
1391 */
1392 m = stream->nbState;
1393 for (i = 0;i < m;i++) {
1394 match = 0;
1395 step = stream->states[2 * i];
1396 /* dead states */
1397 if (step < 0) continue;
1398 /* skip new states just added */
1399 if (stream->states[(2 * i) + 1] > stream->level)
1400 continue;
1401 /* skip continuations */
1402 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1403 if ((stream->states[(2 * i) + 1] < stream->level) && (!desc))
1404 continue;
1405
1406 /* discard old states */
1407 /* something needed about old level discarded */
1408
1409 if (comp->dict) {
1410 if (comp->steps[step].name == NULL) {
1411 if (comp->steps[step].ns == NULL)
1412 match = 1;
1413 else
1414 match = (comp->steps[step].ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001415 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001416 match = ((comp->steps[step].name == name) &&
1417 (comp->steps[step].ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001418 }
1419 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001420 if (comp->steps[step].name == NULL) {
1421 if (comp->steps[step].ns == NULL)
1422 match = 1;
1423 else
1424 match = xmlStrEqual(comp->steps[step].ns, ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001425 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001426 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
1427 (xmlStrEqual(comp->steps[step].ns, ns)));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001428 }
1429 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001430 if (match) {
1431 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
1432 if (desc) {
1433 if (final) {
1434 ret = 1;
1435 } else {
1436 /* descending match create a new state */
1437 xmlStreamCtxtAddState(stream, step + 1,
1438 stream->level + 1);
1439 }
1440 } else {
1441 if (final) {
1442 ret = 1;
1443 } else {
1444 xmlStreamCtxtAddState(stream, step + 1,
1445 stream->level + 1);
1446 }
1447 }
1448 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001449 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001450
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001451 /*
1452 * Check creating a new state.
1453 */
1454 stream->level++;
1455 if (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT)) {
1456 match = 0;
1457 if (comp->dict) {
1458 if (comp->steps[0].name == NULL) {
1459 if (comp->steps[0].ns == NULL)
1460 match = 1;
1461 else
1462 match = (comp->steps[0].ns == ns);
1463 } else {
1464 match = ((comp->steps[0].name == name) &&
1465 (comp->steps[0].ns == ns));
1466 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001467 } else {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001468 if (comp->steps[0].name == NULL) {
1469 if (comp->steps[0].ns == NULL)
1470 match = 1;
1471 else
1472 match = xmlStrEqual(comp->steps[0].ns, ns);
1473 } else {
1474 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1475 (xmlStrEqual(comp->steps[0].ns, ns)));
1476 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001477 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001478 if (match) {
1479 if (comp->steps[0].flags & XML_STREAM_STEP_FINAL)
1480 ret = 1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001481 else
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001482 xmlStreamCtxtAddState(stream, 1, stream->level);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001483 }
1484 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001485
1486 stream = stream->next;
1487 } /* while stream != NULL */
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001488
1489#ifdef DEBUG_STREAMING
1490 xmlDebugStreamCtxt(stream, ret);
1491#endif
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001492 if (err > 0)
1493 ret = -1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001494 return(ret);
1495}
1496
1497/**
1498 * xmlStreamPop:
1499 * @stream: the stream context
1500 *
1501 * push one level from the stream.
1502 *
1503 * Returns: -1 in case of error, 0 otherwise.
1504 */
1505int
1506xmlStreamPop(xmlStreamCtxtPtr stream) {
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001507 int i, m;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001508 int ret;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001509
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001510 if (stream == NULL)
1511 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001512 ret = 0;
1513 while (stream != NULL) {
1514 stream->level--;
1515 if (stream->level < 0)
1516 ret = -1;
1517
1518 /*
1519 * Check evolution of existing states
1520 */
1521 m = stream->nbState;
1522 for (i = 0;i < m;i++) {
1523 if (stream->states[(2 * i)] < 0) break;
1524 /* discard obsoleted states */
1525 if (stream->states[(2 * i) + 1] > stream->level)
1526 stream->states[(2 * i)] = -1;
1527 }
1528 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001529 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001530 return(0);
1531}
1532
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001533/************************************************************************
1534 * *
1535 * The public interfaces *
1536 * *
1537 ************************************************************************/
1538
1539/**
1540 * xmlPatterncompile:
1541 * @pattern: the pattern to compile
1542 * @dict: an optional dictionnary for interned strings
1543 * @flags: compilation flags, undefined yet
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001544 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001545 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001546 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001547 *
1548 * Returns the compiled for of the pattern or NULL in case of error
1549 */
1550xmlPatternPtr
Daniel Veillard427174f2003-12-10 10:42:59 +00001551xmlPatterncompile(const xmlChar *pattern, xmlDict *dict,
1552 int flags ATTRIBUTE_UNUSED,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001553 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001554 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001555 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001556 const xmlChar *or, *start;
1557 xmlChar *tmp = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001558
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001559 if (pattern == NULL)
1560 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001561
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001562 start = pattern;
1563 while (*or != 0) {
1564 or = start;
1565 tmp = NULL;
1566 while ((*or != 0) && (*or != '|')) or++;
1567 if (*or == 0)
1568 ctxt = xmlNewPatParserContext(start, dict, namespaces);
1569 else {
1570 tmp = xmlStrndup(start, or - start);
1571 if (tmp != NULL) {
1572 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
1573 }
1574 or++;
1575 }
1576 if (ctxt == NULL) goto error;
1577 cur = xmlNewPattern();
1578 if (cur == NULL) goto error;
1579 if (ret == NULL)
1580 ret = cur;
1581 else {
1582 cur->next = ret->next;
1583 ret->next = cur;
1584 }
1585 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001586
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001587 xmlCompilePathPattern(ctxt);
1588 xmlFreePatParserContext(ctxt);
1589
1590
1591 xmlStreamCompile(cur);
1592 if (xmlReversePattern(cur) < 0)
1593 goto error;
1594 start = or;
1595 if (tmp != NULL) {
1596 xmlFree(tmp);
1597 tmp = NULL;
1598 }
1599 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001600 return(ret);
1601error:
1602 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
1603 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001604 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001605 return(NULL);
1606}
1607
1608/**
1609 * xmlPatternMatch:
1610 * @comp: the precompiled pattern
1611 * @node: a node
1612 *
1613 * Test wether the node matches the pattern
1614 *
1615 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1616 */
1617int
1618xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
1619{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001620 int ret = 0;
1621
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001622 if ((comp == NULL) || (node == NULL))
1623 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001624
1625 while (comp != NULL) {
1626 ret = xmlPatMatch(comp, node);
1627 if (ret != 0)
1628 return(ret);
1629 comp = comp->next;
1630 }
1631 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001632}
1633
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001634/**
1635 * xmlPatternGetStreamCtxt:
1636 * @comp: the precompiled pattern
1637 *
1638 * Get a streaming context for that pattern
1639 * Use xmlFreeStreamCtxt to free the context.
1640 *
1641 * Returns a pointer to the context or NULL in case of failure
1642 */
1643xmlStreamCtxtPtr
1644xmlPatternGetStreamCtxt(xmlPatternPtr comp)
1645{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001646 xmlStreamCtxtPtr ret = NULL, cur;
1647
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001648 if ((comp == NULL) || (comp->stream == NULL))
1649 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001650
1651 while (comp != NULL) {
1652 if (comp->stream == NULL)
1653 goto failed;
1654 cur = xmlNewStreamCtxt(comp->stream);
1655 if (cur == NULL)
1656 goto failed;
1657 if (ret == NULL)
1658 ret = cur;
1659 else {
1660 cur->next = ret->next;
1661 ret->next = cur;
1662 }
1663 comp = comp->next;
1664 }
1665 return(ret);
1666failed:
1667 xmlFreeStreamCtxt(ret);
1668 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001669}
1670
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001671#endif /* LIBXML_PATTERN_ENABLED */