Coverage Report

Created: 2023-03-26 06:14

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