Coverage Report

Created: 2025-08-04 07:15

/src/libxml2-2.9.7/pattern.c
Line
Count
Source (jump to first uncovered line)
1
/*
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
14
/*
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 .
22
 * - get rid of the "compile" starting with lowercase
23
 * - DONE (2006-05-16): get rid of the Strdup/Strndup in case of dictionary
24
 */
25
26
#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
38
#ifdef LIBXML_PATTERN_ENABLED
39
40
/* #define DEBUG_STREAMING */
41
42
#ifdef ERROR
43
#undef ERROR
44
#endif
45
#define ERROR(a, b, c, d)
46
#define ERROR5(a, b, c, d, e)
47
48
0
#define XML_STREAM_STEP_DESC  1
49
0
#define XML_STREAM_STEP_FINAL 2
50
0
#define XML_STREAM_STEP_ROOT  4
51
0
#define XML_STREAM_STEP_ATTR  8
52
0
#define XML_STREAM_STEP_NODE  16
53
0
#define XML_STREAM_STEP_IN_SET  32
54
55
/*
56
* NOTE: Those private flags (XML_STREAM_xxx) are used
57
*   in _xmlStreamCtxt->flag. They extend the public
58
*   xmlPatternFlags, so be carefull not to interfere with the
59
*   reserved values for xmlPatternFlags.
60
*/
61
0
#define XML_STREAM_FINAL_IS_ANY_NODE 1<<14
62
0
#define XML_STREAM_FROM_ROOT 1<<15
63
0
#define XML_STREAM_DESC 1<<16
64
65
/*
66
* XML_STREAM_ANY_NODE is used for comparison against
67
* xmlElementType enums, to indicate a node of any type.
68
*/
69
0
#define XML_STREAM_ANY_NODE 100
70
71
0
#define XML_PATTERN_NOTPATTERN  (XML_PATTERN_XPATH | \
72
0
         XML_PATTERN_XSSEL | \
73
0
         XML_PATTERN_XSFIELD)
74
75
0
#define XML_STREAM_XS_IDC(c) ((c)->flags & \
76
0
    (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
77
78
0
#define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL)
79
80
#define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD)
81
82
#define XML_PAT_COPY_NSNAME(c, r, nsname) \
83
0
    if ((c)->comp->dict) \
84
0
  r = (xmlChar *) xmlDictLookup((c)->comp->dict, BAD_CAST nsname, -1); \
85
0
    else r = xmlStrdup(BAD_CAST nsname);
86
87
0
#define XML_PAT_FREE_STRING(c, r) if ((c)->comp->dict == NULL) xmlFree(r);
88
89
typedef struct _xmlStreamStep xmlStreamStep;
90
typedef xmlStreamStep *xmlStreamStepPtr;
91
struct _xmlStreamStep {
92
    int flags;      /* properties of that step */
93
    const xmlChar *name;  /* first string value if NULL accept all */
94
    const xmlChar *ns;    /* second string value */
95
    int nodeType;   /* type of node */
96
};
97
98
typedef struct _xmlStreamComp xmlStreamComp;
99
typedef xmlStreamComp *xmlStreamCompPtr;
100
struct _xmlStreamComp {
101
    xmlDict *dict;    /* the dictionary if any */
102
    int nbStep;     /* number of steps in the automata */
103
    int maxStep;    /* allocated number of steps */
104
    xmlStreamStepPtr steps; /* the array of steps */
105
    int flags;
106
};
107
108
struct _xmlStreamCtxt {
109
    struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
110
    xmlStreamCompPtr comp;  /* the compiled stream */
111
    int nbState;    /* number of states in the automata */
112
    int maxState;   /* allocated number of states */
113
    int level;      /* how deep are we ? */
114
    int *states;    /* the array of step indexes */
115
    int flags;      /* validation options */
116
    int blockLevel;
117
};
118
119
static void xmlFreeStreamComp(xmlStreamCompPtr comp);
120
121
/*
122
 * Types are private:
123
 */
124
125
typedef enum {
126
    XML_OP_END=0,
127
    XML_OP_ROOT,
128
    XML_OP_ELEM,
129
    XML_OP_CHILD,
130
    XML_OP_ATTR,
131
    XML_OP_PARENT,
132
    XML_OP_ANCESTOR,
133
    XML_OP_NS,
134
    XML_OP_ALL
135
} xmlPatOp;
136
137
138
typedef struct _xmlStepState xmlStepState;
139
typedef xmlStepState *xmlStepStatePtr;
140
struct _xmlStepState {
141
    int step;
142
    xmlNodePtr node;
143
};
144
145
typedef struct _xmlStepStates xmlStepStates;
146
typedef xmlStepStates *xmlStepStatesPtr;
147
struct _xmlStepStates {
148
    int nbstates;
149
    int maxstates;
150
    xmlStepStatePtr states;
151
};
152
153
typedef struct _xmlStepOp xmlStepOp;
154
typedef xmlStepOp *xmlStepOpPtr;
155
struct _xmlStepOp {
156
    xmlPatOp op;
157
    const xmlChar *value;
158
    const xmlChar *value2; /* The namespace name */
159
};
160
161
0
#define PAT_FROM_ROOT (1<<8)
162
0
#define PAT_FROM_CUR  (1<<9)
163
164
struct _xmlPattern {
165
    void *data;   /* the associated template */
166
    xmlDictPtr dict;    /* the optional dictionary */
167
    struct _xmlPattern *next; /* next pattern if | is used */
168
    const xmlChar *pattern; /* the pattern */
169
    int flags;      /* flags */
170
    int nbStep;
171
    int maxStep;
172
    xmlStepOpPtr steps;        /* ops for computation */
173
    xmlStreamCompPtr stream;  /* the streaming data if any */
174
};
175
176
typedef struct _xmlPatParserContext xmlPatParserContext;
177
typedef xmlPatParserContext *xmlPatParserContextPtr;
178
struct _xmlPatParserContext {
179
    const xmlChar *cur;     /* the current char being parsed */
180
    const xmlChar *base;    /* the full expression */
181
    int            error;   /* error code */
182
    xmlDictPtr     dict;    /* the dictionary if any */
183
    xmlPatternPtr  comp;    /* the result */
184
    xmlNodePtr     elem;    /* the current node if any */
185
    const xmlChar **namespaces;   /* the namespaces definitions */
186
    int   nb_namespaces;    /* the number of namespaces */
187
};
188
189
/************************************************************************
190
 *                  *
191
 *      Type functions          *
192
 *                  *
193
 ************************************************************************/
194
195
/**
196
 * xmlNewPattern:
197
 *
198
 * Create a new XSLT Pattern
199
 *
200
 * Returns the newly allocated xmlPatternPtr or NULL in case of error
201
 */
202
static xmlPatternPtr
203
0
xmlNewPattern(void) {
204
0
    xmlPatternPtr cur;
205
206
0
    cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
207
0
    if (cur == NULL) {
208
0
  ERROR(NULL, NULL, NULL,
209
0
    "xmlNewPattern : malloc failed\n");
210
0
  return(NULL);
211
0
    }
212
0
    memset(cur, 0, sizeof(xmlPattern));
213
0
    cur->maxStep = 10;
214
0
    cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
215
0
    if (cur->steps == NULL) {
216
0
        xmlFree(cur);
217
0
  ERROR(NULL, NULL, NULL,
218
0
    "xmlNewPattern : malloc failed\n");
219
0
  return(NULL);
220
0
    }
221
0
    return(cur);
222
0
}
223
224
/**
225
 * xmlFreePattern:
226
 * @comp:  an XSLT comp
227
 *
228
 * Free up the memory allocated by @comp
229
 */
230
void
231
0
xmlFreePattern(xmlPatternPtr comp) {
232
0
    xmlStepOpPtr op;
233
0
    int i;
234
235
0
    if (comp == NULL)
236
0
  return;
237
0
    if (comp->next != NULL)
238
0
        xmlFreePattern(comp->next);
239
0
    if (comp->stream != NULL)
240
0
        xmlFreeStreamComp(comp->stream);
241
0
    if (comp->pattern != NULL)
242
0
  xmlFree((xmlChar *)comp->pattern);
243
0
    if (comp->steps != NULL) {
244
0
        if (comp->dict == NULL) {
245
0
      for (i = 0;i < comp->nbStep;i++) {
246
0
    op = &comp->steps[i];
247
0
    if (op->value != NULL)
248
0
        xmlFree((xmlChar *) op->value);
249
0
    if (op->value2 != NULL)
250
0
        xmlFree((xmlChar *) op->value2);
251
0
      }
252
0
  }
253
0
  xmlFree(comp->steps);
254
0
    }
255
0
    if (comp->dict != NULL)
256
0
        xmlDictFree(comp->dict);
257
258
0
    memset(comp, -1, sizeof(xmlPattern));
259
0
    xmlFree(comp);
260
0
}
261
262
/**
263
 * xmlFreePatternList:
264
 * @comp:  an XSLT comp list
265
 *
266
 * Free up the memory allocated by all the elements of @comp
267
 */
268
void
269
0
xmlFreePatternList(xmlPatternPtr comp) {
270
0
    xmlPatternPtr cur;
271
272
0
    while (comp != NULL) {
273
0
  cur = comp;
274
0
  comp = comp->next;
275
0
  cur->next = NULL;
276
0
  xmlFreePattern(cur);
277
0
    }
278
0
}
279
280
/**
281
 * xmlNewPatParserContext:
282
 * @pattern:  the pattern context
283
 * @dict:  the inherited dictionary or NULL
284
 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
285
 *              with [NULL, NULL] or NULL if no namespace is used
286
 *
287
 * Create a new XML pattern parser context
288
 *
289
 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
290
 */
291
static xmlPatParserContextPtr
292
xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
293
0
                       const xmlChar **namespaces) {
294
0
    xmlPatParserContextPtr cur;
295
296
0
    if (pattern == NULL)
297
0
        return(NULL);
298
299
0
    cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
300
0
    if (cur == NULL) {
301
0
  ERROR(NULL, NULL, NULL,
302
0
    "xmlNewPatParserContext : malloc failed\n");
303
0
  return(NULL);
304
0
    }
305
0
    memset(cur, 0, sizeof(xmlPatParserContext));
306
0
    cur->dict = dict;
307
0
    cur->cur = pattern;
308
0
    cur->base = pattern;
309
0
    if (namespaces != NULL) {
310
0
        int i;
311
0
        for (i = 0;namespaces[2 * i] != NULL;i++)
312
0
            ;
313
0
        cur->nb_namespaces = i;
314
0
    } else {
315
0
        cur->nb_namespaces = 0;
316
0
    }
317
0
    cur->namespaces = namespaces;
318
0
    return(cur);
319
0
}
320
321
/**
322
 * xmlFreePatParserContext:
323
 * @ctxt:  an XSLT parser context
324
 *
325
 * Free up the memory allocated by @ctxt
326
 */
327
static void
328
0
xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
329
0
    if (ctxt == NULL)
330
0
  return;
331
0
    memset(ctxt, -1, sizeof(xmlPatParserContext));
332
0
    xmlFree(ctxt);
333
0
}
334
335
/**
336
 * xmlPatternAdd:
337
 * @comp:  the compiled match expression
338
 * @op:  an op
339
 * @value:  the first value
340
 * @value2:  the second value
341
 *
342
 * Add a step to an XSLT Compiled Match
343
 *
344
 * Returns -1 in case of failure, 0 otherwise.
345
 */
346
static int
347
xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
348
                xmlPatternPtr comp,
349
                xmlPatOp op, xmlChar * value, xmlChar * value2)
350
0
{
351
0
    if (comp->nbStep >= comp->maxStep) {
352
0
        xmlStepOpPtr temp;
353
0
  temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
354
0
                                   sizeof(xmlStepOp));
355
0
        if (temp == NULL) {
356
0
      ERROR(ctxt, NULL, NULL,
357
0
           "xmlPatternAdd: realloc failed\n");
358
0
      return (-1);
359
0
  }
360
0
  comp->steps = temp;
361
0
  comp->maxStep *= 2;
362
0
    }
363
0
    comp->steps[comp->nbStep].op = op;
364
0
    comp->steps[comp->nbStep].value = value;
365
0
    comp->steps[comp->nbStep].value2 = value2;
366
0
    comp->nbStep++;
367
0
    return (0);
368
0
}
369
370
#if 0
371
/**
372
 * xsltSwapTopPattern:
373
 * @comp:  the compiled match expression
374
 *
375
 * reverse the two top steps.
376
 */
377
static void
378
xsltSwapTopPattern(xmlPatternPtr comp) {
379
    int i;
380
    int j = comp->nbStep - 1;
381
382
    if (j > 0) {
383
  register const xmlChar *tmp;
384
  register xmlPatOp op;
385
  i = j - 1;
386
  tmp = comp->steps[i].value;
387
  comp->steps[i].value = comp->steps[j].value;
388
  comp->steps[j].value = tmp;
389
  tmp = comp->steps[i].value2;
390
  comp->steps[i].value2 = comp->steps[j].value2;
391
  comp->steps[j].value2 = tmp;
392
  op = comp->steps[i].op;
393
  comp->steps[i].op = comp->steps[j].op;
394
  comp->steps[j].op = op;
395
    }
396
}
397
#endif
398
399
/**
400
 * xmlReversePattern:
401
 * @comp:  the compiled match expression
402
 *
403
 * reverse all the stack of expressions
404
 *
405
 * returns 0 in case of success and -1 in case of error.
406
 */
407
static int
408
0
xmlReversePattern(xmlPatternPtr comp) {
409
0
    int i, j;
410
411
    /*
412
     * remove the leading // for //a or .//a
413
     */
414
0
    if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
415
0
        for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
416
0
      comp->steps[i].value = comp->steps[j].value;
417
0
      comp->steps[i].value2 = comp->steps[j].value2;
418
0
      comp->steps[i].op = comp->steps[j].op;
419
0
  }
420
0
  comp->nbStep--;
421
0
    }
422
0
    if (comp->nbStep >= comp->maxStep) {
423
0
        xmlStepOpPtr temp;
424
0
  temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
425
0
                                   sizeof(xmlStepOp));
426
0
        if (temp == NULL) {
427
0
      ERROR(ctxt, NULL, NULL,
428
0
           "xmlReversePattern: realloc failed\n");
429
0
      return (-1);
430
0
  }
431
0
  comp->steps = temp;
432
0
  comp->maxStep *= 2;
433
0
    }
434
0
    i = 0;
435
0
    j = comp->nbStep - 1;
436
0
    while (j > i) {
437
0
  register const xmlChar *tmp;
438
0
  register xmlPatOp op;
439
0
  tmp = comp->steps[i].value;
440
0
  comp->steps[i].value = comp->steps[j].value;
441
0
  comp->steps[j].value = tmp;
442
0
  tmp = comp->steps[i].value2;
443
0
  comp->steps[i].value2 = comp->steps[j].value2;
444
0
  comp->steps[j].value2 = tmp;
445
0
  op = comp->steps[i].op;
446
0
  comp->steps[i].op = comp->steps[j].op;
447
0
  comp->steps[j].op = op;
448
0
  j--;
449
0
  i++;
450
0
    }
451
0
    comp->steps[comp->nbStep].value = NULL;
452
0
    comp->steps[comp->nbStep].value2 = NULL;
453
0
    comp->steps[comp->nbStep++].op = XML_OP_END;
454
0
    return(0);
455
0
}
456
457
/************************************************************************
458
 *                  *
459
 *    The interpreter for the precompiled patterns    *
460
 *                  *
461
 ************************************************************************/
462
463
static int
464
0
xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
465
0
    if ((states->states == NULL) || (states->maxstates <= 0)) {
466
0
        states->maxstates = 4;
467
0
  states->nbstates = 0;
468
0
  states->states = xmlMalloc(4 * sizeof(xmlStepState));
469
0
    }
470
0
    else if (states->maxstates <= states->nbstates) {
471
0
        xmlStepState *tmp;
472
473
0
  tmp = (xmlStepStatePtr) xmlRealloc(states->states,
474
0
             2 * states->maxstates * sizeof(xmlStepState));
475
0
  if (tmp == NULL)
476
0
      return(-1);
477
0
  states->states = tmp;
478
0
  states->maxstates *= 2;
479
0
    }
480
0
    states->states[states->nbstates].step = step;
481
0
    states->states[states->nbstates++].node = node;
482
#if 0
483
    fprintf(stderr, "Push: %d, %s\n", step, node->name);
484
#endif
485
0
    return(0);
486
0
}
487
488
/**
489
 * xmlPatMatch:
490
 * @comp: the precompiled pattern
491
 * @node: a node
492
 *
493
 * Test whether the node matches the pattern
494
 *
495
 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
496
 */
497
static int
498
0
xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
499
0
    int i;
500
0
    xmlStepOpPtr step;
501
0
    xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
502
503
0
    if ((comp == NULL) || (node == NULL)) return(-1);
504
0
    i = 0;
505
0
restart:
506
0
    for (;i < comp->nbStep;i++) {
507
0
  step = &comp->steps[i];
508
0
  switch (step->op) {
509
0
            case XML_OP_END:
510
0
    goto found;
511
0
            case XML_OP_ROOT:
512
0
    if (node->type == XML_NAMESPACE_DECL)
513
0
        goto rollback;
514
0
    node = node->parent;
515
0
    if ((node->type == XML_DOCUMENT_NODE) ||
516
0
#ifdef LIBXML_DOCB_ENABLED
517
0
        (node->type == XML_DOCB_DOCUMENT_NODE) ||
518
0
#endif
519
0
        (node->type == XML_HTML_DOCUMENT_NODE))
520
0
        continue;
521
0
    goto rollback;
522
0
            case XML_OP_ELEM:
523
0
    if (node->type != XML_ELEMENT_NODE)
524
0
        goto rollback;
525
0
    if (step->value == NULL)
526
0
        continue;
527
0
    if (step->value[0] != node->name[0])
528
0
        goto rollback;
529
0
    if (!xmlStrEqual(step->value, node->name))
530
0
        goto rollback;
531
532
    /* Namespace test */
533
0
    if (node->ns == NULL) {
534
0
        if (step->value2 != NULL)
535
0
      goto rollback;
536
0
    } else if (node->ns->href != NULL) {
537
0
        if (step->value2 == NULL)
538
0
      goto rollback;
539
0
        if (!xmlStrEqual(step->value2, node->ns->href))
540
0
      goto rollback;
541
0
    }
542
0
    continue;
543
0
            case XML_OP_CHILD: {
544
0
    xmlNodePtr lst;
545
546
0
    if ((node->type != XML_ELEMENT_NODE) &&
547
0
        (node->type != XML_DOCUMENT_NODE) &&
548
0
#ifdef LIBXML_DOCB_ENABLED
549
0
        (node->type != XML_DOCB_DOCUMENT_NODE) &&
550
0
#endif
551
0
        (node->type != XML_HTML_DOCUMENT_NODE))
552
0
        goto rollback;
553
554
0
    lst = node->children;
555
556
0
    if (step->value != NULL) {
557
0
        while (lst != NULL) {
558
0
      if ((lst->type == XML_ELEMENT_NODE) &&
559
0
          (step->value[0] == lst->name[0]) &&
560
0
          (xmlStrEqual(step->value, lst->name)))
561
0
          break;
562
0
      lst = lst->next;
563
0
        }
564
0
        if (lst != NULL)
565
0
      continue;
566
0
    }
567
0
    goto rollback;
568
0
      }
569
0
            case XML_OP_ATTR:
570
0
    if (node->type != XML_ATTRIBUTE_NODE)
571
0
        goto rollback;
572
0
    if (step->value != NULL) {
573
0
        if (step->value[0] != node->name[0])
574
0
      goto rollback;
575
0
        if (!xmlStrEqual(step->value, node->name))
576
0
      goto rollback;
577
0
    }
578
    /* Namespace test */
579
0
    if (node->ns == NULL) {
580
0
        if (step->value2 != NULL)
581
0
      goto rollback;
582
0
    } else if (step->value2 != NULL) {
583
0
        if (!xmlStrEqual(step->value2, node->ns->href))
584
0
      goto rollback;
585
0
    }
586
0
    continue;
587
0
            case XML_OP_PARENT:
588
0
    if ((node->type == XML_DOCUMENT_NODE) ||
589
0
        (node->type == XML_HTML_DOCUMENT_NODE) ||
590
0
#ifdef LIBXML_DOCB_ENABLED
591
0
        (node->type == XML_DOCB_DOCUMENT_NODE) ||
592
0
#endif
593
0
        (node->type == XML_NAMESPACE_DECL))
594
0
        goto rollback;
595
0
    node = node->parent;
596
0
    if (node == NULL)
597
0
        goto rollback;
598
0
    if (step->value == NULL)
599
0
        continue;
600
0
    if (step->value[0] != node->name[0])
601
0
        goto rollback;
602
0
    if (!xmlStrEqual(step->value, node->name))
603
0
        goto rollback;
604
    /* Namespace test */
605
0
    if (node->ns == NULL) {
606
0
        if (step->value2 != NULL)
607
0
      goto rollback;
608
0
    } else if (node->ns->href != NULL) {
609
0
        if (step->value2 == NULL)
610
0
      goto rollback;
611
0
        if (!xmlStrEqual(step->value2, node->ns->href))
612
0
      goto rollback;
613
0
    }
614
0
    continue;
615
0
            case XML_OP_ANCESTOR:
616
    /* TODO: implement coalescing of ANCESTOR/NODE ops */
617
0
    if (step->value == NULL) {
618
0
        i++;
619
0
        step = &comp->steps[i];
620
0
        if (step->op == XML_OP_ROOT)
621
0
      goto found;
622
0
        if (step->op != XML_OP_ELEM)
623
0
      goto rollback;
624
0
        if (step->value == NULL)
625
0
      return(-1);
626
0
    }
627
0
    if (node == NULL)
628
0
        goto rollback;
629
0
    if ((node->type == XML_DOCUMENT_NODE) ||
630
0
        (node->type == XML_HTML_DOCUMENT_NODE) ||
631
0
#ifdef LIBXML_DOCB_ENABLED
632
0
        (node->type == XML_DOCB_DOCUMENT_NODE) ||
633
0
#endif
634
0
        (node->type == XML_NAMESPACE_DECL))
635
0
        goto rollback;
636
0
    node = node->parent;
637
0
    while (node != NULL) {
638
0
        if ((node->type == XML_ELEMENT_NODE) &&
639
0
      (step->value[0] == node->name[0]) &&
640
0
      (xmlStrEqual(step->value, node->name))) {
641
      /* Namespace test */
642
0
      if (node->ns == NULL) {
643
0
          if (step->value2 == NULL)
644
0
        break;
645
0
      } else if (node->ns->href != NULL) {
646
0
          if ((step->value2 != NULL) &&
647
0
              (xmlStrEqual(step->value2, node->ns->href)))
648
0
        break;
649
0
      }
650
0
        }
651
0
        node = node->parent;
652
0
    }
653
0
    if (node == NULL)
654
0
        goto rollback;
655
    /*
656
     * prepare a potential rollback from here
657
     * for ancestors of that node.
658
     */
659
0
    if (step->op == XML_OP_ANCESTOR)
660
0
        xmlPatPushState(&states, i, node);
661
0
    else
662
0
        xmlPatPushState(&states, i - 1, node);
663
0
    continue;
664
0
            case XML_OP_NS:
665
0
    if (node->type != XML_ELEMENT_NODE)
666
0
        goto rollback;
667
0
    if (node->ns == NULL) {
668
0
        if (step->value != NULL)
669
0
      goto rollback;
670
0
    } else if (node->ns->href != NULL) {
671
0
        if (step->value == NULL)
672
0
      goto rollback;
673
0
        if (!xmlStrEqual(step->value, node->ns->href))
674
0
      goto rollback;
675
0
    }
676
0
    break;
677
0
            case XML_OP_ALL:
678
0
    if (node->type != XML_ELEMENT_NODE)
679
0
        goto rollback;
680
0
    break;
681
0
  }
682
0
    }
683
0
found:
684
0
    if (states.states != NULL) {
685
        /* Free the rollback states */
686
0
  xmlFree(states.states);
687
0
    }
688
0
    return(1);
689
0
rollback:
690
    /* got an error try to rollback */
691
0
    if (states.states == NULL)
692
0
  return(0);
693
0
    if (states.nbstates <= 0) {
694
0
  xmlFree(states.states);
695
0
  return(0);
696
0
    }
697
0
    states.nbstates--;
698
0
    i = states.states[states.nbstates].step;
699
0
    node = states.states[states.nbstates].node;
700
#if 0
701
    fprintf(stderr, "Pop: %d, %s\n", i, node->name);
702
#endif
703
0
    goto restart;
704
0
}
705
706
/************************************************************************
707
 *                  *
708
 *      Dedicated parser for templates      *
709
 *                  *
710
 ************************************************************************/
711
712
#define TODO                \
713
    xmlGenericError(xmlGenericErrorContext,       \
714
      "Unimplemented block at %s:%d\n",       \
715
            __FILE__, __LINE__);
716
0
#define CUR (*ctxt->cur)
717
#define SKIP(val) ctxt->cur += (val)
718
0
#define NXT(val) ctxt->cur[(val)]
719
#define PEEKPREV(val) ctxt->cur[-(val)]
720
0
#define CUR_PTR ctxt->cur
721
722
#define SKIP_BLANKS             \
723
0
    while (IS_BLANK_CH(CUR)) NEXT
724
725
#define CURRENT (*ctxt->cur)
726
0
#define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
727
728
729
#define PUSH(op, val, val2)           \
730
0
    if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
731
732
#define XSLT_ERROR(X)             \
733
    { xsltError(ctxt, __FILE__, __LINE__, X);       \
734
      ctxt->error = (X); return; }
735
736
#define XSLT_ERROR0(X)              \
737
    { xsltError(ctxt, __FILE__, __LINE__, X);       \
738
      ctxt->error = (X); return(0); }
739
740
#if 0
741
/**
742
 * xmlPatScanLiteral:
743
 * @ctxt:  the XPath Parser context
744
 *
745
 * Parse an XPath Litteral:
746
 *
747
 * [29] Literal ::= '"' [^"]* '"'
748
 *                | "'" [^']* "'"
749
 *
750
 * Returns the Literal parsed or NULL
751
 */
752
753
static xmlChar *
754
xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
755
    const xmlChar *q, *cur;
756
    xmlChar *ret = NULL;
757
    int val, len;
758
759
    SKIP_BLANKS;
760
    if (CUR == '"') {
761
        NEXT;
762
  cur = q = CUR_PTR;
763
  val = xmlStringCurrentChar(NULL, cur, &len);
764
  while ((IS_CHAR(val)) && (val != '"')) {
765
      cur += len;
766
      val = xmlStringCurrentChar(NULL, cur, &len);
767
  }
768
  if (!IS_CHAR(val)) {
769
      ctxt->error = 1;
770
      return(NULL);
771
  } else {
772
      if (ctxt->dict)
773
    ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
774
      else
775
    ret = xmlStrndup(q, cur - q);
776
        }
777
  cur += len;
778
  CUR_PTR = cur;
779
    } else if (CUR == '\'') {
780
        NEXT;
781
  cur = q = CUR_PTR;
782
  val = xmlStringCurrentChar(NULL, cur, &len);
783
  while ((IS_CHAR(val)) && (val != '\'')) {
784
      cur += len;
785
      val = xmlStringCurrentChar(NULL, cur, &len);
786
  }
787
  if (!IS_CHAR(val)) {
788
      ctxt->error = 1;
789
      return(NULL);
790
  } else {
791
      if (ctxt->dict)
792
    ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
793
      else
794
    ret = xmlStrndup(q, cur - q);
795
        }
796
  cur += len;
797
  CUR_PTR = cur;
798
    } else {
799
  /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
800
  ctxt->error = 1;
801
  return(NULL);
802
    }
803
    return(ret);
804
}
805
#endif
806
807
/**
808
 * xmlPatScanName:
809
 * @ctxt:  the XPath Parser context
810
 *
811
 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
812
 *                  CombiningChar | Extender
813
 *
814
 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
815
 *
816
 * [6] Names ::= Name (S Name)*
817
 *
818
 * Returns the Name parsed or NULL
819
 */
820
821
static xmlChar *
822
0
xmlPatScanName(xmlPatParserContextPtr ctxt) {
823
0
    const xmlChar *q, *cur;
824
0
    xmlChar *ret = NULL;
825
0
    int val, len;
826
827
0
    SKIP_BLANKS;
828
829
0
    cur = q = CUR_PTR;
830
0
    val = xmlStringCurrentChar(NULL, cur, &len);
831
0
    if (!IS_LETTER(val) && (val != '_') && (val != ':'))
832
0
  return(NULL);
833
834
0
    while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
835
0
           (val == '.') || (val == '-') ||
836
0
     (val == '_') ||
837
0
     (IS_COMBINING(val)) ||
838
0
     (IS_EXTENDER(val))) {
839
0
  cur += len;
840
0
  val = xmlStringCurrentChar(NULL, cur, &len);
841
0
    }
842
0
    if (ctxt->dict)
843
0
  ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
844
0
    else
845
0
  ret = xmlStrndup(q, cur - q);
846
0
    CUR_PTR = cur;
847
0
    return(ret);
848
0
}
849
850
/**
851
 * xmlPatScanNCName:
852
 * @ctxt:  the XPath Parser context
853
 *
854
 * Parses a non qualified name
855
 *
856
 * Returns the Name parsed or NULL
857
 */
858
859
static xmlChar *
860
0
xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
861
0
    const xmlChar *q, *cur;
862
0
    xmlChar *ret = NULL;
863
0
    int val, len;
864
865
0
    SKIP_BLANKS;
866
867
0
    cur = q = CUR_PTR;
868
0
    val = xmlStringCurrentChar(NULL, cur, &len);
869
0
    if (!IS_LETTER(val) && (val != '_'))
870
0
  return(NULL);
871
872
0
    while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
873
0
           (val == '.') || (val == '-') ||
874
0
     (val == '_') ||
875
0
     (IS_COMBINING(val)) ||
876
0
     (IS_EXTENDER(val))) {
877
0
  cur += len;
878
0
  val = xmlStringCurrentChar(NULL, cur, &len);
879
0
    }
880
0
    if (ctxt->dict)
881
0
  ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
882
0
    else
883
0
  ret = xmlStrndup(q, cur - q);
884
0
    CUR_PTR = cur;
885
0
    return(ret);
886
0
}
887
888
#if 0
889
/**
890
 * xmlPatScanQName:
891
 * @ctxt:  the XPath Parser context
892
 * @prefix:  the place to store the prefix
893
 *
894
 * Parse a qualified name
895
 *
896
 * Returns the Name parsed or NULL
897
 */
898
899
static xmlChar *
900
xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
901
    xmlChar *ret = NULL;
902
903
    *prefix = NULL;
904
    ret = xmlPatScanNCName(ctxt);
905
    if (CUR == ':') {
906
        *prefix = ret;
907
  NEXT;
908
  ret = xmlPatScanNCName(ctxt);
909
    }
910
    return(ret);
911
}
912
#endif
913
914
/**
915
 * xmlCompileAttributeTest:
916
 * @ctxt:  the compilation context
917
 *
918
 * Compile an attribute test.
919
 */
920
static void
921
0
xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
922
0
    xmlChar *token = NULL;
923
0
    xmlChar *name = NULL;
924
0
    xmlChar *URL = NULL;
925
926
0
    SKIP_BLANKS;
927
0
    name = xmlPatScanNCName(ctxt);
928
0
    if (name == NULL) {
929
0
  if (CUR == '*') {
930
0
      PUSH(XML_OP_ATTR, NULL, NULL);
931
0
      NEXT;
932
0
  } else {
933
0
      ERROR(NULL, NULL, NULL,
934
0
    "xmlCompileAttributeTest : Name expected\n");
935
0
      ctxt->error = 1;
936
0
  }
937
0
  return;
938
0
    }
939
0
    if (CUR == ':') {
940
0
  int i;
941
0
  xmlChar *prefix = name;
942
943
0
  NEXT;
944
945
0
  if (IS_BLANK_CH(CUR)) {
946
0
      ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
947
0
      XML_PAT_FREE_STRING(ctxt, prefix);
948
0
      ctxt->error = 1;
949
0
      goto error;
950
0
  }
951
  /*
952
  * This is a namespace match
953
  */
954
0
  token = xmlPatScanName(ctxt);
955
0
  if ((prefix[0] == 'x') &&
956
0
      (prefix[1] == 'm') &&
957
0
      (prefix[2] == 'l') &&
958
0
      (prefix[3] == 0))
959
0
  {
960
0
      XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
961
0
  } else {
962
0
      for (i = 0;i < ctxt->nb_namespaces;i++) {
963
0
    if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
964
0
        XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
965
0
        break;
966
0
    }
967
0
      }
968
0
      if (i >= ctxt->nb_namespaces) {
969
0
    ERROR5(NULL, NULL, NULL,
970
0
        "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
971
0
        prefix);
972
0
          XML_PAT_FREE_STRING(ctxt, prefix);
973
0
    ctxt->error = 1;
974
0
    goto error;
975
0
      }
976
0
  }
977
0
  XML_PAT_FREE_STRING(ctxt, prefix);
978
0
  if (token == NULL) {
979
0
      if (CUR == '*') {
980
0
    NEXT;
981
0
    PUSH(XML_OP_ATTR, NULL, URL);
982
0
      } else {
983
0
    ERROR(NULL, NULL, NULL,
984
0
        "xmlCompileAttributeTest : Name expected\n");
985
0
    ctxt->error = 1;
986
0
    goto error;
987
0
      }
988
0
  } else {
989
0
      PUSH(XML_OP_ATTR, token, URL);
990
0
  }
991
0
    } else {
992
0
  PUSH(XML_OP_ATTR, name, NULL);
993
0
    }
994
0
    return;
995
0
error:
996
0
    if (URL != NULL)
997
0
  XML_PAT_FREE_STRING(ctxt, URL)
998
0
    if (token != NULL)
999
0
  XML_PAT_FREE_STRING(ctxt, token);
1000
0
}
1001
1002
/**
1003
 * xmlCompileStepPattern:
1004
 * @ctxt:  the compilation context
1005
 *
1006
 * Compile the Step Pattern and generates a precompiled
1007
 * form suitable for fast matching.
1008
 *
1009
 * [3]    Step    ::=    '.' | NameTest
1010
 * [4]    NameTest    ::=    QName | '*' | NCName ':' '*'
1011
 */
1012
1013
static void
1014
0
xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
1015
0
    xmlChar *token = NULL;
1016
0
    xmlChar *name = NULL;
1017
0
    xmlChar *URL = NULL;
1018
0
    int hasBlanks = 0;
1019
1020
0
    SKIP_BLANKS;
1021
0
    if (CUR == '.') {
1022
  /*
1023
  * Context node.
1024
  */
1025
0
  NEXT;
1026
0
  PUSH(XML_OP_ELEM, NULL, NULL);
1027
0
  return;
1028
0
    }
1029
0
    if (CUR == '@') {
1030
  /*
1031
  * Attribute test.
1032
  */
1033
0
  if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1034
0
      ERROR5(NULL, NULL, NULL,
1035
0
    "Unexpected attribute axis in '%s'.\n", ctxt->base);
1036
0
      ctxt->error = 1;
1037
0
      return;
1038
0
  }
1039
0
  NEXT;
1040
0
  xmlCompileAttributeTest(ctxt);
1041
0
  if (ctxt->error != 0)
1042
0
      goto error;
1043
0
  return;
1044
0
    }
1045
0
    name = xmlPatScanNCName(ctxt);
1046
0
    if (name == NULL) {
1047
0
  if (CUR == '*') {
1048
0
      NEXT;
1049
0
      PUSH(XML_OP_ALL, NULL, NULL);
1050
0
      return;
1051
0
  } else {
1052
0
      ERROR(NULL, NULL, NULL,
1053
0
        "xmlCompileStepPattern : Name expected\n");
1054
0
      ctxt->error = 1;
1055
0
      return;
1056
0
  }
1057
0
    }
1058
0
    if (IS_BLANK_CH(CUR)) {
1059
0
  hasBlanks = 1;
1060
0
  SKIP_BLANKS;
1061
0
    }
1062
0
    if (CUR == ':') {
1063
0
  NEXT;
1064
0
  if (CUR != ':') {
1065
0
      xmlChar *prefix = name;
1066
0
      int i;
1067
1068
0
      if (hasBlanks || IS_BLANK_CH(CUR)) {
1069
0
    ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1070
0
    ctxt->error = 1;
1071
0
    goto error;
1072
0
      }
1073
      /*
1074
       * This is a namespace match
1075
       */
1076
0
      token = xmlPatScanName(ctxt);
1077
0
      if ((prefix[0] == 'x') &&
1078
0
    (prefix[1] == 'm') &&
1079
0
    (prefix[2] == 'l') &&
1080
0
    (prefix[3] == 0))
1081
0
      {
1082
0
    XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1083
0
      } else {
1084
0
    for (i = 0;i < ctxt->nb_namespaces;i++) {
1085
0
        if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1086
0
      XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1087
0
      break;
1088
0
        }
1089
0
    }
1090
0
    if (i >= ctxt->nb_namespaces) {
1091
0
        ERROR5(NULL, NULL, NULL,
1092
0
      "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1093
0
      prefix);
1094
0
        ctxt->error = 1;
1095
0
        goto error;
1096
0
    }
1097
0
      }
1098
0
      XML_PAT_FREE_STRING(ctxt, prefix);
1099
0
      name = NULL;
1100
0
      if (token == NULL) {
1101
0
    if (CUR == '*') {
1102
0
        NEXT;
1103
0
        PUSH(XML_OP_NS, URL, NULL);
1104
0
    } else {
1105
0
        ERROR(NULL, NULL, NULL,
1106
0
          "xmlCompileStepPattern : Name expected\n");
1107
0
        ctxt->error = 1;
1108
0
        goto error;
1109
0
    }
1110
0
      } else {
1111
0
    PUSH(XML_OP_ELEM, token, URL);
1112
0
      }
1113
0
  } else {
1114
0
      NEXT;
1115
0
      if (xmlStrEqual(name, (const xmlChar *) "child")) {
1116
0
    XML_PAT_FREE_STRING(ctxt, name);
1117
0
    name = xmlPatScanName(ctxt);
1118
0
    if (name == NULL) {
1119
0
        if (CUR == '*') {
1120
0
      NEXT;
1121
0
      PUSH(XML_OP_ALL, NULL, NULL);
1122
0
      return;
1123
0
        } else {
1124
0
      ERROR(NULL, NULL, NULL,
1125
0
          "xmlCompileStepPattern : QName expected\n");
1126
0
      ctxt->error = 1;
1127
0
      goto error;
1128
0
        }
1129
0
    }
1130
0
    if (CUR == ':') {
1131
0
        xmlChar *prefix = name;
1132
0
        int i;
1133
1134
0
        NEXT;
1135
0
        if (IS_BLANK_CH(CUR)) {
1136
0
      ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1137
0
      ctxt->error = 1;
1138
0
      goto error;
1139
0
        }
1140
        /*
1141
        * This is a namespace match
1142
        */
1143
0
        token = xmlPatScanName(ctxt);
1144
0
        if ((prefix[0] == 'x') &&
1145
0
      (prefix[1] == 'm') &&
1146
0
      (prefix[2] == 'l') &&
1147
0
      (prefix[3] == 0))
1148
0
        {
1149
0
      XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1150
0
        } else {
1151
0
      for (i = 0;i < ctxt->nb_namespaces;i++) {
1152
0
          if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1153
0
        XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1154
0
        break;
1155
0
          }
1156
0
      }
1157
0
      if (i >= ctxt->nb_namespaces) {
1158
0
          ERROR5(NULL, NULL, NULL,
1159
0
        "xmlCompileStepPattern : no namespace bound "
1160
0
        "to prefix %s\n", prefix);
1161
0
          ctxt->error = 1;
1162
0
          goto error;
1163
0
      }
1164
0
        }
1165
0
        XML_PAT_FREE_STRING(ctxt, prefix);
1166
0
        name = NULL;
1167
0
        if (token == NULL) {
1168
0
      if (CUR == '*') {
1169
0
          NEXT;
1170
0
          PUSH(XML_OP_NS, URL, NULL);
1171
0
      } else {
1172
0
          ERROR(NULL, NULL, NULL,
1173
0
        "xmlCompileStepPattern : Name expected\n");
1174
0
          ctxt->error = 1;
1175
0
          goto error;
1176
0
      }
1177
0
        } else {
1178
0
      PUSH(XML_OP_CHILD, token, URL);
1179
0
        }
1180
0
    } else
1181
0
        PUSH(XML_OP_CHILD, name, NULL);
1182
0
    return;
1183
0
      } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1184
0
    XML_PAT_FREE_STRING(ctxt, name)
1185
0
    name = NULL;
1186
0
    if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1187
0
        ERROR5(NULL, NULL, NULL,
1188
0
      "Unexpected attribute axis in '%s'.\n", ctxt->base);
1189
0
        ctxt->error = 1;
1190
0
        goto error;
1191
0
    }
1192
0
    xmlCompileAttributeTest(ctxt);
1193
0
    if (ctxt->error != 0)
1194
0
        goto error;
1195
0
    return;
1196
0
      } else {
1197
0
    ERROR5(NULL, NULL, NULL,
1198
0
        "The 'element' or 'attribute' axis is expected.\n", NULL);
1199
0
    ctxt->error = 1;
1200
0
    goto error;
1201
0
      }
1202
0
  }
1203
0
    } else if (CUR == '*') {
1204
0
        if (name != NULL) {
1205
0
      ctxt->error = 1;
1206
0
      goto error;
1207
0
  }
1208
0
  NEXT;
1209
0
  PUSH(XML_OP_ALL, token, NULL);
1210
0
    } else {
1211
0
  PUSH(XML_OP_ELEM, name, NULL);
1212
0
    }
1213
0
    return;
1214
0
error:
1215
0
    if (URL != NULL)
1216
0
  XML_PAT_FREE_STRING(ctxt, URL)
1217
0
    if (token != NULL)
1218
0
  XML_PAT_FREE_STRING(ctxt, token)
1219
0
    if (name != NULL)
1220
0
  XML_PAT_FREE_STRING(ctxt, name)
1221
0
}
1222
1223
/**
1224
 * xmlCompilePathPattern:
1225
 * @ctxt:  the compilation context
1226
 *
1227
 * Compile the Path Pattern and generates a precompiled
1228
 * form suitable for fast matching.
1229
 *
1230
 * [5]    Path    ::=    ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1231
 */
1232
static void
1233
0
xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1234
0
    SKIP_BLANKS;
1235
0
    if (CUR == '/') {
1236
0
        ctxt->comp->flags |= PAT_FROM_ROOT;
1237
0
    } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
1238
0
        ctxt->comp->flags |= PAT_FROM_CUR;
1239
0
    }
1240
1241
0
    if ((CUR == '/') && (NXT(1) == '/')) {
1242
0
  PUSH(XML_OP_ANCESTOR, NULL, NULL);
1243
0
  NEXT;
1244
0
  NEXT;
1245
0
    } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1246
0
  PUSH(XML_OP_ANCESTOR, NULL, NULL);
1247
0
  NEXT;
1248
0
  NEXT;
1249
0
  NEXT;
1250
  /* Check for incompleteness. */
1251
0
  SKIP_BLANKS;
1252
0
  if (CUR == 0) {
1253
0
      ERROR5(NULL, NULL, NULL,
1254
0
         "Incomplete expression '%s'.\n", ctxt->base);
1255
0
      ctxt->error = 1;
1256
0
      goto error;
1257
0
  }
1258
0
    }
1259
0
    if (CUR == '@') {
1260
0
  NEXT;
1261
0
  xmlCompileAttributeTest(ctxt);
1262
0
  SKIP_BLANKS;
1263
  /* TODO: check for incompleteness */
1264
0
  if (CUR != 0) {
1265
0
      xmlCompileStepPattern(ctxt);
1266
0
      if (ctxt->error != 0)
1267
0
    goto error;
1268
0
  }
1269
0
    } else {
1270
0
        if (CUR == '/') {
1271
0
      PUSH(XML_OP_ROOT, NULL, NULL);
1272
0
      NEXT;
1273
      /* Check for incompleteness. */
1274
0
      SKIP_BLANKS;
1275
0
      if (CUR == 0) {
1276
0
    ERROR5(NULL, NULL, NULL,
1277
0
        "Incomplete expression '%s'.\n", ctxt->base);
1278
0
    ctxt->error = 1;
1279
0
    goto error;
1280
0
      }
1281
0
  }
1282
0
  xmlCompileStepPattern(ctxt);
1283
0
  if (ctxt->error != 0)
1284
0
      goto error;
1285
0
  SKIP_BLANKS;
1286
0
  while (CUR == '/') {
1287
0
      if (NXT(1) == '/') {
1288
0
          PUSH(XML_OP_ANCESTOR, NULL, NULL);
1289
0
    NEXT;
1290
0
    NEXT;
1291
0
    SKIP_BLANKS;
1292
0
    xmlCompileStepPattern(ctxt);
1293
0
    if (ctxt->error != 0)
1294
0
        goto error;
1295
0
      } else {
1296
0
          PUSH(XML_OP_PARENT, NULL, NULL);
1297
0
    NEXT;
1298
0
    SKIP_BLANKS;
1299
0
    if (CUR == 0) {
1300
0
        ERROR5(NULL, NULL, NULL,
1301
0
        "Incomplete expression '%s'.\n", ctxt->base);
1302
0
        ctxt->error = 1;
1303
0
        goto error;
1304
0
    }
1305
0
    xmlCompileStepPattern(ctxt);
1306
0
    if (ctxt->error != 0)
1307
0
        goto error;
1308
0
      }
1309
0
  }
1310
0
    }
1311
0
    if (CUR != 0) {
1312
0
  ERROR5(NULL, NULL, NULL,
1313
0
         "Failed to compile pattern %s\n", ctxt->base);
1314
0
  ctxt->error = 1;
1315
0
    }
1316
0
error:
1317
0
    return;
1318
0
}
1319
1320
/**
1321
 * xmlCompileIDCXPathPath:
1322
 * @ctxt:  the compilation context
1323
 *
1324
 * Compile the Path Pattern and generates a precompiled
1325
 * form suitable for fast matching.
1326
 *
1327
 * [5]    Path    ::=    ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1328
 */
1329
static void
1330
0
xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
1331
0
    SKIP_BLANKS;
1332
0
    if (CUR == '/') {
1333
0
  ERROR5(NULL, NULL, NULL,
1334
0
      "Unexpected selection of the document root in '%s'.\n",
1335
0
      ctxt->base);
1336
0
  goto error;
1337
0
    }
1338
0
    ctxt->comp->flags |= PAT_FROM_CUR;
1339
1340
0
    if (CUR == '.') {
1341
  /* "." - "self::node()" */
1342
0
  NEXT;
1343
0
  SKIP_BLANKS;
1344
0
  if (CUR == 0) {
1345
      /*
1346
      * Selection of the context node.
1347
      */
1348
0
      PUSH(XML_OP_ELEM, NULL, NULL);
1349
0
      return;
1350
0
  }
1351
0
  if (CUR != '/') {
1352
      /* TODO: A more meaningful error message. */
1353
0
      ERROR5(NULL, NULL, NULL,
1354
0
      "Unexpected token after '.' in '%s'.\n", ctxt->base);
1355
0
      goto error;
1356
0
  }
1357
  /* "./" - "self::node()/" */
1358
0
  NEXT;
1359
0
  SKIP_BLANKS;
1360
0
  if (CUR == '/') {
1361
0
      if (IS_BLANK_CH(PEEKPREV(1))) {
1362
    /*
1363
    * Disallow "./ /"
1364
    */
1365
0
    ERROR5(NULL, NULL, NULL,
1366
0
        "Unexpected '/' token in '%s'.\n", ctxt->base);
1367
0
    goto error;
1368
0
      }
1369
      /* ".//" - "self:node()/descendant-or-self::node()/" */
1370
0
      PUSH(XML_OP_ANCESTOR, NULL, NULL);
1371
0
      NEXT;
1372
0
      SKIP_BLANKS;
1373
0
  }
1374
0
  if (CUR == 0)
1375
0
      goto error_unfinished;
1376
0
    }
1377
    /*
1378
    * Process steps.
1379
    */
1380
0
    do {
1381
0
  xmlCompileStepPattern(ctxt);
1382
0
  if (ctxt->error != 0)
1383
0
      goto error;
1384
0
  SKIP_BLANKS;
1385
0
  if (CUR != '/')
1386
0
      break;
1387
0
  PUSH(XML_OP_PARENT, NULL, NULL);
1388
0
  NEXT;
1389
0
  SKIP_BLANKS;
1390
0
  if (CUR == '/') {
1391
      /*
1392
      * Disallow subsequent '//'.
1393
      */
1394
0
      ERROR5(NULL, NULL, NULL,
1395
0
    "Unexpected subsequent '//' in '%s'.\n",
1396
0
    ctxt->base);
1397
0
      goto error;
1398
0
  }
1399
0
  if (CUR == 0)
1400
0
      goto error_unfinished;
1401
1402
0
    } while (CUR != 0);
1403
1404
0
    if (CUR != 0) {
1405
0
  ERROR5(NULL, NULL, NULL,
1406
0
      "Failed to compile expression '%s'.\n", ctxt->base);
1407
0
  ctxt->error = 1;
1408
0
    }
1409
0
    return;
1410
0
error:
1411
0
    ctxt->error = 1;
1412
0
    return;
1413
1414
0
error_unfinished:
1415
0
    ctxt->error = 1;
1416
0
    ERROR5(NULL, NULL, NULL,
1417
0
  "Unfinished expression '%s'.\n", ctxt->base);
1418
0
    return;
1419
0
}
1420
1421
/************************************************************************
1422
 *                  *
1423
 *      The streaming code        *
1424
 *                  *
1425
 ************************************************************************/
1426
1427
#ifdef DEBUG_STREAMING
1428
static void
1429
xmlDebugStreamComp(xmlStreamCompPtr stream) {
1430
    int i;
1431
1432
    if (stream == NULL) {
1433
        printf("Stream: NULL\n");
1434
  return;
1435
    }
1436
    printf("Stream: %d steps\n", stream->nbStep);
1437
    for (i = 0;i < stream->nbStep;i++) {
1438
  if (stream->steps[i].ns != NULL) {
1439
      printf("{%s}", stream->steps[i].ns);
1440
  }
1441
        if (stream->steps[i].name == NULL) {
1442
      printf("* ");
1443
  } else {
1444
      printf("%s ", stream->steps[i].name);
1445
  }
1446
  if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1447
      printf("root ");
1448
  if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1449
      printf("// ");
1450
  if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1451
      printf("final ");
1452
  printf("\n");
1453
    }
1454
}
1455
static void
1456
xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1457
    int i;
1458
1459
    if (ctxt == NULL) {
1460
        printf("Stream: NULL\n");
1461
  return;
1462
    }
1463
    printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1464
    if (match)
1465
        printf("matches\n");
1466
    else
1467
        printf("\n");
1468
    for (i = 0;i < ctxt->nbState;i++) {
1469
        if (ctxt->states[2 * i] < 0)
1470
      printf(" %d: free\n", i);
1471
  else {
1472
      printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1473
             ctxt->states[(2 * i) + 1]);
1474
            if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1475
          XML_STREAM_STEP_DESC)
1476
          printf(" //\n");
1477
      else
1478
          printf("\n");
1479
  }
1480
    }
1481
}
1482
#endif
1483
/**
1484
 * xmlNewStreamComp:
1485
 * @size: the number of expected steps
1486
 *
1487
 * build a new compiled pattern for streaming
1488
 *
1489
 * Returns the new structure or NULL in case of error.
1490
 */
1491
static xmlStreamCompPtr
1492
0
xmlNewStreamComp(int size) {
1493
0
    xmlStreamCompPtr cur;
1494
1495
0
    if (size < 4)
1496
0
        size  = 4;
1497
1498
0
    cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1499
0
    if (cur == NULL) {
1500
0
  ERROR(NULL, NULL, NULL,
1501
0
    "xmlNewStreamComp: malloc failed\n");
1502
0
  return(NULL);
1503
0
    }
1504
0
    memset(cur, 0, sizeof(xmlStreamComp));
1505
0
    cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1506
0
    if (cur->steps == NULL) {
1507
0
  xmlFree(cur);
1508
0
  ERROR(NULL, NULL, NULL,
1509
0
        "xmlNewStreamComp: malloc failed\n");
1510
0
  return(NULL);
1511
0
    }
1512
0
    cur->nbStep = 0;
1513
0
    cur->maxStep = size;
1514
0
    return(cur);
1515
0
}
1516
1517
/**
1518
 * xmlFreeStreamComp:
1519
 * @comp: the compiled pattern for streaming
1520
 *
1521
 * Free the compiled pattern for streaming
1522
 */
1523
static void
1524
0
xmlFreeStreamComp(xmlStreamCompPtr comp) {
1525
0
    if (comp != NULL) {
1526
0
        if (comp->steps != NULL)
1527
0
      xmlFree(comp->steps);
1528
0
  if (comp->dict != NULL)
1529
0
      xmlDictFree(comp->dict);
1530
0
        xmlFree(comp);
1531
0
    }
1532
0
}
1533
1534
/**
1535
 * xmlStreamCompAddStep:
1536
 * @comp: the compiled pattern for streaming
1537
 * @name: the first string, the name, or NULL for *
1538
 * @ns: the second step, the namespace name
1539
 * @flags: the flags for that step
1540
 *
1541
 * Add a new step to the compiled pattern
1542
 *
1543
 * Returns -1 in case of error or the step index if successful
1544
 */
1545
static int
1546
xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1547
0
                     const xmlChar *ns, int nodeType, int flags) {
1548
0
    xmlStreamStepPtr cur;
1549
1550
0
    if (comp->nbStep >= comp->maxStep) {
1551
0
  cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1552
0
         comp->maxStep * 2 * sizeof(xmlStreamStep));
1553
0
  if (cur == NULL) {
1554
0
      ERROR(NULL, NULL, NULL,
1555
0
      "xmlNewStreamComp: malloc failed\n");
1556
0
      return(-1);
1557
0
  }
1558
0
  comp->steps = cur;
1559
0
        comp->maxStep *= 2;
1560
0
    }
1561
0
    cur = &comp->steps[comp->nbStep++];
1562
0
    cur->flags = flags;
1563
0
    cur->name = name;
1564
0
    cur->ns = ns;
1565
0
    cur->nodeType = nodeType;
1566
0
    return(comp->nbStep - 1);
1567
0
}
1568
1569
/**
1570
 * xmlStreamCompile:
1571
 * @comp: the precompiled pattern
1572
 *
1573
 * Tries to stream compile a pattern
1574
 *
1575
 * Returns -1 in case of failure and 0 in case of success.
1576
 */
1577
static int
1578
0
xmlStreamCompile(xmlPatternPtr comp) {
1579
0
    xmlStreamCompPtr stream;
1580
0
    int i, s = 0, root = 0, flags = 0, prevs = -1;
1581
0
    xmlStepOp step;
1582
1583
0
    if ((comp == NULL) || (comp->steps == NULL))
1584
0
        return(-1);
1585
    /*
1586
     * special case for .
1587
     */
1588
0
    if ((comp->nbStep == 1) &&
1589
0
        (comp->steps[0].op == XML_OP_ELEM) &&
1590
0
  (comp->steps[0].value == NULL) &&
1591
0
  (comp->steps[0].value2 == NULL)) {
1592
0
  stream = xmlNewStreamComp(0);
1593
0
  if (stream == NULL)
1594
0
      return(-1);
1595
  /* Note that the stream will have no steps in this case. */
1596
0
  stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1597
0
  comp->stream = stream;
1598
0
  return(0);
1599
0
    }
1600
1601
0
    stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1602
0
    if (stream == NULL)
1603
0
        return(-1);
1604
0
    if (comp->dict != NULL) {
1605
0
        stream->dict = comp->dict;
1606
0
  xmlDictReference(stream->dict);
1607
0
    }
1608
1609
0
    i = 0;
1610
0
    if (comp->flags & PAT_FROM_ROOT)
1611
0
  stream->flags |= XML_STREAM_FROM_ROOT;
1612
1613
0
    for (;i < comp->nbStep;i++) {
1614
0
  step = comp->steps[i];
1615
0
        switch (step.op) {
1616
0
      case XML_OP_END:
1617
0
          break;
1618
0
      case XML_OP_ROOT:
1619
0
          if (i != 0)
1620
0
        goto error;
1621
0
    root = 1;
1622
0
    break;
1623
0
      case XML_OP_NS:
1624
0
    s = xmlStreamCompAddStep(stream, NULL, step.value,
1625
0
        XML_ELEMENT_NODE, flags);
1626
0
    if (s < 0)
1627
0
        goto error;
1628
0
    prevs = s;
1629
0
    flags = 0;
1630
0
    break;
1631
0
      case XML_OP_ATTR:
1632
0
    flags |= XML_STREAM_STEP_ATTR;
1633
0
    prevs = -1;
1634
0
    s = xmlStreamCompAddStep(stream,
1635
0
        step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
1636
0
    flags = 0;
1637
0
    if (s < 0)
1638
0
        goto error;
1639
0
    break;
1640
0
      case XML_OP_ELEM:
1641
0
          if ((step.value == NULL) && (step.value2 == NULL)) {
1642
        /*
1643
        * We have a "." or "self::node()" here.
1644
        * Eliminate redundant self::node() tests like in "/./."
1645
        * or "//./"
1646
        * The only case we won't eliminate is "//.", i.e. if
1647
        * self::node() is the last node test and we had
1648
        * continuation somewhere beforehand.
1649
        */
1650
0
        if ((comp->nbStep == i + 1) &&
1651
0
      (flags & XML_STREAM_STEP_DESC)) {
1652
      /*
1653
      * Mark the special case where the expression resolves
1654
      * to any type of node.
1655
      */
1656
0
      if (comp->nbStep == i + 1) {
1657
0
          stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1658
0
      }
1659
0
      flags |= XML_STREAM_STEP_NODE;
1660
0
      s = xmlStreamCompAddStep(stream, NULL, NULL,
1661
0
          XML_STREAM_ANY_NODE, flags);
1662
0
      if (s < 0)
1663
0
          goto error;
1664
0
      flags = 0;
1665
      /*
1666
      * If there was a previous step, mark it to be added to
1667
      * the result node-set; this is needed since only
1668
      * the last step will be marked as "final" and only
1669
      * "final" nodes are added to the resulting set.
1670
      */
1671
0
      if (prevs != -1) {
1672
0
          stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1673
0
          prevs = -1;
1674
0
      }
1675
0
      break;
1676
1677
0
        } else {
1678
      /* Just skip this one. */
1679
0
      continue;
1680
0
        }
1681
0
    }
1682
    /* An element node. */
1683
0
          s = xmlStreamCompAddStep(stream, step.value, step.value2,
1684
0
        XML_ELEMENT_NODE, flags);
1685
0
    if (s < 0)
1686
0
        goto error;
1687
0
    prevs = s;
1688
0
    flags = 0;
1689
0
    break;
1690
0
      case XML_OP_CHILD:
1691
    /* An element node child. */
1692
0
          s = xmlStreamCompAddStep(stream, step.value, step.value2,
1693
0
        XML_ELEMENT_NODE, flags);
1694
0
    if (s < 0)
1695
0
        goto error;
1696
0
    prevs = s;
1697
0
    flags = 0;
1698
0
    break;
1699
0
      case XML_OP_ALL:
1700
0
          s = xmlStreamCompAddStep(stream, NULL, NULL,
1701
0
        XML_ELEMENT_NODE, flags);
1702
0
    if (s < 0)
1703
0
        goto error;
1704
0
    prevs = s;
1705
0
    flags = 0;
1706
0
    break;
1707
0
      case XML_OP_PARENT:
1708
0
          break;
1709
0
      case XML_OP_ANCESTOR:
1710
    /* Skip redundant continuations. */
1711
0
    if (flags & XML_STREAM_STEP_DESC)
1712
0
        break;
1713
0
          flags |= XML_STREAM_STEP_DESC;
1714
    /*
1715
    * Mark the expression as having "//".
1716
    */
1717
0
    if ((stream->flags & XML_STREAM_DESC) == 0)
1718
0
        stream->flags |= XML_STREAM_DESC;
1719
0
    break;
1720
0
  }
1721
0
    }
1722
0
    if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1723
  /*
1724
  * If this should behave like a real pattern, we will mark
1725
  * the first step as having "//", to be reentrant on every
1726
  * tree level.
1727
  */
1728
0
  if ((stream->flags & XML_STREAM_DESC) == 0)
1729
0
      stream->flags |= XML_STREAM_DESC;
1730
1731
0
  if (stream->nbStep > 0) {
1732
0
      if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0)
1733
0
    stream->steps[0].flags |= XML_STREAM_STEP_DESC;
1734
0
  }
1735
0
    }
1736
0
    if (stream->nbStep <= s)
1737
0
  goto error;
1738
0
    stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1739
0
    if (root)
1740
0
  stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1741
#ifdef DEBUG_STREAMING
1742
    xmlDebugStreamComp(stream);
1743
#endif
1744
0
    comp->stream = stream;
1745
0
    return(0);
1746
0
error:
1747
0
    xmlFreeStreamComp(stream);
1748
0
    return(0);
1749
0
}
1750
1751
/**
1752
 * xmlNewStreamCtxt:
1753
 * @size: the number of expected states
1754
 *
1755
 * build a new stream context
1756
 *
1757
 * Returns the new structure or NULL in case of error.
1758
 */
1759
static xmlStreamCtxtPtr
1760
0
xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1761
0
    xmlStreamCtxtPtr cur;
1762
1763
0
    cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1764
0
    if (cur == NULL) {
1765
0
  ERROR(NULL, NULL, NULL,
1766
0
    "xmlNewStreamCtxt: malloc failed\n");
1767
0
  return(NULL);
1768
0
    }
1769
0
    memset(cur, 0, sizeof(xmlStreamCtxt));
1770
0
    cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1771
0
    if (cur->states == NULL) {
1772
0
  xmlFree(cur);
1773
0
  ERROR(NULL, NULL, NULL,
1774
0
        "xmlNewStreamCtxt: malloc failed\n");
1775
0
  return(NULL);
1776
0
    }
1777
0
    cur->nbState = 0;
1778
0
    cur->maxState = 4;
1779
0
    cur->level = 0;
1780
0
    cur->comp = stream;
1781
0
    cur->blockLevel = -1;
1782
0
    return(cur);
1783
0
}
1784
1785
/**
1786
 * xmlFreeStreamCtxt:
1787
 * @stream: the stream context
1788
 *
1789
 * Free the stream context
1790
 */
1791
void
1792
0
xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
1793
0
    xmlStreamCtxtPtr next;
1794
1795
0
    while (stream != NULL) {
1796
0
        next = stream->next;
1797
0
        if (stream->states != NULL)
1798
0
      xmlFree(stream->states);
1799
0
        xmlFree(stream);
1800
0
  stream = next;
1801
0
    }
1802
0
}
1803
1804
/**
1805
 * xmlStreamCtxtAddState:
1806
 * @comp: the stream context
1807
 * @idx: the step index for that streaming state
1808
 *
1809
 * Add a new state to the stream context
1810
 *
1811
 * Returns -1 in case of error or the state index if successful
1812
 */
1813
static int
1814
0
xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1815
0
    int i;
1816
0
    for (i = 0;i < comp->nbState;i++) {
1817
0
        if (comp->states[2 * i] < 0) {
1818
0
      comp->states[2 * i] = idx;
1819
0
      comp->states[2 * i + 1] = level;
1820
0
      return(i);
1821
0
  }
1822
0
    }
1823
0
    if (comp->nbState >= comp->maxState) {
1824
0
        int *cur;
1825
1826
0
  cur = (int *) xmlRealloc(comp->states,
1827
0
         comp->maxState * 4 * sizeof(int));
1828
0
  if (cur == NULL) {
1829
0
      ERROR(NULL, NULL, NULL,
1830
0
      "xmlNewStreamCtxt: malloc failed\n");
1831
0
      return(-1);
1832
0
  }
1833
0
  comp->states = cur;
1834
0
        comp->maxState *= 2;
1835
0
    }
1836
0
    comp->states[2 * comp->nbState] = idx;
1837
0
    comp->states[2 * comp->nbState++ + 1] = level;
1838
0
    return(comp->nbState - 1);
1839
0
}
1840
1841
/**
1842
 * xmlStreamPushInternal:
1843
 * @stream: the stream context
1844
 * @name: the current name
1845
 * @ns: the namespace name
1846
 * @nodeType: the type of the node
1847
 *
1848
 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1849
 * indicated a dictionary, then strings for name and ns will be expected
1850
 * to come from the dictionary.
1851
 * Both @name and @ns being NULL means the / i.e. the root of the document.
1852
 * This can also act as a reset.
1853
 *
1854
 * Returns: -1 in case of error, 1 if the current state in the stream is a
1855
 *    match and 0 otherwise.
1856
 */
1857
static int
1858
xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1859
          const xmlChar *name, const xmlChar *ns,
1860
0
          int nodeType) {
1861
0
    int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc;
1862
0
    xmlStreamCompPtr comp;
1863
0
    xmlStreamStep step;
1864
#ifdef DEBUG_STREAMING
1865
    xmlStreamCtxtPtr orig = stream;
1866
#endif
1867
1868
0
    if ((stream == NULL) || (stream->nbState < 0))
1869
0
        return(-1);
1870
1871
0
    while (stream != NULL) {
1872
0
  comp = stream->comp;
1873
1874
0
  if ((nodeType == XML_ELEMENT_NODE) &&
1875
0
      (name == NULL) && (ns == NULL)) {
1876
      /* We have a document node here (or a reset). */
1877
0
      stream->nbState = 0;
1878
0
      stream->level = 0;
1879
0
      stream->blockLevel = -1;
1880
0
      if (comp->flags & XML_STREAM_FROM_ROOT) {
1881
0
    if (comp->nbStep == 0) {
1882
        /* TODO: We have a "/." here? */
1883
0
        ret = 1;
1884
0
    } else {
1885
0
        if ((comp->nbStep == 1) &&
1886
0
      (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
1887
0
      (comp->steps[0].flags & XML_STREAM_STEP_DESC))
1888
0
        {
1889
      /*
1890
      * In the case of "//." the document node will match
1891
      * as well.
1892
      */
1893
0
      ret = 1;
1894
0
        } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1895
      /* TODO: Do we need this ? */
1896
0
      tmp = xmlStreamCtxtAddState(stream, 0, 0);
1897
0
      if (tmp < 0)
1898
0
          err++;
1899
0
        }
1900
0
    }
1901
0
      }
1902
0
      stream = stream->next;
1903
0
      continue; /* while */
1904
0
  }
1905
1906
  /*
1907
  * Fast check for ".".
1908
  */
1909
0
  if (comp->nbStep == 0) {
1910
      /*
1911
       * / and . are handled at the XPath node set creation
1912
       * level by checking min depth
1913
       */
1914
0
      if (stream->flags & XML_PATTERN_XPATH) {
1915
0
    stream = stream->next;
1916
0
    continue; /* while */
1917
0
      }
1918
      /*
1919
      * For non-pattern like evaluation like XML Schema IDCs
1920
      * or traditional XPath expressions, this will match if
1921
      * we are at the first level only, otherwise on every level.
1922
      */
1923
0
      if ((nodeType != XML_ATTRIBUTE_NODE) &&
1924
0
    (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1925
0
    (stream->level == 0))) {
1926
0
        ret = 1;
1927
0
      }
1928
0
      stream->level++;
1929
0
      goto stream_next;
1930
0
  }
1931
0
  if (stream->blockLevel != -1) {
1932
      /*
1933
      * Skip blocked expressions.
1934
      */
1935
0
      stream->level++;
1936
0
      goto stream_next;
1937
0
  }
1938
1939
0
  if ((nodeType != XML_ELEMENT_NODE) &&
1940
0
      (nodeType != XML_ATTRIBUTE_NODE) &&
1941
0
      ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) {
1942
      /*
1943
      * No need to process nodes of other types if we don't
1944
      * resolve to those types.
1945
      * TODO: Do we need to block the context here?
1946
      */
1947
0
      stream->level++;
1948
0
      goto stream_next;
1949
0
  }
1950
1951
  /*
1952
   * Check evolution of existing states
1953
   */
1954
0
  i = 0;
1955
0
  m = stream->nbState;
1956
0
  while (i < m) {
1957
0
      if ((comp->flags & XML_STREAM_DESC) == 0) {
1958
    /*
1959
    * If there is no "//", then only the last
1960
    * added state is of interest.
1961
    */
1962
0
    stepNr = stream->states[2 * (stream->nbState -1)];
1963
    /*
1964
    * TODO: Security check, should not happen, remove it.
1965
    */
1966
0
    if (stream->states[(2 * (stream->nbState -1)) + 1] <
1967
0
        stream->level) {
1968
0
        return (-1);
1969
0
    }
1970
0
    desc = 0;
1971
    /* loop-stopper */
1972
0
    i = m;
1973
0
      } else {
1974
    /*
1975
    * If there are "//", then we need to process every "//"
1976
    * occuring in the states, plus any other state for this
1977
    * level.
1978
    */
1979
0
    stepNr = stream->states[2 * i];
1980
1981
    /* TODO: should not happen anymore: dead states */
1982
0
    if (stepNr < 0)
1983
0
        goto next_state;
1984
1985
0
    tmp = stream->states[(2 * i) + 1];
1986
1987
    /* skip new states just added */
1988
0
    if (tmp > stream->level)
1989
0
        goto next_state;
1990
1991
    /* skip states at ancestor levels, except if "//" */
1992
0
    desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC;
1993
0
    if ((tmp < stream->level) && (!desc))
1994
0
        goto next_state;
1995
0
      }
1996
      /*
1997
      * Check for correct node-type.
1998
      */
1999
0
      step = comp->steps[stepNr];
2000
0
      if (step.nodeType != nodeType) {
2001
0
    if (step.nodeType == XML_ATTRIBUTE_NODE) {
2002
        /*
2003
        * Block this expression for deeper evaluation.
2004
        */
2005
0
        if ((comp->flags & XML_STREAM_DESC) == 0)
2006
0
      stream->blockLevel = stream->level +1;
2007
0
        goto next_state;
2008
0
    } else if (step.nodeType != XML_STREAM_ANY_NODE)
2009
0
        goto next_state;
2010
0
      }
2011
      /*
2012
      * Compare local/namespace-name.
2013
      */
2014
0
      match = 0;
2015
0
      if (step.nodeType == XML_STREAM_ANY_NODE) {
2016
0
    match = 1;
2017
0
      } else if (step.name == NULL) {
2018
0
    if (step.ns == NULL) {
2019
        /*
2020
        * This lets through all elements/attributes.
2021
        */
2022
0
        match = 1;
2023
0
    } else if (ns != NULL)
2024
0
        match = xmlStrEqual(step.ns, ns);
2025
0
      } else if (((step.ns != NULL) == (ns != NULL)) &&
2026
0
    (name != NULL) &&
2027
0
    (step.name[0] == name[0]) &&
2028
0
    xmlStrEqual(step.name, name) &&
2029
0
    ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2030
0
      {
2031
0
    match = 1;
2032
0
      }
2033
#if 0
2034
/*
2035
* TODO: Pointer comparison won't work, since not guaranteed that the given
2036
*  values are in the same dict; especially if it's the namespace name,
2037
*  normally coming from ns->href. We need a namespace dict mechanism !
2038
*/
2039
      } else if (comp->dict) {
2040
    if (step.name == NULL) {
2041
        if (step.ns == NULL)
2042
      match = 1;
2043
        else
2044
      match = (step.ns == ns);
2045
    } else {
2046
        match = ((step.name == name) && (step.ns == ns));
2047
    }
2048
#endif /* if 0 ------------------------------------------------------- */
2049
0
      if (match) {
2050
0
    final = step.flags & XML_STREAM_STEP_FINAL;
2051
0
    if (desc) {
2052
0
        if (final) {
2053
0
      ret = 1;
2054
0
        } else {
2055
      /* descending match create a new state */
2056
0
      xmlStreamCtxtAddState(stream, stepNr + 1,
2057
0
                            stream->level + 1);
2058
0
        }
2059
0
    } else {
2060
0
        if (final) {
2061
0
      ret = 1;
2062
0
        } else {
2063
0
      xmlStreamCtxtAddState(stream, stepNr + 1,
2064
0
                            stream->level + 1);
2065
0
        }
2066
0
    }
2067
0
    if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2068
        /*
2069
        * Check if we have a special case like "foo/bar//.", where
2070
        * "foo" is selected as well.
2071
        */
2072
0
        ret = 1;
2073
0
    }
2074
0
      }
2075
0
      if (((comp->flags & XML_STREAM_DESC) == 0) &&
2076
0
    ((! match) || final))  {
2077
    /*
2078
    * Mark this expression as blocked for any evaluation at
2079
    * deeper levels. Note that this includes "/foo"
2080
    * expressions if the *pattern* behaviour is used.
2081
    */
2082
0
    stream->blockLevel = stream->level +1;
2083
0
      }
2084
0
next_state:
2085
0
      i++;
2086
0
  }
2087
2088
0
  stream->level++;
2089
2090
  /*
2091
  * Re/enter the expression.
2092
  * Don't reenter if it's an absolute expression like "/foo",
2093
  *   except "//foo".
2094
  */
2095
0
  step = comp->steps[0];
2096
0
  if (step.flags & XML_STREAM_STEP_ROOT)
2097
0
      goto stream_next;
2098
2099
0
  desc = step.flags & XML_STREAM_STEP_DESC;
2100
0
  if (stream->flags & XML_PATTERN_NOTPATTERN) {
2101
      /*
2102
      * Re/enter the expression if it is a "descendant" one,
2103
      * or if we are at the 1st level of evaluation.
2104
      */
2105
2106
0
      if (stream->level == 1) {
2107
0
    if (XML_STREAM_XS_IDC(stream)) {
2108
        /*
2109
        * XS-IDC: The missing "self::node()" will always
2110
        * match the first given node.
2111
        */
2112
0
        goto stream_next;
2113
0
    } else
2114
0
        goto compare;
2115
0
      }
2116
      /*
2117
      * A "//" is always reentrant.
2118
      */
2119
0
      if (desc)
2120
0
    goto compare;
2121
2122
      /*
2123
      * XS-IDC: Process the 2nd level, since the missing
2124
      * "self::node()" is responsible for the 2nd level being
2125
      * the real start level.
2126
      */
2127
0
      if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
2128
0
    goto compare;
2129
2130
0
      goto stream_next;
2131
0
  }
2132
2133
0
compare:
2134
  /*
2135
  * Check expected node-type.
2136
  */
2137
0
  if (step.nodeType != nodeType) {
2138
0
      if (nodeType == XML_ATTRIBUTE_NODE)
2139
0
    goto stream_next;
2140
0
      else if (step.nodeType != XML_STREAM_ANY_NODE)
2141
0
    goto stream_next;
2142
0
  }
2143
  /*
2144
  * Compare local/namespace-name.
2145
  */
2146
0
  match = 0;
2147
0
  if (step.nodeType == XML_STREAM_ANY_NODE) {
2148
0
      match = 1;
2149
0
  } else if (step.name == NULL) {
2150
0
      if (step.ns == NULL) {
2151
    /*
2152
    * This lets through all elements/attributes.
2153
    */
2154
0
    match = 1;
2155
0
      } else if (ns != NULL)
2156
0
    match = xmlStrEqual(step.ns, ns);
2157
0
  } else if (((step.ns != NULL) == (ns != NULL)) &&
2158
0
      (name != NULL) &&
2159
0
      (step.name[0] == name[0]) &&
2160
0
      xmlStrEqual(step.name, name) &&
2161
0
      ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2162
0
  {
2163
0
      match = 1;
2164
0
  }
2165
0
  final = step.flags & XML_STREAM_STEP_FINAL;
2166
0
  if (match) {
2167
0
      if (final)
2168
0
    ret = 1;
2169
0
      else
2170
0
    xmlStreamCtxtAddState(stream, 1, stream->level);
2171
0
      if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2172
    /*
2173
    * Check if we have a special case like "foo//.", where
2174
    * "foo" is selected as well.
2175
    */
2176
0
    ret = 1;
2177
0
      }
2178
0
  }
2179
0
  if (((comp->flags & XML_STREAM_DESC) == 0) &&
2180
0
      ((! match) || final))  {
2181
      /*
2182
      * Mark this expression as blocked for any evaluation at
2183
      * deeper levels.
2184
      */
2185
0
      stream->blockLevel = stream->level;
2186
0
  }
2187
2188
0
stream_next:
2189
0
        stream = stream->next;
2190
0
    } /* while stream != NULL */
2191
2192
0
    if (err > 0)
2193
0
        ret = -1;
2194
#ifdef DEBUG_STREAMING
2195
    xmlDebugStreamCtxt(orig, ret);
2196
#endif
2197
0
    return(ret);
2198
0
}
2199
2200
/**
2201
 * xmlStreamPush:
2202
 * @stream: the stream context
2203
 * @name: the current name
2204
 * @ns: the namespace name
2205
 *
2206
 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2207
 * indicated a dictionary, then strings for name and ns will be expected
2208
 * to come from the dictionary.
2209
 * Both @name and @ns being NULL means the / i.e. the root of the document.
2210
 * This can also act as a reset.
2211
 * Otherwise the function will act as if it has been given an element-node.
2212
 *
2213
 * Returns: -1 in case of error, 1 if the current state in the stream is a
2214
 *    match and 0 otherwise.
2215
 */
2216
int
2217
xmlStreamPush(xmlStreamCtxtPtr stream,
2218
0
              const xmlChar *name, const xmlChar *ns) {
2219
0
    return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE));
2220
0
}
2221
2222
/**
2223
 * xmlStreamPushNode:
2224
 * @stream: the stream context
2225
 * @name: the current name
2226
 * @ns: the namespace name
2227
 * @nodeType: the type of the node being pushed
2228
 *
2229
 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2230
 * indicated a dictionary, then strings for name and ns will be expected
2231
 * to come from the dictionary.
2232
 * Both @name and @ns being NULL means the / i.e. the root of the document.
2233
 * This can also act as a reset.
2234
 * Different from xmlStreamPush() this function can be fed with nodes of type:
2235
 * element-, attribute-, text-, cdata-section-, comment- and
2236
 * processing-instruction-node.
2237
 *
2238
 * Returns: -1 in case of error, 1 if the current state in the stream is a
2239
 *    match and 0 otherwise.
2240
 */
2241
int
2242
xmlStreamPushNode(xmlStreamCtxtPtr stream,
2243
      const xmlChar *name, const xmlChar *ns,
2244
      int nodeType)
2245
0
{
2246
0
    return (xmlStreamPushInternal(stream, name, ns,
2247
0
  nodeType));
2248
0
}
2249
2250
/**
2251
* xmlStreamPushAttr:
2252
* @stream: the stream context
2253
* @name: the current name
2254
* @ns: the namespace name
2255
*
2256
* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2257
* indicated a dictionary, then strings for name and ns will be expected
2258
* to come from the dictionary.
2259
* Both @name and @ns being NULL means the / i.e. the root of the document.
2260
* This can also act as a reset.
2261
* Otherwise the function will act as if it has been given an attribute-node.
2262
*
2263
* Returns: -1 in case of error, 1 if the current state in the stream is a
2264
*    match and 0 otherwise.
2265
*/
2266
int
2267
xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2268
0
      const xmlChar *name, const xmlChar *ns) {
2269
0
    return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE));
2270
0
}
2271
2272
/**
2273
 * xmlStreamPop:
2274
 * @stream: the stream context
2275
 *
2276
 * push one level from the stream.
2277
 *
2278
 * Returns: -1 in case of error, 0 otherwise.
2279
 */
2280
int
2281
0
xmlStreamPop(xmlStreamCtxtPtr stream) {
2282
0
    int i, lev;
2283
2284
0
    if (stream == NULL)
2285
0
        return(-1);
2286
0
    while (stream != NULL) {
2287
  /*
2288
  * Reset block-level.
2289
  */
2290
0
  if (stream->blockLevel == stream->level)
2291
0
      stream->blockLevel = -1;
2292
2293
  /*
2294
   *  stream->level can be zero when XML_FINAL_IS_ANY_NODE is set
2295
   *  (see the thread at
2296
   *  http://mail.gnome.org/archives/xslt/2008-July/msg00027.html)
2297
   */
2298
0
  if (stream->level)
2299
0
      stream->level--;
2300
  /*
2301
   * Check evolution of existing states
2302
   */
2303
0
  for (i = stream->nbState -1; i >= 0; i--) {
2304
      /* discard obsoleted states */
2305
0
      lev = stream->states[(2 * i) + 1];
2306
0
      if (lev > stream->level)
2307
0
    stream->nbState--;
2308
0
      if (lev <= stream->level)
2309
0
    break;
2310
0
  }
2311
0
  stream = stream->next;
2312
0
    }
2313
0
    return(0);
2314
0
}
2315
2316
/**
2317
 * xmlStreamWantsAnyNode:
2318
 * @streamCtxt: the stream context
2319
 *
2320
 * Query if the streaming pattern additionally needs to be fed with
2321
 * text-, cdata-section-, comment- and processing-instruction-nodes.
2322
 * If the result is 0 then only element-nodes and attribute-nodes
2323
 * need to be pushed.
2324
 *
2325
 * Returns: 1 in case of need of nodes of the above described types,
2326
 *          0 otherwise. -1 on API errors.
2327
 */
2328
int
2329
xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
2330
0
{
2331
0
    if (streamCtxt == NULL)
2332
0
  return(-1);
2333
0
    while (streamCtxt != NULL) {
2334
0
  if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
2335
0
      return(1);
2336
0
  streamCtxt = streamCtxt->next;
2337
0
    }
2338
0
    return(0);
2339
0
}
2340
2341
/************************************************************************
2342
 *                  *
2343
 *      The public interfaces       *
2344
 *                  *
2345
 ************************************************************************/
2346
2347
/**
2348
 * xmlPatterncompile:
2349
 * @pattern: the pattern to compile
2350
 * @dict: an optional dictionary for interned strings
2351
 * @flags: compilation flags, see xmlPatternFlags
2352
 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
2353
 *
2354
 * Compile a pattern.
2355
 *
2356
 * Returns the compiled form of the pattern or NULL in case of error
2357
 */
2358
xmlPatternPtr
2359
xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
2360
0
                  const xmlChar **namespaces) {
2361
0
    xmlPatternPtr ret = NULL, cur;
2362
0
    xmlPatParserContextPtr ctxt = NULL;
2363
0
    const xmlChar *or, *start;
2364
0
    xmlChar *tmp = NULL;
2365
0
    int type = 0;
2366
0
    int streamable = 1;
2367
2368
0
    if (pattern == NULL)
2369
0
        return(NULL);
2370
2371
0
    start = pattern;
2372
0
    or = start;
2373
0
    while (*or != 0) {
2374
0
  tmp = NULL;
2375
0
  while ((*or != 0) && (*or != '|')) or++;
2376
0
        if (*or == 0)
2377
0
      ctxt = xmlNewPatParserContext(start, dict, namespaces);
2378
0
  else {
2379
0
      tmp = xmlStrndup(start, or - start);
2380
0
      if (tmp != NULL) {
2381
0
    ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2382
0
      }
2383
0
      or++;
2384
0
  }
2385
0
  if (ctxt == NULL) goto error;
2386
0
  cur = xmlNewPattern();
2387
0
  if (cur == NULL) goto error;
2388
  /*
2389
  * Assign string dict.
2390
  */
2391
0
  if (dict) {
2392
0
      cur->dict = dict;
2393
0
      xmlDictReference(dict);
2394
0
  }
2395
0
  if (ret == NULL)
2396
0
      ret = cur;
2397
0
  else {
2398
0
      cur->next = ret->next;
2399
0
      ret->next = cur;
2400
0
  }
2401
0
  cur->flags = flags;
2402
0
  ctxt->comp = cur;
2403
2404
0
  if (XML_STREAM_XS_IDC(cur))
2405
0
      xmlCompileIDCXPathPath(ctxt);
2406
0
  else
2407
0
      xmlCompilePathPattern(ctxt);
2408
0
  if (ctxt->error != 0)
2409
0
      goto error;
2410
0
  xmlFreePatParserContext(ctxt);
2411
0
  ctxt = NULL;
2412
2413
2414
0
        if (streamable) {
2415
0
      if (type == 0) {
2416
0
          type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2417
0
      } else if (type == PAT_FROM_ROOT) {
2418
0
          if (cur->flags & PAT_FROM_CUR)
2419
0
        streamable = 0;
2420
0
      } else if (type == PAT_FROM_CUR) {
2421
0
          if (cur->flags & PAT_FROM_ROOT)
2422
0
        streamable = 0;
2423
0
      }
2424
0
  }
2425
0
  if (streamable)
2426
0
      xmlStreamCompile(cur);
2427
0
  if (xmlReversePattern(cur) < 0)
2428
0
      goto error;
2429
0
  if (tmp != NULL) {
2430
0
      xmlFree(tmp);
2431
0
      tmp = NULL;
2432
0
  }
2433
0
  start = or;
2434
0
    }
2435
0
    if (streamable == 0) {
2436
0
        cur = ret;
2437
0
  while (cur != NULL) {
2438
0
      if (cur->stream != NULL) {
2439
0
    xmlFreeStreamComp(cur->stream);
2440
0
    cur->stream = NULL;
2441
0
      }
2442
0
      cur = cur->next;
2443
0
  }
2444
0
    }
2445
2446
0
    return(ret);
2447
0
error:
2448
0
    if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2449
0
    if (ret != NULL) xmlFreePattern(ret);
2450
0
    if (tmp != NULL) xmlFree(tmp);
2451
0
    return(NULL);
2452
0
}
2453
2454
/**
2455
 * xmlPatternMatch:
2456
 * @comp: the precompiled pattern
2457
 * @node: a node
2458
 *
2459
 * Test whether the node matches the pattern
2460
 *
2461
 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2462
 */
2463
int
2464
xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2465
0
{
2466
0
    int ret = 0;
2467
2468
0
    if ((comp == NULL) || (node == NULL))
2469
0
        return(-1);
2470
2471
0
    while (comp != NULL) {
2472
0
        ret = xmlPatMatch(comp, node);
2473
0
  if (ret != 0)
2474
0
      return(ret);
2475
0
  comp = comp->next;
2476
0
    }
2477
0
    return(ret);
2478
0
}
2479
2480
/**
2481
 * xmlPatternGetStreamCtxt:
2482
 * @comp: the precompiled pattern
2483
 *
2484
 * Get a streaming context for that pattern
2485
 * Use xmlFreeStreamCtxt to free the context.
2486
 *
2487
 * Returns a pointer to the context or NULL in case of failure
2488
 */
2489
xmlStreamCtxtPtr
2490
xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2491
0
{
2492
0
    xmlStreamCtxtPtr ret = NULL, cur;
2493
2494
0
    if ((comp == NULL) || (comp->stream == NULL))
2495
0
        return(NULL);
2496
2497
0
    while (comp != NULL) {
2498
0
        if (comp->stream == NULL)
2499
0
      goto failed;
2500
0
  cur = xmlNewStreamCtxt(comp->stream);
2501
0
  if (cur == NULL)
2502
0
      goto failed;
2503
0
  if (ret == NULL)
2504
0
      ret = cur;
2505
0
  else {
2506
0
      cur->next = ret->next;
2507
0
      ret->next = cur;
2508
0
  }
2509
0
  cur->flags = comp->flags;
2510
0
  comp = comp->next;
2511
0
    }
2512
0
    return(ret);
2513
0
failed:
2514
0
    xmlFreeStreamCtxt(ret);
2515
0
    return(NULL);
2516
0
}
2517
2518
/**
2519
 * xmlPatternStreamable:
2520
 * @comp: the precompiled pattern
2521
 *
2522
 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2523
 * should work.
2524
 *
2525
 * Returns 1 if streamable, 0 if not and -1 in case of error.
2526
 */
2527
int
2528
0
xmlPatternStreamable(xmlPatternPtr comp) {
2529
0
    if (comp == NULL)
2530
0
        return(-1);
2531
0
    while (comp != NULL) {
2532
0
        if (comp->stream == NULL)
2533
0
      return(0);
2534
0
  comp = comp->next;
2535
0
    }
2536
0
    return(1);
2537
0
}
2538
2539
/**
2540
 * xmlPatternMaxDepth:
2541
 * @comp: the precompiled pattern
2542
 *
2543
 * Check the maximum depth reachable by a pattern
2544
 *
2545
 * Returns -2 if no limit (using //), otherwise the depth,
2546
 *         and -1 in case of error
2547
 */
2548
int
2549
0
xmlPatternMaxDepth(xmlPatternPtr comp) {
2550
0
    int ret = 0, i;
2551
0
    if (comp == NULL)
2552
0
        return(-1);
2553
0
    while (comp != NULL) {
2554
0
        if (comp->stream == NULL)
2555
0
      return(-1);
2556
0
  for (i = 0;i < comp->stream->nbStep;i++)
2557
0
      if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2558
0
          return(-2);
2559
0
  if (comp->stream->nbStep > ret)
2560
0
      ret = comp->stream->nbStep;
2561
0
  comp = comp->next;
2562
0
    }
2563
0
    return(ret);
2564
0
}
2565
2566
/**
2567
 * xmlPatternMinDepth:
2568
 * @comp: the precompiled pattern
2569
 *
2570
 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2571
 * part of the set.
2572
 *
2573
 * Returns -1 in case of error otherwise the depth,
2574
 *
2575
 */
2576
int
2577
0
xmlPatternMinDepth(xmlPatternPtr comp) {
2578
0
    int ret = 12345678;
2579
0
    if (comp == NULL)
2580
0
        return(-1);
2581
0
    while (comp != NULL) {
2582
0
        if (comp->stream == NULL)
2583
0
      return(-1);
2584
0
  if (comp->stream->nbStep < ret)
2585
0
      ret = comp->stream->nbStep;
2586
0
  if (ret == 0)
2587
0
      return(0);
2588
0
  comp = comp->next;
2589
0
    }
2590
0
    return(ret);
2591
0
}
2592
2593
/**
2594
 * xmlPatternFromRoot:
2595
 * @comp: the precompiled pattern
2596
 *
2597
 * Check if the pattern must be looked at from the root.
2598
 *
2599
 * Returns 1 if true, 0 if false and -1 in case of error
2600
 */
2601
int
2602
0
xmlPatternFromRoot(xmlPatternPtr comp) {
2603
0
    if (comp == NULL)
2604
0
        return(-1);
2605
0
    while (comp != NULL) {
2606
0
        if (comp->stream == NULL)
2607
0
      return(-1);
2608
0
  if (comp->flags & PAT_FROM_ROOT)
2609
0
      return(1);
2610
0
  comp = comp->next;
2611
0
    }
2612
0
    return(0);
2613
2614
0
}
2615
2616
#define bottom_pattern
2617
#include "elfgcchack.h"
2618
#endif /* LIBXML_PATTERN_ENABLED */