Coverage Report

Created: 2025-11-09 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libxml2/pattern.c
Line
Count
Source
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
 * Author: Daniel Veillard
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/pattern.h>
31
#include <libxml/xmlmemory.h>
32
#include <libxml/tree.h>
33
#include <libxml/dict.h>
34
#include <libxml/xmlerror.h>
35
#include <libxml/parserInternals.h>
36
37
#include "private/memory.h"
38
#include "private/parser.h"
39
40
#ifdef LIBXML_PATTERN_ENABLED
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
1.41M
#define XML_STREAM_STEP_DESC  1
49
246k
#define XML_STREAM_STEP_FINAL 2
50
484k
#define XML_STREAM_STEP_ROOT  4
51
23.0k
#define XML_STREAM_STEP_ATTR  8
52
2.08k
#define XML_STREAM_STEP_NODE  16
53
1.37k
#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
4.80k
#define XML_STREAM_FINAL_IS_ANY_NODE 1<<14
62
11.8k
#define XML_STREAM_FROM_ROOT 1<<15
63
691k
#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
216k
#define XML_STREAM_ANY_NODE 100
70
71
699k
#define XML_PATTERN_NOTPATTERN  (XML_PATTERN_XPATH | \
72
699k
         XML_PATTERN_XSSEL | \
73
699k
         XML_PATTERN_XSFIELD)
74
75
191k
#define XML_STREAM_XS_IDC(c) ((c)->flags & \
76
191k
    (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
77
78
37.5k
#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
25.6k
    if ((c)->comp->dict) \
84
25.6k
  r = (xmlChar *) xmlDictLookup((c)->comp->dict, BAD_CAST nsname, -1); \
85
25.6k
    else r = xmlStrdup(BAD_CAST nsname);
86
87
48.5k
#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
106k
#define PAT_FROM_ROOT (1<<8)
162
104k
#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
 * Create a new XSLT Pattern
197
 *
198
 * @returns the newly allocated xmlPattern or NULL in case of error
199
 */
200
static xmlPatternPtr
201
72.4k
xmlNewPattern(void) {
202
72.4k
    xmlPatternPtr cur;
203
204
72.4k
    cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
205
72.4k
    if (cur == NULL) {
206
8
  ERROR(NULL, NULL, NULL,
207
8
    "xmlNewPattern : malloc failed\n");
208
8
  return(NULL);
209
8
    }
210
72.4k
    memset(cur, 0, sizeof(xmlPattern));
211
72.4k
    cur->steps = NULL;
212
72.4k
    cur->maxStep = 0;
213
72.4k
    return(cur);
214
72.4k
}
215
216
/**
217
 * Free up the memory allocated by `comp`
218
 *
219
 * @param comp  an XSLT comp
220
 */
221
void
222
34.7k
xmlFreePattern(xmlPattern *comp) {
223
34.7k
    xmlFreePatternList(comp);
224
34.7k
}
225
226
static void
227
72.4k
xmlFreePatternInternal(xmlPatternPtr comp) {
228
72.4k
    xmlStepOpPtr op;
229
72.4k
    int i;
230
231
72.4k
    if (comp == NULL)
232
0
  return;
233
72.4k
    if (comp->stream != NULL)
234
43.3k
        xmlFreeStreamComp(comp->stream);
235
72.4k
    if (comp->pattern != NULL)
236
0
  xmlFree((xmlChar *)comp->pattern);
237
72.4k
    if (comp->steps != NULL) {
238
62.9k
        if (comp->dict == NULL) {
239
24.3M
      for (i = 0;i < comp->nbStep;i++) {
240
24.2M
    op = &comp->steps[i];
241
24.2M
    if (op->value != NULL)
242
6.87M
        xmlFree((xmlChar *) op->value);
243
24.2M
    if (op->value2 != NULL)
244
10.4k
        xmlFree((xmlChar *) op->value2);
245
24.2M
      }
246
62.9k
  }
247
62.9k
  xmlFree(comp->steps);
248
62.9k
    }
249
72.4k
    if (comp->dict != NULL)
250
0
        xmlDictFree(comp->dict);
251
252
72.4k
    memset(comp, -1, sizeof(xmlPattern));
253
72.4k
    xmlFree(comp);
254
72.4k
}
255
256
/**
257
 * Free up the memory allocated by all the elements of `comp`
258
 *
259
 * @param comp  an XSLT comp list
260
 */
261
void
262
34.7k
xmlFreePatternList(xmlPattern *comp) {
263
34.7k
    xmlPatternPtr cur;
264
265
107k
    while (comp != NULL) {
266
72.4k
  cur = comp;
267
72.4k
  comp = comp->next;
268
72.4k
  cur->next = NULL;
269
72.4k
  xmlFreePatternInternal(cur);
270
72.4k
    }
271
34.7k
}
272
273
/**
274
 * Create a new XML pattern parser context
275
 *
276
 * @param pattern  the pattern context
277
 * @param dict  the inherited dictionary or NULL
278
 * @param namespaces  the prefix definitions, array of [URI, prefix] terminated
279
 *              with [NULL, NULL] or NULL if no namespace is used
280
 * @returns the newly allocated xmlPatParserContext or NULL in case of error
281
 */
282
static xmlPatParserContextPtr
283
xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
284
72.4k
                       const xmlChar **namespaces) {
285
72.4k
    xmlPatParserContextPtr cur;
286
287
72.4k
    if (pattern == NULL)
288
0
        return(NULL);
289
290
72.4k
    cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
291
72.4k
    if (cur == NULL) {
292
29
  ERROR(NULL, NULL, NULL,
293
29
    "xmlNewPatParserContext : malloc failed\n");
294
29
  return(NULL);
295
29
    }
296
72.4k
    memset(cur, 0, sizeof(xmlPatParserContext));
297
72.4k
    cur->dict = dict;
298
72.4k
    cur->cur = pattern;
299
72.4k
    cur->base = pattern;
300
72.4k
    if (namespaces != NULL) {
301
66.8k
        int i;
302
130k
        for (i = 0;namespaces[2 * i] != NULL;i++)
303
63.5k
            ;
304
66.8k
        cur->nb_namespaces = i;
305
66.8k
    } else {
306
5.57k
        cur->nb_namespaces = 0;
307
5.57k
    }
308
72.4k
    cur->namespaces = namespaces;
309
72.4k
    return(cur);
310
72.4k
}
311
312
/**
313
 * Free up the memory allocated by `ctxt`
314
 *
315
 * @param ctxt  an XSLT parser context
316
 */
317
static void
318
72.4k
xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
319
72.4k
    if (ctxt == NULL)
320
0
  return;
321
72.4k
    memset(ctxt, -1, sizeof(xmlPatParserContext));
322
72.4k
    xmlFree(ctxt);
323
72.4k
}
324
325
static int
326
198k
xmlPatternGrow(xmlPatternPtr comp) {
327
198k
    xmlStepOpPtr temp;
328
198k
    int newSize;
329
330
198k
    newSize = xmlGrowCapacity(comp->maxStep, sizeof(temp[0]),
331
198k
                              10, XML_MAX_ITEMS);
332
198k
    if (newSize < 0)
333
0
        return(-1);
334
198k
    temp = xmlRealloc(comp->steps, newSize * sizeof(temp[0]));
335
198k
    if (temp == NULL)
336
38
        return(-1);
337
198k
    comp->steps = temp;
338
198k
    comp->maxStep = newSize;
339
340
198k
    return(0);
341
198k
}
342
343
/**
344
 * Add a step to an XSLT Compiled Match
345
 *
346
 * @param ctxt  the pattern parser context
347
 * @param comp  the compiled match expression
348
 * @param op  an op
349
 * @param value  the first value
350
 * @param value2  the second value
351
 * @returns -1 in case of failure, 0 otherwise.
352
 */
353
static int
354
xmlPatternAdd(xmlPatParserContextPtr ctxt, xmlPatternPtr comp,
355
              xmlPatOp op, xmlChar * value, xmlChar * value2)
356
24.2M
{
357
24.2M
    if (comp->nbStep >= comp->maxStep) {
358
163k
        if (xmlPatternGrow(comp) < 0) {
359
32
            ctxt->error = -1;
360
32
            return(-1);
361
32
        }
362
163k
    }
363
24.2M
    comp->steps[comp->nbStep].op = op;
364
24.2M
    comp->steps[comp->nbStep].value = value;
365
24.2M
    comp->steps[comp->nbStep].value2 = value2;
366
24.2M
    comp->nbStep++;
367
24.2M
    return(0);
368
24.2M
}
369
370
/**
371
 * reverse all the stack of expressions
372
 *
373
 * @param comp  the compiled match expression
374
 * @returns 0 in case of success and -1 in case of error.
375
 */
376
static int
377
45.1k
xmlReversePattern(xmlPatternPtr comp) {
378
45.1k
    int i, j;
379
380
    /*
381
     * remove the leading // for //a or .//a
382
     */
383
45.1k
    if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
384
5.85M
        for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
385
5.85M
      comp->steps[i].value = comp->steps[j].value;
386
5.85M
      comp->steps[i].value2 = comp->steps[j].value2;
387
5.85M
      comp->steps[i].op = comp->steps[j].op;
388
5.85M
  }
389
5.10k
  comp->nbStep--;
390
5.10k
    }
391
392
    /*
393
     * Grow to add OP_END later
394
     */
395
45.1k
    if (comp->nbStep >= comp->maxStep) {
396
35.0k
        if (xmlPatternGrow(comp) < 0)
397
6
            return(-1);
398
35.0k
    }
399
400
45.1k
    i = 0;
401
45.1k
    j = comp->nbStep - 1;
402
9.95M
    while (j > i) {
403
9.90M
  register const xmlChar *tmp;
404
9.90M
  register xmlPatOp op;
405
9.90M
  tmp = comp->steps[i].value;
406
9.90M
  comp->steps[i].value = comp->steps[j].value;
407
9.90M
  comp->steps[j].value = tmp;
408
9.90M
  tmp = comp->steps[i].value2;
409
9.90M
  comp->steps[i].value2 = comp->steps[j].value2;
410
9.90M
  comp->steps[j].value2 = tmp;
411
9.90M
  op = comp->steps[i].op;
412
9.90M
  comp->steps[i].op = comp->steps[j].op;
413
9.90M
  comp->steps[j].op = op;
414
9.90M
  j--;
415
9.90M
  i++;
416
9.90M
    }
417
418
45.1k
    comp->steps[comp->nbStep].value = NULL;
419
45.1k
    comp->steps[comp->nbStep].value2 = NULL;
420
45.1k
    comp->steps[comp->nbStep++].op = XML_OP_END;
421
45.1k
    return(0);
422
45.1k
}
423
424
/************************************************************************
425
 *                  *
426
 *    The interpreter for the precompiled patterns    *
427
 *                  *
428
 ************************************************************************/
429
430
static int
431
0
xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
432
0
    if (states->maxstates <= states->nbstates) {
433
0
        xmlStepState *tmp;
434
0
        int newSize;
435
436
0
        newSize = xmlGrowCapacity(states->maxstates, sizeof(tmp[0]),
437
0
                                  4, XML_MAX_ITEMS);
438
0
        if (newSize < 0)
439
0
      return(-1);
440
0
  tmp = xmlRealloc(states->states, newSize * sizeof(tmp[0]));
441
0
  if (tmp == NULL)
442
0
      return(-1);
443
0
  states->states = tmp;
444
0
  states->maxstates = newSize;
445
0
    }
446
0
    states->states[states->nbstates].step = step;
447
0
    states->states[states->nbstates++].node = node;
448
0
    return(0);
449
0
}
450
451
/**
452
 * Test whether the node matches the pattern
453
 *
454
 * @param comp  the precompiled pattern
455
 * @param node  a node
456
 * @returns 1 if it matches, 0 if it doesn't and -1 in case of failure
457
 */
458
static int
459
5.14k
xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
460
5.14k
    int i;
461
5.14k
    xmlStepOpPtr step;
462
5.14k
    xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
463
464
5.14k
    if ((comp == NULL) || (node == NULL)) return(-1);
465
5.14k
    i = 0;
466
5.14k
restart:
467
8.25k
    for (;i < comp->nbStep;i++) {
468
8.25k
  step = &comp->steps[i];
469
8.25k
  switch (step->op) {
470
96
            case XML_OP_END:
471
96
    goto found;
472
24
            case XML_OP_ROOT:
473
24
    if (node->type == XML_NAMESPACE_DECL)
474
0
        goto rollback;
475
24
    node = node->parent;
476
24
    if ((node->type == XML_DOCUMENT_NODE) ||
477
0
        (node->type == XML_HTML_DOCUMENT_NODE))
478
24
        continue;
479
0
    goto rollback;
480
3.37k
            case XML_OP_ELEM:
481
3.37k
    if (node->type != XML_ELEMENT_NODE)
482
398
        goto rollback;
483
2.97k
    if (step->value == NULL)
484
338
        continue;
485
2.63k
    if (step->value[0] != node->name[0])
486
1.68k
        goto rollback;
487
952
    if (!xmlStrEqual(step->value, node->name))
488
548
        goto rollback;
489
490
    /* Namespace test */
491
404
    if (node->ns == NULL) {
492
404
        if (step->value2 != NULL)
493
195
      goto rollback;
494
404
    } else if (node->ns->href != NULL) {
495
0
        if (step->value2 == NULL)
496
0
      goto rollback;
497
0
        if (!xmlStrEqual(step->value2, node->ns->href))
498
0
      goto rollback;
499
0
    }
500
209
    continue;
501
209
            case XML_OP_CHILD: {
502
0
    xmlNodePtr lst;
503
504
0
    if ((node->type != XML_ELEMENT_NODE) &&
505
0
        (node->type != XML_DOCUMENT_NODE) &&
506
0
        (node->type != XML_HTML_DOCUMENT_NODE))
507
0
        goto rollback;
508
509
0
    lst = node->children;
510
511
0
    if (step->value != NULL) {
512
0
        while (lst != NULL) {
513
0
      if ((lst->type == XML_ELEMENT_NODE) &&
514
0
          (step->value[0] == lst->name[0]) &&
515
0
          (xmlStrEqual(step->value, lst->name)))
516
0
          break;
517
0
      lst = lst->next;
518
0
        }
519
0
        if (lst != NULL)
520
0
      continue;
521
0
    }
522
0
    goto rollback;
523
0
      }
524
616
            case XML_OP_ATTR:
525
616
    if (node->type != XML_ATTRIBUTE_NODE)
526
616
        goto rollback;
527
0
    if (step->value != NULL) {
528
0
        if (step->value[0] != node->name[0])
529
0
      goto rollback;
530
0
        if (!xmlStrEqual(step->value, node->name))
531
0
      goto rollback;
532
0
    }
533
    /* Namespace test */
534
0
    if (node->ns == NULL) {
535
0
        if (step->value2 != NULL)
536
0
      goto rollback;
537
0
    } else if (step->value2 != NULL) {
538
0
        if (!xmlStrEqual(step->value2, node->ns->href))
539
0
      goto rollback;
540
0
    }
541
0
    continue;
542
1.25k
            case XML_OP_PARENT:
543
1.25k
    if ((node->type == XML_DOCUMENT_NODE) ||
544
1.25k
        (node->type == XML_HTML_DOCUMENT_NODE) ||
545
1.25k
        (node->type == XML_NAMESPACE_DECL))
546
0
        goto rollback;
547
1.25k
    node = node->parent;
548
1.25k
    if (node == NULL)
549
0
        goto rollback;
550
1.25k
    if (step->value == NULL)
551
1.25k
        continue;
552
0
    if (step->value[0] != node->name[0])
553
0
        goto rollback;
554
0
    if (!xmlStrEqual(step->value, node->name))
555
0
        goto rollback;
556
    /* Namespace test */
557
0
    if (node->ns == NULL) {
558
0
        if (step->value2 != NULL)
559
0
      goto rollback;
560
0
    } else if (node->ns->href != NULL) {
561
0
        if (step->value2 == NULL)
562
0
      goto rollback;
563
0
        if (!xmlStrEqual(step->value2, node->ns->href))
564
0
      goto rollback;
565
0
    }
566
0
    continue;
567
444
            case XML_OP_ANCESTOR:
568
    /* TODO: implement coalescing of ANCESTOR/NODE ops */
569
444
    if (step->value == NULL) {
570
444
        i++;
571
444
        step = &comp->steps[i];
572
444
        if (step->op == XML_OP_ROOT)
573
0
      goto found;
574
444
        if (step->op != XML_OP_ELEM)
575
222
      goto rollback;
576
222
        if (step->value == NULL)
577
7
      return(-1);
578
222
    }
579
215
    if (node == NULL)
580
0
        goto rollback;
581
215
    if ((node->type == XML_DOCUMENT_NODE) ||
582
215
        (node->type == XML_HTML_DOCUMENT_NODE) ||
583
215
        (node->type == XML_NAMESPACE_DECL))
584
0
        goto rollback;
585
215
    node = node->parent;
586
430
    while (node != NULL) {
587
215
        if ((node->type == XML_ELEMENT_NODE) &&
588
0
      (step->value[0] == node->name[0]) &&
589
0
      (xmlStrEqual(step->value, node->name))) {
590
      /* Namespace test */
591
0
      if (node->ns == NULL) {
592
0
          if (step->value2 == NULL)
593
0
        break;
594
0
      } else if (node->ns->href != NULL) {
595
0
          if ((step->value2 != NULL) &&
596
0
              (xmlStrEqual(step->value2, node->ns->href)))
597
0
        break;
598
0
      }
599
0
        }
600
215
        node = node->parent;
601
215
    }
602
215
    if (node == NULL)
603
215
        goto rollback;
604
    /*
605
     * prepare a potential rollback from here
606
     * for ancestors of that node.
607
     */
608
0
    if (step->op == XML_OP_ANCESTOR)
609
0
        xmlPatPushState(&states, i, node);
610
0
    else
611
0
        xmlPatPushState(&states, i - 1, node);
612
0
    continue;
613
635
            case XML_OP_NS:
614
635
    if (node->type != XML_ELEMENT_NODE)
615
329
        goto rollback;
616
306
    if (node->ns == NULL) {
617
306
        if (step->value != NULL)
618
306
      goto rollback;
619
306
    } else if (node->ns->href != NULL) {
620
0
        if (step->value == NULL)
621
0
      goto rollback;
622
0
        if (!xmlStrEqual(step->value, node->ns->href))
623
0
      goto rollback;
624
0
    }
625
0
    break;
626
1.81k
            case XML_OP_ALL:
627
1.81k
    if (node->type != XML_ELEMENT_NODE)
628
532
        goto rollback;
629
1.28k
    break;
630
8.25k
  }
631
8.25k
    }
632
96
found:
633
96
    if (states.states != NULL) {
634
        /* Free the rollback states */
635
0
  xmlFree(states.states);
636
0
    }
637
96
    return(1);
638
5.04k
rollback:
639
    /* got an error try to rollback */
640
5.04k
    if (states.states == NULL)
641
5.04k
  return(0);
642
0
    if (states.nbstates <= 0) {
643
0
  xmlFree(states.states);
644
0
  return(0);
645
0
    }
646
0
    states.nbstates--;
647
0
    i = states.states[states.nbstates].step;
648
0
    node = states.states[states.nbstates].node;
649
0
    goto restart;
650
0
}
651
652
/************************************************************************
653
 *                  *
654
 *      Dedicated parser for templates      *
655
 *                  *
656
 ************************************************************************/
657
658
61.3M
#define CUR (*ctxt->cur)
659
#define SKIP(val) ctxt->cur += (val)
660
12.0M
#define NXT(val) ctxt->cur[(val)]
661
#define PEEKPREV(val) ctxt->cur[-(val)]
662
16.0M
#define CUR_PTR ctxt->cur
663
664
#define SKIP_BLANKS             \
665
34.1M
    while (IS_BLANK_CH(CUR)) NEXT
666
667
#define CURRENT (*ctxt->cur)
668
31.1M
#define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
669
670
671
#define PUSH(op, val, val2)           \
672
24.2M
    if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
673
674
/**
675
 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
676
 *                  CombiningChar | Extender
677
 *
678
 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
679
 *
680
 * [6] Names ::= Name (S Name)*
681
 *
682
 * @param ctxt  the XPath Parser context
683
 * @returns the Name parsed or NULL
684
 */
685
686
static xmlChar *
687
37.5k
xmlPatScanName(xmlPatParserContextPtr ctxt) {
688
37.5k
    const xmlChar *q, *cur;
689
37.5k
    xmlChar *ret = NULL;
690
691
37.5k
    SKIP_BLANKS;
692
693
37.5k
    q = CUR_PTR;
694
37.5k
    cur = xmlScanName(q, XML_MAX_NAME_LENGTH, 0);
695
37.5k
    if ((cur == NULL) || (cur == q))
696
21.1k
        return(NULL);
697
16.3k
    if (ctxt->dict)
698
0
  ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
699
16.3k
    else
700
16.3k
  ret = xmlStrndup(q, cur - q);
701
16.3k
    CUR_PTR = cur;
702
16.3k
    return(ret);
703
37.5k
}
704
705
/**
706
 * Parses a non qualified name
707
 *
708
 * @param ctxt  the XPath Parser context
709
 * @returns the Name parsed or NULL
710
 */
711
712
static xmlChar *
713
9.13M
xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
714
9.13M
    const xmlChar *q, *cur;
715
9.13M
    xmlChar *ret = NULL;
716
717
9.13M
    SKIP_BLANKS;
718
719
9.13M
    q = CUR_PTR;
720
9.13M
    cur = xmlScanName(q, XML_MAX_NAME_LENGTH, XML_SCAN_NC);
721
9.13M
    if ((cur == NULL) || (cur == q))
722
2.25M
        return(NULL);
723
6.88M
    if (ctxt->dict)
724
0
  ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
725
6.88M
    else
726
6.88M
  ret = xmlStrndup(q, cur - q);
727
6.88M
    if (ret == NULL)
728
5
        ctxt->error = -1;
729
6.88M
    CUR_PTR = cur;
730
6.88M
    return(ret);
731
9.13M
}
732
733
/**
734
 * Compile an attribute test.
735
 *
736
 * @param ctxt  the compilation context
737
 */
738
static void
739
37.7k
xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
740
37.7k
    xmlChar *token = NULL;
741
37.7k
    xmlChar *name = NULL;
742
37.7k
    xmlChar *URL = NULL;
743
744
37.7k
    SKIP_BLANKS;
745
37.7k
    name = xmlPatScanNCName(ctxt);
746
37.7k
    if (ctxt->error < 0)
747
2
        return;
748
37.7k
    if (name == NULL) {
749
6.79k
  if (CUR == '*') {
750
4.55k
      PUSH(XML_OP_ATTR, NULL, NULL);
751
4.54k
      NEXT;
752
4.54k
  } else {
753
2.24k
      ERROR(NULL, NULL, NULL,
754
2.24k
    "xmlCompileAttributeTest : Name expected\n");
755
2.24k
      ctxt->error = 1;
756
2.24k
  }
757
6.79k
  return;
758
6.79k
    }
759
30.9k
    if (CUR == ':') {
760
10.8k
  int i;
761
10.8k
  xmlChar *prefix = name;
762
763
10.8k
  NEXT;
764
765
10.8k
  if (IS_BLANK_CH(CUR)) {
766
1.08k
      ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
767
1.08k
      ctxt->error = 1;
768
1.08k
      goto error;
769
1.08k
  }
770
  /*
771
  * This is a namespace match
772
  */
773
9.75k
  token = xmlPatScanName(ctxt);
774
9.75k
  if ((prefix[0] == 'x') &&
775
9.58k
      (prefix[1] == 'm') &&
776
6.65k
      (prefix[2] == 'l') &&
777
6.35k
      (prefix[3] == 0))
778
6.09k
  {
779
6.09k
      XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
780
6.09k
  } else {
781
5.64k
      for (i = 0;i < ctxt->nb_namespaces;i++) {
782
4.61k
    if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
783
2.63k
        XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
784
2.63k
        break;
785
2.63k
    }
786
4.61k
      }
787
3.66k
      if (i >= ctxt->nb_namespaces) {
788
1.02k
    ERROR5(NULL, NULL, NULL,
789
1.02k
        "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
790
1.02k
        prefix);
791
1.02k
    ctxt->error = 1;
792
1.02k
    goto error;
793
1.02k
      }
794
3.66k
  }
795
8.72k
        XML_PAT_FREE_STRING(ctxt, name);
796
8.72k
        name = NULL;
797
8.72k
  if (token == NULL) {
798
3.57k
      if (CUR == '*') {
799
2.78k
    NEXT;
800
2.78k
    PUSH(XML_OP_ATTR, NULL, URL);
801
2.78k
      } else {
802
786
    ERROR(NULL, NULL, NULL,
803
786
        "xmlCompileAttributeTest : Name expected\n");
804
786
    ctxt->error = 1;
805
786
    goto error;
806
786
      }
807
5.15k
  } else {
808
5.15k
      PUSH(XML_OP_ATTR, token, URL);
809
5.15k
  }
810
20.1k
    } else {
811
20.1k
  PUSH(XML_OP_ATTR, name, NULL);
812
20.1k
    }
813
28.0k
    return;
814
28.0k
error:
815
2.90k
    if (name != NULL)
816
2.11k
  XML_PAT_FREE_STRING(ctxt, name);
817
2.90k
    if (URL != NULL)
818
788
  XML_PAT_FREE_STRING(ctxt, URL)
819
2.90k
    if (token != NULL)
820
480
  XML_PAT_FREE_STRING(ctxt, token);
821
2.90k
}
822
823
/**
824
 * Compile the Step Pattern and generates a precompiled
825
 * form suitable for fast matching.
826
 *
827
 * [3]    Step    ::=    '.' | NameTest
828
 * [4]    NameTest    ::=    QName | '*' | NCName ':' '*'
829
 *
830
 * @param ctxt  the compilation context
831
 */
832
833
static void
834
12.1M
xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
835
12.1M
    xmlChar *token = NULL;
836
12.1M
    xmlChar *name = NULL;
837
12.1M
    xmlChar *URL = NULL;
838
12.1M
    int hasBlanks = 0;
839
840
12.1M
    SKIP_BLANKS;
841
12.1M
    if (CUR == '.') {
842
  /*
843
  * Context node.
844
  */
845
3.00M
  NEXT;
846
3.00M
  PUSH(XML_OP_ELEM, NULL, NULL);
847
3.00M
  return;
848
3.00M
    }
849
9.13M
    if (CUR == '@') {
850
  /*
851
  * Attribute test.
852
  */
853
34.3k
  if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
854
519
      ERROR5(NULL, NULL, NULL,
855
519
    "Unexpected attribute axis in '%s'.\n", ctxt->base);
856
519
      ctxt->error = 1;
857
519
      return;
858
519
  }
859
33.7k
  NEXT;
860
33.7k
  xmlCompileAttributeTest(ctxt);
861
33.7k
  if (ctxt->error != 0)
862
3.90k
      goto error;
863
29.8k
  return;
864
33.7k
    }
865
9.10M
    name = xmlPatScanNCName(ctxt);
866
9.10M
    if (ctxt->error < 0)
867
3
        return;
868
9.10M
    if (name == NULL) {
869
2.24M
  if (CUR == '*') {
870
2.24M
      NEXT;
871
2.24M
      PUSH(XML_OP_ALL, NULL, NULL);
872
2.24M
      return;
873
2.24M
  } else {
874
4.00k
      ERROR(NULL, NULL, NULL,
875
4.00k
        "xmlCompileStepPattern : Name expected\n");
876
4.00k
      ctxt->error = 1;
877
4.00k
      return;
878
4.00k
  }
879
2.24M
    }
880
6.85M
    if (IS_BLANK_CH(CUR)) {
881
502k
  hasBlanks = 1;
882
502k
  SKIP_BLANKS;
883
502k
    }
884
6.85M
    if (CUR == ':') {
885
32.7k
  NEXT;
886
32.7k
  if (CUR != ':') {
887
21.9k
      xmlChar *prefix = name;
888
21.9k
      int i;
889
890
21.9k
      if (hasBlanks || IS_BLANK_CH(CUR)) {
891
616
    ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
892
616
    ctxt->error = 1;
893
616
    goto error;
894
616
      }
895
      /*
896
       * This is a namespace match
897
       */
898
21.3k
      token = xmlPatScanName(ctxt);
899
21.3k
      if ((prefix[0] == 'x') &&
900
17.1k
    (prefix[1] == 'm') &&
901
5.15k
    (prefix[2] == 'l') &&
902
4.92k
    (prefix[3] == 0))
903
4.67k
      {
904
4.67k
    XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
905
16.7k
      } else {
906
23.7k
    for (i = 0;i < ctxt->nb_namespaces;i++) {
907
19.2k
        if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
908
12.2k
      XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
909
12.2k
      break;
910
12.2k
        }
911
19.2k
    }
912
16.7k
    if (i >= ctxt->nb_namespaces) {
913
4.46k
        ERROR5(NULL, NULL, NULL,
914
4.46k
      "xmlCompileStepPattern : no namespace bound to prefix %s\n",
915
4.46k
      prefix);
916
4.46k
        ctxt->error = 1;
917
4.46k
        goto error;
918
4.46k
    }
919
16.7k
      }
920
16.9k
      XML_PAT_FREE_STRING(ctxt, prefix);
921
16.9k
      name = NULL;
922
16.9k
      if (token == NULL) {
923
14.4k
    if (CUR == '*') {
924
13.9k
        NEXT;
925
13.9k
        PUSH(XML_OP_NS, URL, NULL);
926
13.9k
    } else {
927
425
        ERROR(NULL, NULL, NULL,
928
425
          "xmlCompileStepPattern : Name expected\n");
929
425
        ctxt->error = 1;
930
425
        goto error;
931
425
    }
932
14.4k
      } else {
933
2.51k
    PUSH(XML_OP_ELEM, token, URL);
934
2.51k
      }
935
16.9k
  } else {
936
10.7k
      NEXT;
937
10.7k
      if (xmlStrEqual(name, (const xmlChar *) "child")) {
938
6.37k
    XML_PAT_FREE_STRING(ctxt, name);
939
6.37k
    name = xmlPatScanName(ctxt);
940
6.37k
    if (name == NULL) {
941
981
        if (CUR == '*') {
942
316
      NEXT;
943
316
      PUSH(XML_OP_ALL, NULL, NULL);
944
315
      return;
945
665
        } else {
946
665
      ERROR(NULL, NULL, NULL,
947
665
          "xmlCompileStepPattern : QName expected\n");
948
665
      ctxt->error = 1;
949
665
      goto error;
950
665
        }
951
981
    }
952
5.39k
    if (CUR == ':') {
953
0
        xmlChar *prefix = name;
954
0
        int i;
955
956
0
        NEXT;
957
0
        if (IS_BLANK_CH(CUR)) {
958
0
      ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
959
0
      ctxt->error = 1;
960
0
      goto error;
961
0
        }
962
        /*
963
        * This is a namespace match
964
        */
965
0
        token = xmlPatScanName(ctxt);
966
0
        if ((prefix[0] == 'x') &&
967
0
      (prefix[1] == 'm') &&
968
0
      (prefix[2] == 'l') &&
969
0
      (prefix[3] == 0))
970
0
        {
971
0
      XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
972
0
        } else {
973
0
      for (i = 0;i < ctxt->nb_namespaces;i++) {
974
0
          if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
975
0
        XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
976
0
        break;
977
0
          }
978
0
      }
979
0
      if (i >= ctxt->nb_namespaces) {
980
0
          ERROR5(NULL, NULL, NULL,
981
0
        "xmlCompileStepPattern : no namespace bound "
982
0
        "to prefix %s\n", prefix);
983
0
          ctxt->error = 1;
984
0
          goto error;
985
0
      }
986
0
        }
987
0
        XML_PAT_FREE_STRING(ctxt, prefix);
988
0
        name = NULL;
989
0
        if (token == NULL) {
990
0
      if (CUR == '*') {
991
0
          NEXT;
992
0
          PUSH(XML_OP_NS, URL, NULL);
993
0
      } else {
994
0
          ERROR(NULL, NULL, NULL,
995
0
        "xmlCompileStepPattern : Name expected\n");
996
0
          ctxt->error = 1;
997
0
          goto error;
998
0
      }
999
0
        } else {
1000
0
      PUSH(XML_OP_ELEM, token, URL);
1001
0
        }
1002
0
    } else
1003
5.39k
        PUSH(XML_OP_ELEM, name, NULL);
1004
5.39k
    return;
1005
5.39k
      } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1006
3.25k
    XML_PAT_FREE_STRING(ctxt, name)
1007
3.25k
    name = NULL;
1008
3.25k
    if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1009
523
        ERROR5(NULL, NULL, NULL,
1010
523
      "Unexpected attribute axis in '%s'.\n", ctxt->base);
1011
523
        ctxt->error = 1;
1012
523
        goto error;
1013
523
    }
1014
2.73k
    xmlCompileAttributeTest(ctxt);
1015
2.73k
    if (ctxt->error != 0)
1016
1.18k
        goto error;
1017
1.55k
    return;
1018
2.73k
      } else {
1019
1.14k
    ERROR5(NULL, NULL, NULL,
1020
1.14k
        "The 'element' or 'attribute' axis is expected.\n", NULL);
1021
1.14k
    ctxt->error = 1;
1022
1.14k
    goto error;
1023
1.14k
      }
1024
10.7k
  }
1025
6.82M
    } else if (CUR == '*') {
1026
417
        if (name != NULL) {
1027
417
      ctxt->error = 1;
1028
417
      goto error;
1029
417
  }
1030
0
  NEXT;
1031
0
  PUSH(XML_OP_ALL, token, NULL);
1032
6.82M
    } else {
1033
6.82M
  PUSH(XML_OP_ELEM, name, NULL);
1034
6.82M
    }
1035
6.83M
    return;
1036
6.83M
error:
1037
13.3k
    if (URL != NULL)
1038
428
  XML_PAT_FREE_STRING(ctxt, URL)
1039
13.3k
    if (token != NULL)
1040
2.84k
  XML_PAT_FREE_STRING(ctxt, token)
1041
13.3k
    if (name != NULL)
1042
6.64k
  XML_PAT_FREE_STRING(ctxt, name)
1043
13.3k
}
1044
1045
/**
1046
 * Compile the Path Pattern and generates a precompiled
1047
 * form suitable for fast matching.
1048
 *
1049
 * [5]    Path    ::=    ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1050
 *
1051
 * @param ctxt  the compilation context
1052
 */
1053
static void
1054
13.8k
xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1055
13.8k
    SKIP_BLANKS;
1056
13.8k
    if (CUR == '/') {
1057
4.91k
        ctxt->comp->flags |= PAT_FROM_ROOT;
1058
8.94k
    } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
1059
1.89k
        ctxt->comp->flags |= PAT_FROM_CUR;
1060
1.89k
    }
1061
1062
13.8k
    if ((CUR == '/') && (NXT(1) == '/')) {
1063
879
  PUSH(XML_OP_ANCESTOR, NULL, NULL);
1064
879
  NEXT;
1065
879
  NEXT;
1066
12.9k
    } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1067
317
  PUSH(XML_OP_ANCESTOR, NULL, NULL);
1068
317
  NEXT;
1069
317
  NEXT;
1070
317
  NEXT;
1071
  /* Check for incompleteness. */
1072
317
  SKIP_BLANKS;
1073
317
  if (CUR == 0) {
1074
27
      ERROR5(NULL, NULL, NULL,
1075
27
         "Incomplete expression '%s'.\n", ctxt->base);
1076
27
      ctxt->error = 1;
1077
27
      goto error;
1078
27
  }
1079
317
    }
1080
13.8k
    if (CUR == '@') {
1081
1.22k
  NEXT;
1082
1.22k
  xmlCompileAttributeTest(ctxt);
1083
1.22k
        if (ctxt->error != 0)
1084
70
            goto error;
1085
1.15k
  SKIP_BLANKS;
1086
  /* TODO: check for incompleteness */
1087
1.15k
  if (CUR != 0) {
1088
506
      xmlCompileStepPattern(ctxt);
1089
506
      if (ctxt->error != 0)
1090
35
    goto error;
1091
506
  }
1092
12.6k
    } else {
1093
12.6k
        if (CUR == '/') {
1094
4.25k
      PUSH(XML_OP_ROOT, NULL, NULL);
1095
4.25k
      NEXT;
1096
      /* Check for incompleteness. */
1097
4.25k
      SKIP_BLANKS;
1098
4.25k
      if (CUR == 0) {
1099
31
    ERROR5(NULL, NULL, NULL,
1100
31
        "Incomplete expression '%s'.\n", ctxt->base);
1101
31
    ctxt->error = 1;
1102
31
    goto error;
1103
31
      }
1104
4.25k
  }
1105
12.5k
  xmlCompileStepPattern(ctxt);
1106
12.5k
  if (ctxt->error != 0)
1107
247
      goto error;
1108
12.3k
  SKIP_BLANKS;
1109
12.0M
  while (CUR == '/') {
1110
12.0M
      if (NXT(1) == '/') {
1111
599k
          PUSH(XML_OP_ANCESTOR, NULL, NULL);
1112
599k
    NEXT;
1113
599k
    NEXT;
1114
599k
    SKIP_BLANKS;
1115
599k
    xmlCompileStepPattern(ctxt);
1116
599k
    if (ctxt->error != 0)
1117
57
        goto error;
1118
11.4M
      } else {
1119
11.4M
          PUSH(XML_OP_PARENT, NULL, NULL);
1120
11.4M
    NEXT;
1121
11.4M
    SKIP_BLANKS;
1122
11.4M
    if (CUR == 0) {
1123
76
        ERROR5(NULL, NULL, NULL,
1124
76
        "Incomplete expression '%s'.\n", ctxt->base);
1125
76
        ctxt->error = 1;
1126
76
        goto error;
1127
76
    }
1128
11.4M
    xmlCompileStepPattern(ctxt);
1129
11.4M
    if (ctxt->error != 0)
1130
32
        goto error;
1131
11.4M
      }
1132
12.0M
  }
1133
12.3k
    }
1134
13.2k
    if (CUR != 0) {
1135
125
  ERROR5(NULL, NULL, NULL,
1136
125
         "Failed to compile pattern %s\n", ctxt->base);
1137
125
  ctxt->error = 1;
1138
125
    }
1139
13.8k
error:
1140
13.8k
    return;
1141
13.2k
}
1142
1143
/**
1144
 * Compile the Path Pattern and generates a precompiled
1145
 * form suitable for fast matching.
1146
 *
1147
 * [5]    Path    ::=    ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1148
 *
1149
 * @param ctxt  the compilation context
1150
 */
1151
static void
1152
58.5k
xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
1153
58.5k
    SKIP_BLANKS;
1154
58.5k
    if (CUR == '/') {
1155
683
  ERROR5(NULL, NULL, NULL,
1156
683
      "Unexpected selection of the document root in '%s'.\n",
1157
683
      ctxt->base);
1158
683
  goto error;
1159
683
    }
1160
57.8k
    ctxt->comp->flags |= PAT_FROM_CUR;
1161
1162
57.8k
    if (CUR == '.') {
1163
  /* "." - "self::node()" */
1164
11.4k
  NEXT;
1165
11.4k
  SKIP_BLANKS;
1166
11.4k
  if (CUR == 0) {
1167
      /*
1168
      * Selection of the context node.
1169
      */
1170
1.91k
      PUSH(XML_OP_ELEM, NULL, NULL);
1171
1.91k
      return;
1172
1.91k
  }
1173
9.55k
  if (CUR != '/') {
1174
      /* TODO: A more meaningful error message. */
1175
549
      ERROR5(NULL, NULL, NULL,
1176
549
      "Unexpected token after '.' in '%s'.\n", ctxt->base);
1177
549
      goto error;
1178
549
  }
1179
  /* "./" - "self::node()/" */
1180
9.00k
  NEXT;
1181
9.00k
  SKIP_BLANKS;
1182
9.00k
  if (CUR == '/') {
1183
5.70k
      if (IS_BLANK_CH(PEEKPREV(1))) {
1184
    /*
1185
    * Disallow "./ /"
1186
    */
1187
200
    ERROR5(NULL, NULL, NULL,
1188
200
        "Unexpected '/' token in '%s'.\n", ctxt->base);
1189
200
    goto error;
1190
200
      }
1191
      /* ".//" - "self:node()/descendant-or-self::node()/" */
1192
5.50k
      PUSH(XML_OP_ANCESTOR, NULL, NULL);
1193
5.50k
      NEXT;
1194
5.50k
      SKIP_BLANKS;
1195
5.50k
  }
1196
8.80k
  if (CUR == 0)
1197
558
      goto error_unfinished;
1198
8.80k
    }
1199
    /*
1200
    * Process steps.
1201
    */
1202
120k
    do {
1203
120k
  xmlCompileStepPattern(ctxt);
1204
120k
  if (ctxt->error != 0)
1205
17.5k
      goto error;
1206
103k
  SKIP_BLANKS;
1207
103k
  if (CUR != '/')
1208
35.7k
      break;
1209
67.3k
  PUSH(XML_OP_PARENT, NULL, NULL);
1210
67.3k
  NEXT;
1211
67.3k
  SKIP_BLANKS;
1212
67.3k
  if (CUR == '/') {
1213
      /*
1214
      * Disallow subsequent '//'.
1215
      */
1216
1.07k
      ERROR5(NULL, NULL, NULL,
1217
1.07k
    "Unexpected subsequent '//' in '%s'.\n",
1218
1.07k
    ctxt->base);
1219
1.07k
      goto error;
1220
1.07k
  }
1221
66.2k
  if (CUR == 0)
1222
268
      goto error_unfinished;
1223
1224
66.2k
    } while (CUR != 0);
1225
1226
35.7k
    if (CUR != 0) {
1227
5.68k
  ERROR5(NULL, NULL, NULL,
1228
5.68k
      "Failed to compile expression '%s'.\n", ctxt->base);
1229
5.68k
  ctxt->error = 1;
1230
5.68k
    }
1231
35.7k
    return;
1232
20.0k
error:
1233
20.0k
    ctxt->error = 1;
1234
20.0k
    return;
1235
1236
826
error_unfinished:
1237
826
    ctxt->error = 1;
1238
826
    ERROR5(NULL, NULL, NULL,
1239
826
  "Unfinished expression '%s'.\n", ctxt->base);
1240
826
}
1241
1242
/************************************************************************
1243
 *                  *
1244
 *      The streaming code        *
1245
 *                  *
1246
 ************************************************************************/
1247
1248
/**
1249
 * build a new compiled pattern for streaming
1250
 *
1251
 * @param size  the number of expected steps
1252
 * @returns the new structure or NULL in case of error.
1253
 */
1254
static xmlStreamCompPtr
1255
44.4k
xmlNewStreamComp(int size) {
1256
44.4k
    xmlStreamCompPtr cur;
1257
1258
44.4k
    if (size < 4)
1259
38.6k
        size  = 4;
1260
1261
44.4k
    cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1262
44.4k
    if (cur == NULL) {
1263
9
  ERROR(NULL, NULL, NULL,
1264
9
    "xmlNewStreamComp: malloc failed\n");
1265
9
  return(NULL);
1266
9
    }
1267
44.4k
    memset(cur, 0, sizeof(xmlStreamComp));
1268
44.4k
    cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1269
44.4k
    if (cur->steps == NULL) {
1270
5
  xmlFree(cur);
1271
5
  ERROR(NULL, NULL, NULL,
1272
5
        "xmlNewStreamComp: malloc failed\n");
1273
5
  return(NULL);
1274
5
    }
1275
44.4k
    cur->nbStep = 0;
1276
44.4k
    cur->maxStep = size;
1277
44.4k
    return(cur);
1278
44.4k
}
1279
1280
/**
1281
 * Free the compiled pattern for streaming
1282
 *
1283
 * @param comp  the compiled pattern for streaming
1284
 */
1285
static void
1286
44.4k
xmlFreeStreamComp(xmlStreamCompPtr comp) {
1287
44.4k
    if (comp != NULL) {
1288
44.4k
        if (comp->steps != NULL)
1289
44.4k
      xmlFree(comp->steps);
1290
44.4k
  if (comp->dict != NULL)
1291
0
      xmlDictFree(comp->dict);
1292
44.4k
        xmlFree(comp);
1293
44.4k
    }
1294
44.4k
}
1295
1296
/**
1297
 * Add a new step to the compiled pattern
1298
 *
1299
 * @param comp  the compiled pattern for streaming
1300
 * @param name  the first string, the name, or NULL for *
1301
 * @param ns  the second step, the namespace name
1302
 * @param nodeType  the node type
1303
 * @param flags  the flags for that step
1304
 * @returns -1 in case of error or the step index if successful
1305
 */
1306
static int
1307
xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1308
7.63M
                     const xmlChar *ns, int nodeType, int flags) {
1309
7.63M
    xmlStreamStepPtr cur;
1310
1311
7.63M
    if (comp->nbStep >= comp->maxStep) {
1312
0
        xmlStreamStepPtr tmp;
1313
0
        int newSize;
1314
1315
0
        newSize = xmlGrowCapacity(comp->maxStep, sizeof(tmp[0]),
1316
0
                                  4, XML_MAX_ITEMS);
1317
0
        if (newSize < 0) {
1318
0
      ERROR(NULL, NULL, NULL,
1319
0
      "xmlNewStreamComp: growCapacity failed\n");
1320
0
      return(-1);
1321
0
        }
1322
0
  cur = xmlRealloc(comp->steps, newSize * sizeof(tmp[0]));
1323
0
  if (cur == NULL) {
1324
0
      ERROR(NULL, NULL, NULL,
1325
0
      "xmlNewStreamComp: malloc failed\n");
1326
0
      return(-1);
1327
0
  }
1328
0
  comp->steps = cur;
1329
0
        comp->maxStep = newSize;
1330
0
    }
1331
7.63M
    cur = &comp->steps[comp->nbStep++];
1332
7.63M
    cur->flags = flags;
1333
7.63M
    cur->name = name;
1334
7.63M
    cur->ns = ns;
1335
7.63M
    cur->nodeType = nodeType;
1336
7.63M
    return(comp->nbStep - 1);
1337
7.63M
}
1338
1339
/**
1340
 * Tries to stream compile a pattern
1341
 *
1342
 * @param comp  the precompiled pattern
1343
 * @returns -1 in case of failure and 0 in case of success.
1344
 */
1345
static int
1346
44.4k
xmlStreamCompile(xmlPatternPtr comp) {
1347
44.4k
    xmlStreamCompPtr stream;
1348
44.4k
    int i, s = 0, root = 0, flags = 0, prevs = -1;
1349
44.4k
    xmlStepOp step;
1350
1351
44.4k
    if ((comp == NULL) || (comp->steps == NULL))
1352
0
        return(-1);
1353
    /*
1354
     * special case for .
1355
     */
1356
44.4k
    if ((comp->nbStep == 1) &&
1357
23.6k
        (comp->steps[0].op == XML_OP_ELEM) &&
1358
19.4k
  (comp->steps[0].value == NULL) &&
1359
2.72k
  (comp->steps[0].value2 == NULL)) {
1360
2.72k
  stream = xmlNewStreamComp(0);
1361
2.72k
  if (stream == NULL)
1362
3
      return(-1);
1363
  /* Note that the stream will have no steps in this case. */
1364
2.72k
  stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1365
2.72k
  comp->stream = stream;
1366
2.72k
  return(0);
1367
2.72k
    }
1368
1369
41.7k
    stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1370
41.7k
    if (stream == NULL)
1371
11
        return(-1);
1372
41.6k
    if (comp->dict != NULL) {
1373
0
        stream->dict = comp->dict;
1374
0
  xmlDictReference(stream->dict);
1375
0
    }
1376
1377
41.6k
    i = 0;
1378
41.6k
    if (comp->flags & PAT_FROM_ROOT)
1379
4.72k
  stream->flags |= XML_STREAM_FROM_ROOT;
1380
1381
19.8M
    for (;i < comp->nbStep;i++) {
1382
19.8M
  step = comp->steps[i];
1383
19.8M
        switch (step.op) {
1384
0
      case XML_OP_END:
1385
0
          break;
1386
4.12k
      case XML_OP_ROOT:
1387
4.12k
          if (i != 0)
1388
212
        goto error;
1389
3.91k
    root = 1;
1390
3.91k
    break;
1391
5.18k
      case XML_OP_NS:
1392
5.18k
    s = xmlStreamCompAddStep(stream, NULL, step.value,
1393
5.18k
        XML_ELEMENT_NODE, flags);
1394
5.18k
    if (s < 0)
1395
0
        goto error;
1396
5.18k
    prevs = s;
1397
5.18k
    flags = 0;
1398
5.18k
    break;
1399
23.0k
      case XML_OP_ATTR:
1400
23.0k
    flags |= XML_STREAM_STEP_ATTR;
1401
23.0k
    prevs = -1;
1402
23.0k
    s = xmlStreamCompAddStep(stream,
1403
23.0k
        step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
1404
23.0k
    flags = 0;
1405
23.0k
    if (s < 0)
1406
0
        goto error;
1407
23.0k
    break;
1408
8.14M
      case XML_OP_ELEM:
1409
8.14M
          if ((step.value == NULL) && (step.value2 == NULL)) {
1410
        /*
1411
        * We have a "." or "self::node()" here.
1412
        * Eliminate redundant self::node() tests like in "/./."
1413
        * or "//./"
1414
        * The only case we won't eliminate is "//.", i.e. if
1415
        * self::node() is the last node test and we had
1416
        * continuation somewhere beforehand.
1417
        */
1418
2.31M
        if ((comp->nbStep == i + 1) &&
1419
3.55k
      (flags & XML_STREAM_STEP_DESC)) {
1420
      /*
1421
      * Mark the special case where the expression resolves
1422
      * to any type of node.
1423
      */
1424
2.08k
      if (comp->nbStep == i + 1) {
1425
2.08k
          stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1426
2.08k
      }
1427
2.08k
      flags |= XML_STREAM_STEP_NODE;
1428
2.08k
      s = xmlStreamCompAddStep(stream, NULL, NULL,
1429
2.08k
          XML_STREAM_ANY_NODE, flags);
1430
2.08k
      if (s < 0)
1431
0
          goto error;
1432
2.08k
      flags = 0;
1433
      /*
1434
      * If there was a previous step, mark it to be added to
1435
      * the result node-set; this is needed since only
1436
      * the last step will be marked as "final" and only
1437
      * "final" nodes are added to the resulting set.
1438
      */
1439
2.08k
      if (prevs != -1) {
1440
419
          stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1441
419
          prevs = -1;
1442
419
      }
1443
2.08k
      break;
1444
1445
2.31M
        } else {
1446
      /* Just skip this one. */
1447
2.31M
      continue;
1448
2.31M
        }
1449
2.31M
    }
1450
    /* An element node. */
1451
5.83M
          s = xmlStreamCompAddStep(stream, step.value, step.value2,
1452
5.83M
        XML_ELEMENT_NODE, flags);
1453
5.83M
    if (s < 0)
1454
0
        goto error;
1455
5.83M
    prevs = s;
1456
5.83M
    flags = 0;
1457
5.83M
    break;
1458
0
      case XML_OP_CHILD:
1459
    /* An element node child. */
1460
0
          s = xmlStreamCompAddStep(stream, step.value, step.value2,
1461
0
        XML_ELEMENT_NODE, flags);
1462
0
    if (s < 0)
1463
0
        goto error;
1464
0
    prevs = s;
1465
0
    flags = 0;
1466
0
    break;
1467
1.76M
      case XML_OP_ALL:
1468
1.76M
          s = xmlStreamCompAddStep(stream, NULL, NULL,
1469
1.76M
        XML_ELEMENT_NODE, flags);
1470
1.76M
    if (s < 0)
1471
0
        goto error;
1472
1.76M
    prevs = s;
1473
1.76M
    flags = 0;
1474
1.76M
    break;
1475
9.44M
      case XML_OP_PARENT:
1476
9.44M
          break;
1477
459k
      case XML_OP_ANCESTOR:
1478
    /* Skip redundant continuations. */
1479
459k
    if (flags & XML_STREAM_STEP_DESC)
1480
298
        break;
1481
459k
          flags |= XML_STREAM_STEP_DESC;
1482
    /*
1483
    * Mark the expression as having "//".
1484
    */
1485
459k
    if ((stream->flags & XML_STREAM_DESC) == 0)
1486
6.49k
        stream->flags |= XML_STREAM_DESC;
1487
459k
    break;
1488
19.8M
  }
1489
19.8M
    }
1490
41.4k
    if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1491
  /*
1492
  * If this should behave like a real pattern, we will mark
1493
  * the first step as having "//", to be reentrant on every
1494
  * tree level.
1495
  */
1496
7.48k
  if ((stream->flags & XML_STREAM_DESC) == 0)
1497
5.84k
      stream->flags |= XML_STREAM_DESC;
1498
1499
7.48k
  if (stream->nbStep > 0) {
1500
7.25k
      if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0)
1501
6.39k
    stream->steps[0].flags |= XML_STREAM_STEP_DESC;
1502
7.25k
  }
1503
7.48k
    }
1504
41.4k
    if (stream->nbStep <= s)
1505
587
  goto error;
1506
40.8k
    stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1507
40.8k
    if (root)
1508
3.64k
  stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1509
40.8k
    comp->stream = stream;
1510
40.8k
    return(0);
1511
799
error:
1512
799
    xmlFreeStreamComp(stream);
1513
799
    return(0);
1514
41.4k
}
1515
1516
/**
1517
 * build a new stream context
1518
 *
1519
 * @param stream  the copmiled stream
1520
 * @returns the new structure or NULL in case of error.
1521
 */
1522
static xmlStreamCtxtPtr
1523
108k
xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1524
108k
    xmlStreamCtxtPtr cur;
1525
1526
108k
    cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1527
108k
    if (cur == NULL) {
1528
16
  ERROR(NULL, NULL, NULL,
1529
16
    "xmlNewStreamCtxt: malloc failed\n");
1530
16
  return(NULL);
1531
16
    }
1532
108k
    memset(cur, 0, sizeof(xmlStreamCtxt));
1533
108k
    cur->states = NULL;
1534
108k
    cur->nbState = 0;
1535
108k
    cur->maxState = 0;
1536
108k
    cur->level = 0;
1537
108k
    cur->comp = stream;
1538
108k
    cur->blockLevel = -1;
1539
108k
    return(cur);
1540
108k
}
1541
1542
/**
1543
 * Free the stream context
1544
 *
1545
 * @param stream  the stream context
1546
 */
1547
void
1548
95.8k
xmlFreeStreamCtxt(xmlStreamCtxt *stream) {
1549
95.8k
    xmlStreamCtxtPtr next;
1550
1551
204k
    while (stream != NULL) {
1552
108k
        next = stream->next;
1553
108k
        if (stream->states != NULL)
1554
3.45k
      xmlFree(stream->states);
1555
108k
        xmlFree(stream);
1556
108k
  stream = next;
1557
108k
    }
1558
95.8k
}
1559
1560
/**
1561
 * Add a new state to the stream context
1562
 *
1563
 * @param comp  the stream context
1564
 * @param idx  the step index for that streaming state
1565
 * @param level  the level
1566
 * @returns -1 in case of error or the state index if successful
1567
 */
1568
static int
1569
4.20k
xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1570
4.20k
    int i;
1571
4.82k
    for (i = 0;i < comp->nbState;i++) {
1572
625
        if (comp->states[2 * i] < 0) {
1573
0
      comp->states[2 * i] = idx;
1574
0
      comp->states[2 * i + 1] = level;
1575
0
      return(i);
1576
0
  }
1577
625
    }
1578
4.20k
    if (comp->nbState >= comp->maxState) {
1579
4.07k
        int *tmp;
1580
4.07k
        int newSize;
1581
1582
4.07k
        newSize = xmlGrowCapacity(comp->maxState, sizeof(tmp[0]) * 2,
1583
4.07k
                                  4, XML_MAX_ITEMS);
1584
4.07k
        if (newSize < 0) {
1585
0
      ERROR(NULL, NULL, NULL,
1586
0
      "xmlNewStreamCtxt: growCapacity failed\n");
1587
0
      return(-1);
1588
0
        }
1589
4.07k
  tmp = xmlRealloc(comp->states, newSize * sizeof(tmp[0]) * 2);
1590
4.07k
  if (tmp == NULL) {
1591
2
      ERROR(NULL, NULL, NULL,
1592
2
      "xmlNewStreamCtxt: malloc failed\n");
1593
2
      return(-1);
1594
2
  }
1595
4.07k
  comp->states = tmp;
1596
4.07k
        comp->maxState = newSize;
1597
4.07k
    }
1598
4.20k
    comp->states[2 * comp->nbState] = idx;
1599
4.20k
    comp->states[2 * comp->nbState++ + 1] = level;
1600
4.20k
    return(comp->nbState - 1);
1601
4.20k
}
1602
1603
/**
1604
 * Push new data onto the stream. NOTE: if the call #xmlPatterncompile
1605
 * indicated a dictionary, then strings for name and ns will be expected
1606
 * to come from the dictionary.
1607
 * Both `name` and `ns` being NULL means the / i.e. the root of the document.
1608
 * This can also act as a reset.
1609
 *
1610
 * @param stream  the stream context
1611
 * @param name  the current name
1612
 * @param ns  the namespace name
1613
 * @param nodeType  the type of the node
1614
 * @returns -1 in case of error, 1 if the current state in the stream is a
1615
 *    match and 0 otherwise.
1616
 */
1617
static int
1618
xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1619
          const xmlChar *name, const xmlChar *ns,
1620
860k
          int nodeType) {
1621
860k
    int ret = 0, final = 0, tmp, i, m, match, stepNr, desc;
1622
860k
    xmlStreamCompPtr comp;
1623
860k
    xmlStreamStep step;
1624
1625
860k
    if ((stream == NULL) || (stream->nbState < 0))
1626
0
        return(-1);
1627
1628
1.95M
    while (stream != NULL) {
1629
1.09M
  comp = stream->comp;
1630
1631
1.09M
  if ((nodeType == XML_ELEMENT_NODE) &&
1632
911k
      (name == NULL) && (ns == NULL)) {
1633
      /* We have a document node here (or a reset). */
1634
7.12k
      stream->nbState = 0;
1635
7.12k
      stream->level = 0;
1636
7.12k
      stream->blockLevel = -1;
1637
7.12k
      if (comp->flags & XML_STREAM_FROM_ROOT) {
1638
3.32k
    if (comp->nbStep == 0) {
1639
        /* TODO: We have a "/." here? */
1640
0
        ret = 1;
1641
3.32k
    } else {
1642
3.32k
        if ((comp->nbStep == 1) &&
1643
1.99k
      (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
1644
199
      (comp->steps[0].flags & XML_STREAM_STEP_DESC))
1645
199
        {
1646
      /*
1647
      * In the case of "//." the document node will match
1648
      * as well.
1649
      */
1650
199
      ret = 1;
1651
3.12k
        } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1652
2.90k
      if (xmlStreamCtxtAddState(stream, 0, 0) < 0)
1653
0
                            return(-1);
1654
2.90k
        }
1655
3.32k
    }
1656
3.32k
      }
1657
7.12k
      stream = stream->next;
1658
7.12k
      continue; /* while */
1659
7.12k
  }
1660
1661
  /*
1662
  * Fast check for ".".
1663
  */
1664
1.08M
  if (comp->nbStep == 0) {
1665
      /*
1666
       * / and . are handled at the XPath node set creation
1667
       * level by checking min depth
1668
       */
1669
214k
      if (stream->flags & XML_PATTERN_XPATH) {
1670
0
    stream = stream->next;
1671
0
    continue; /* while */
1672
0
      }
1673
      /*
1674
      * For non-pattern like evaluation like XML Schema IDCs
1675
      * or traditional XPath expressions, this will match if
1676
      * we are at the first level only, otherwise on every level.
1677
      */
1678
214k
      if ((nodeType != XML_ATTRIBUTE_NODE) &&
1679
179k
    (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1680
179k
    (stream->level == 0))) {
1681
45.6k
        ret = 1;
1682
45.6k
      }
1683
214k
      stream->level++;
1684
214k
      goto stream_next;
1685
214k
  }
1686
870k
  if (stream->blockLevel != -1) {
1687
      /*
1688
      * Skip blocked expressions.
1689
      */
1690
392k
      stream->level++;
1691
392k
      goto stream_next;
1692
392k
  }
1693
1694
477k
  if ((nodeType != XML_ELEMENT_NODE) &&
1695
82.9k
      (nodeType != XML_ATTRIBUTE_NODE) &&
1696
0
      ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) {
1697
      /*
1698
      * No need to process nodes of other types if we don't
1699
      * resolve to those types.
1700
      * TODO: Do we need to block the context here?
1701
      */
1702
0
      stream->level++;
1703
0
      goto stream_next;
1704
0
  }
1705
1706
  /*
1707
   * Check evolution of existing states
1708
   */
1709
477k
  i = 0;
1710
477k
  m = stream->nbState;
1711
481k
  while (i < m) {
1712
4.12k
      if ((comp->flags & XML_STREAM_DESC) == 0) {
1713
    /*
1714
    * If there is no "//", then only the last
1715
    * added state is of interest.
1716
    */
1717
3.56k
    stepNr = stream->states[2 * (stream->nbState -1)];
1718
    /*
1719
    * TODO: Security check, should not happen, remove it.
1720
    */
1721
3.56k
    if (stream->states[(2 * (stream->nbState -1)) + 1] <
1722
3.56k
        stream->level) {
1723
0
        return (-1);
1724
0
    }
1725
3.56k
    desc = 0;
1726
    /* loop-stopper */
1727
3.56k
    i = m;
1728
3.56k
      } else {
1729
    /*
1730
    * If there are "//", then we need to process every "//"
1731
    * occurring in the states, plus any other state for this
1732
    * level.
1733
    */
1734
558
    stepNr = stream->states[2 * i];
1735
1736
    /* TODO: should not happen anymore: dead states */
1737
558
    if (stepNr < 0)
1738
0
        goto next_state;
1739
1740
558
    tmp = stream->states[(2 * i) + 1];
1741
1742
    /* skip new states just added */
1743
558
    if (tmp > stream->level)
1744
0
        goto next_state;
1745
1746
    /* skip states at ancestor levels, except if "//" */
1747
558
    desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC;
1748
558
    if ((tmp < stream->level) && (!desc))
1749
7
        goto next_state;
1750
558
      }
1751
      /*
1752
      * Check for correct node-type.
1753
      */
1754
4.11k
      step = comp->steps[stepNr];
1755
4.11k
      if (step.nodeType != nodeType) {
1756
483
    if (step.nodeType == XML_ATTRIBUTE_NODE) {
1757
        /*
1758
        * Block this expression for deeper evaluation.
1759
        */
1760
473
        if ((comp->flags & XML_STREAM_DESC) == 0)
1761
276
      stream->blockLevel = stream->level +1;
1762
473
        goto next_state;
1763
473
    } else if (step.nodeType != XML_STREAM_ANY_NODE)
1764
10
        goto next_state;
1765
483
      }
1766
      /*
1767
      * Compare local/namespace-name.
1768
      */
1769
3.63k
      match = 0;
1770
3.63k
      if (step.nodeType == XML_STREAM_ANY_NODE) {
1771
0
    match = 1;
1772
3.63k
      } else if (step.name == NULL) {
1773
1.20k
    if (step.ns == NULL) {
1774
        /*
1775
        * This lets through all elements/attributes.
1776
        */
1777
866
        match = 1;
1778
866
    } else if (ns != NULL)
1779
0
        match = xmlStrEqual(step.ns, ns);
1780
2.42k
      } else if (((step.ns != NULL) == (ns != NULL)) &&
1781
2.09k
    (name != NULL) &&
1782
2.09k
    (step.name[0] == name[0]) &&
1783
1.08k
    xmlStrEqual(step.name, name) &&
1784
356
    ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
1785
356
      {
1786
356
    match = 1;
1787
356
      }
1788
3.63k
      if (match) {
1789
1.22k
    final = step.flags & XML_STREAM_STEP_FINAL;
1790
1.22k
                if (final) {
1791
605
                    ret = 1;
1792
617
                } else if (xmlStreamCtxtAddState(stream, stepNr + 1,
1793
617
                                                 stream->level + 1) < 0) {
1794
0
                    return(-1);
1795
0
                }
1796
1.22k
    if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
1797
        /*
1798
        * Check if we have a special case like "foo/bar//.", where
1799
        * "foo" is selected as well.
1800
        */
1801
3
        ret = 1;
1802
3
    }
1803
1.22k
      }
1804
3.63k
      if (((comp->flags & XML_STREAM_DESC) == 0) &&
1805
3.29k
    ((! match) || final))  {
1806
    /*
1807
    * Mark this expression as blocked for any evaluation at
1808
    * deeper levels. Note that this includes "/foo"
1809
    * expressions if the *pattern* behaviour is used.
1810
    */
1811
2.90k
    stream->blockLevel = stream->level +1;
1812
2.90k
      }
1813
4.12k
next_state:
1814
4.12k
      i++;
1815
4.12k
  }
1816
1817
477k
  stream->level++;
1818
1819
  /*
1820
  * Re/enter the expression.
1821
  * Don't reenter if it's an absolute expression like "/foo",
1822
  *   except "//foo".
1823
  */
1824
477k
  step = comp->steps[0];
1825
477k
  if (step.flags & XML_STREAM_STEP_ROOT)
1826
2.90k
      goto stream_next;
1827
1828
474k
  desc = step.flags & XML_STREAM_STEP_DESC;
1829
474k
  if (stream->flags & XML_PATTERN_NOTPATTERN) {
1830
      /*
1831
      * Re/enter the expression if it is a "descendant" one,
1832
      * or if we are at the 1st level of evaluation.
1833
      */
1834
1835
470k
      if (stream->level == 1) {
1836
55.5k
    if (XML_STREAM_XS_IDC(stream)) {
1837
        /*
1838
        * XS-IDC: The missing "self::node()" will always
1839
        * match the first given node.
1840
        */
1841
55.5k
        goto stream_next;
1842
55.5k
    } else
1843
0
        goto compare;
1844
55.5k
      }
1845
      /*
1846
      * A "//" is always reentrant.
1847
      */
1848
415k
      if (desc)
1849
186k
    goto compare;
1850
1851
      /*
1852
      * XS-IDC: Process the 2nd level, since the missing
1853
      * "self::node()" is responsible for the 2nd level being
1854
      * the real start level.
1855
      */
1856
228k
      if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
1857
64.0k
    goto compare;
1858
1859
164k
      goto stream_next;
1860
228k
  }
1861
1862
254k
compare:
1863
  /*
1864
  * Check expected node-type.
1865
  */
1866
254k
  if (step.nodeType != nodeType) {
1867
51.1k
      if (nodeType == XML_ATTRIBUTE_NODE)
1868
46.3k
    goto stream_next;
1869
4.75k
      else if (step.nodeType != XML_STREAM_ANY_NODE)
1870
4.56k
    goto stream_next;
1871
51.1k
  }
1872
  /*
1873
  * Compare local/namespace-name.
1874
  */
1875
204k
  match = 0;
1876
204k
  if (step.nodeType == XML_STREAM_ANY_NODE) {
1877
199
      match = 1;
1878
203k
  } else if (step.name == NULL) {
1879
1.47k
      if (step.ns == NULL) {
1880
    /*
1881
    * This lets through all elements/attributes.
1882
    */
1883
1.09k
    match = 1;
1884
1.09k
      } else if (ns != NULL)
1885
0
    match = xmlStrEqual(step.ns, ns);
1886
202k
  } else if (((step.ns != NULL) == (ns != NULL)) &&
1887
201k
      (name != NULL) &&
1888
201k
      (step.name[0] == name[0]) &&
1889
120k
      xmlStrEqual(step.name, name) &&
1890
83.9k
      ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
1891
83.9k
  {
1892
83.9k
      match = 1;
1893
83.9k
  }
1894
204k
  final = step.flags & XML_STREAM_STEP_FINAL;
1895
204k
  if (match) {
1896
85.2k
      if (final) {
1897
84.5k
    ret = 1;
1898
84.5k
            } else if (xmlStreamCtxtAddState(stream, 1, stream->level) < 0) {
1899
2
                return(-1);
1900
2
            }
1901
85.2k
      if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
1902
    /*
1903
    * Check if we have a special case like "foo//.", where
1904
    * "foo" is selected as well.
1905
    */
1906
2
    ret = 1;
1907
2
      }
1908
85.2k
  }
1909
204k
  if (((comp->flags & XML_STREAM_DESC) == 0) &&
1910
53.8k
      ((! match) || final))  {
1911
      /*
1912
      * Mark this expression as blocked for any evaluation at
1913
      * deeper levels.
1914
      */
1915
53.5k
      stream->blockLevel = stream->level;
1916
53.5k
  }
1917
1918
1.08M
stream_next:
1919
1.08M
        stream = stream->next;
1920
1.08M
    } /* while stream != NULL */
1921
1922
860k
    return(ret);
1923
860k
}
1924
1925
/**
1926
 * Push new data onto the stream. NOTE: if the call #xmlPatterncompile
1927
 * indicated a dictionary, then strings for name and ns will be expected
1928
 * to come from the dictionary.
1929
 * Both `name` and `ns` being NULL means the / i.e. the root of the document.
1930
 * This can also act as a reset.
1931
 * Otherwise the function will act as if it has been given an element-node.
1932
 *
1933
 * @param stream  the stream context
1934
 * @param name  the current name
1935
 * @param ns  the namespace name
1936
 * @returns -1 in case of error, 1 if the current state in the stream is a
1937
 *    match and 0 otherwise.
1938
 */
1939
int
1940
xmlStreamPush(xmlStreamCtxt *stream,
1941
738k
              const xmlChar *name, const xmlChar *ns) {
1942
738k
    return (xmlStreamPushInternal(stream, name, ns, XML_ELEMENT_NODE));
1943
738k
}
1944
1945
/**
1946
 * Push new data onto the stream. NOTE: if the call #xmlPatterncompile
1947
 * indicated a dictionary, then strings for name and ns will be expected
1948
 * to come from the dictionary.
1949
 * Both `name` and `ns` being NULL means the / i.e. the root of the document.
1950
 * This can also act as a reset.
1951
 * Different from #xmlStreamPush this function can be fed with nodes of type:
1952
 * element-, attribute-, text-, cdata-section-, comment- and
1953
 * processing-instruction-node.
1954
 *
1955
 * @param stream  the stream context
1956
 * @param name  the current name
1957
 * @param ns  the namespace name
1958
 * @param nodeType  the type of the node being pushed
1959
 * @returns -1 in case of error, 1 if the current state in the stream is a
1960
 *    match and 0 otherwise.
1961
 */
1962
int
1963
xmlStreamPushNode(xmlStreamCtxt *stream,
1964
      const xmlChar *name, const xmlChar *ns,
1965
      int nodeType)
1966
0
{
1967
0
    return (xmlStreamPushInternal(stream, name, ns,
1968
0
  nodeType));
1969
0
}
1970
1971
/**
1972
 * Push new attribute data onto the stream.
1973
 *
1974
 * NOTE: If the call to #xmlPatterncompile indicated a dictionary,
1975
 * then strings for `name` and `ns` will be expected to come from
1976
 * the dictionary.
1977
 *
1978
 * Both `name` and `ns` being NULL means the root of the document.
1979
 * This can also act as a reset. Otherwise the function will act as
1980
 * if it has been given an attribute-node.
1981
 *
1982
 * @param stream  the stream context
1983
 * @param name  the current name
1984
 * @param ns  the namespace name
1985
 * @returns -1 in case of error, 1 if the current state in the stream
1986
 * is a match and 0 otherwise.
1987
 */
1988
int
1989
xmlStreamPushAttr(xmlStreamCtxt *stream,
1990
121k
      const xmlChar *name, const xmlChar *ns) {
1991
121k
    return (xmlStreamPushInternal(stream, name, ns, XML_ATTRIBUTE_NODE));
1992
121k
}
1993
1994
/**
1995
 * push one level from the stream.
1996
 *
1997
 * @param stream  the stream context
1998
 * @returns -1 in case of error, 0 otherwise.
1999
 */
2000
int
2001
836k
xmlStreamPop(xmlStreamCtxt *stream) {
2002
836k
    int i, lev;
2003
2004
836k
    if (stream == NULL)
2005
0
        return(-1);
2006
1.88M
    while (stream != NULL) {
2007
  /*
2008
  * Reset block-level.
2009
  */
2010
1.04M
  if (stream->blockLevel == stream->level)
2011
56.3k
      stream->blockLevel = -1;
2012
2013
  /*
2014
   *  stream->level can be zero when XML_FINAL_IS_ANY_NODE is set
2015
   *  (see the thread at
2016
   *  http://mail.gnome.org/archives/xslt/2008-July/msg00027.html)
2017
   */
2018
1.04M
  if (stream->level)
2019
1.04M
      stream->level--;
2020
  /*
2021
   * Check evolution of existing states
2022
   */
2023
1.05M
  for (i = stream->nbState -1; i >= 0; i--) {
2024
      /* discard obsoleted states */
2025
7.09k
      lev = stream->states[(2 * i) + 1];
2026
7.09k
      if (lev > stream->level)
2027
1.28k
    stream->nbState--;
2028
7.09k
      if (lev <= stream->level)
2029
5.81k
    break;
2030
7.09k
  }
2031
1.04M
  stream = stream->next;
2032
1.04M
    }
2033
836k
    return(0);
2034
836k
}
2035
2036
/**
2037
 * Query if the streaming pattern additionally needs to be fed with
2038
 * text-, cdata-section-, comment- and processing-instruction-nodes.
2039
 * If the result is 0 then only element-nodes and attribute-nodes
2040
 * need to be pushed.
2041
 *
2042
 * @param streamCtxt  the stream context
2043
 * @returns 1 in case of need of nodes of the above described types,
2044
 *          0 otherwise. -1 on API errors.
2045
 */
2046
int
2047
xmlStreamWantsAnyNode(xmlStreamCtxt *streamCtxt)
2048
0
{
2049
0
    if (streamCtxt == NULL)
2050
0
  return(-1);
2051
0
    while (streamCtxt != NULL) {
2052
0
  if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
2053
0
      return(1);
2054
0
  streamCtxt = streamCtxt->next;
2055
0
    }
2056
0
    return(0);
2057
0
}
2058
2059
/************************************************************************
2060
 *                  *
2061
 *      The public interfaces       *
2062
 *                  *
2063
 ************************************************************************/
2064
2065
/**
2066
 * Compile a pattern.
2067
 *
2068
 * @since 2.13.0
2069
 *
2070
 * @param pattern  the pattern to compile
2071
 * @param dict  an optional dictionary for interned strings
2072
 * @param flags  compilation flags, see xmlPatternFlags
2073
 * @param namespaces  the prefix definitions, array of [URI, prefix] or NULL
2074
 * @param patternOut  output pattern
2075
 * @returns 0 on success, 1 on error, -1 if a memory allocation failed.
2076
 */
2077
int
2078
xmlPatternCompileSafe(const xmlChar *pattern, xmlDict *dict, int flags,
2079
34.9k
                      const xmlChar **namespaces, xmlPattern **patternOut) {
2080
34.9k
    xmlPatternPtr ret = NULL, cur;
2081
34.9k
    xmlPatParserContextPtr ctxt = NULL;
2082
34.9k
    const xmlChar *or, *start;
2083
34.9k
    xmlChar *tmp = NULL;
2084
34.9k
    int type = 0;
2085
34.9k
    int streamable = 1;
2086
34.9k
    int error;
2087
2088
34.9k
    if (patternOut == NULL)
2089
0
        return(1);
2090
2091
34.9k
    if (pattern == NULL) {
2092
0
        error = 1;
2093
0
        goto error;
2094
0
    }
2095
2096
34.9k
    start = pattern;
2097
34.9k
    or = start;
2098
80.0k
    while (*or != 0) {
2099
72.4k
  tmp = NULL;
2100
30.6M
  while ((*or != 0) && (*or != '|')) or++;
2101
72.4k
        if (*or == 0)
2102
22.9k
      ctxt = xmlNewPatParserContext(start, dict, namespaces);
2103
49.5k
  else {
2104
49.5k
      tmp = xmlStrndup(start, or - start);
2105
49.5k
      if (tmp != NULL) {
2106
49.5k
    ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2107
49.5k
      }
2108
49.5k
      or++;
2109
49.5k
  }
2110
72.4k
  if (ctxt == NULL) {
2111
49
            error = -1;
2112
49
            goto error;
2113
49
        }
2114
72.4k
  cur = xmlNewPattern();
2115
72.4k
  if (cur == NULL) {
2116
8
            error = -1;
2117
8
            goto error;
2118
8
        }
2119
  /*
2120
  * Assign string dict.
2121
  */
2122
72.4k
  if (dict) {
2123
0
      cur->dict = dict;
2124
0
      xmlDictReference(dict);
2125
0
  }
2126
72.4k
  if (ret == NULL)
2127
34.7k
      ret = cur;
2128
37.6k
  else {
2129
37.6k
      cur->next = ret->next;
2130
37.6k
      ret->next = cur;
2131
37.6k
  }
2132
72.4k
  cur->flags = flags;
2133
72.4k
  ctxt->comp = cur;
2134
2135
72.4k
  if (XML_STREAM_XS_IDC(cur))
2136
58.5k
      xmlCompileIDCXPathPath(ctxt);
2137
13.8k
  else
2138
13.8k
      xmlCompilePathPattern(ctxt);
2139
72.4k
  if (ctxt->error != 0) {
2140
27.2k
            error = ctxt->error;
2141
27.2k
      goto error;
2142
27.2k
        }
2143
45.1k
  xmlFreePatParserContext(ctxt);
2144
45.1k
  ctxt = NULL;
2145
2146
2147
45.1k
        if (streamable) {
2148
44.4k
      if (type == 0) {
2149
23.4k
          type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2150
23.4k
      } else if (type == PAT_FROM_ROOT) {
2151
5.82k
          if (cur->flags & PAT_FROM_CUR)
2152
23
        streamable = 0;
2153
15.1k
      } else if (type == PAT_FROM_CUR) {
2154
15.1k
          if (cur->flags & PAT_FROM_ROOT)
2155
11
        streamable = 0;
2156
15.1k
      }
2157
44.4k
  }
2158
45.1k
  if (streamable) {
2159
44.4k
      error = xmlStreamCompile(cur);
2160
44.4k
            if (error != 0)
2161
14
                goto error;
2162
44.4k
        }
2163
45.1k
  error = xmlReversePattern(cur);
2164
45.1k
        if (error != 0)
2165
6
      goto error;
2166
45.1k
  if (tmp != NULL) {
2167
37.6k
      xmlFree(tmp);
2168
37.6k
      tmp = NULL;
2169
37.6k
  }
2170
45.1k
  start = or;
2171
45.1k
    }
2172
7.62k
    if (streamable == 0) {
2173
32
        cur = ret;
2174
976
  while (cur != NULL) {
2175
944
      if (cur->stream != NULL) {
2176
253
    xmlFreeStreamComp(cur->stream);
2177
253
    cur->stream = NULL;
2178
253
      }
2179
944
      cur = cur->next;
2180
944
  }
2181
32
    }
2182
2183
7.62k
    *patternOut = ret;
2184
7.62k
    return(0);
2185
27.3k
error:
2186
27.3k
    if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2187
27.3k
    if (ret != NULL) xmlFreePattern(ret);
2188
27.3k
    if (tmp != NULL) xmlFree(tmp);
2189
27.3k
    *patternOut = NULL;
2190
27.3k
    return(error);
2191
34.9k
}
2192
2193
/**
2194
 * Compile a pattern.
2195
 *
2196
 * @param pattern  the pattern to compile
2197
 * @param dict  an optional dictionary for interned strings
2198
 * @param flags  compilation flags, see xmlPatternFlags
2199
 * @param namespaces  the prefix definitions, array of [URI, prefix] or NULL
2200
 * @returns the compiled form of the pattern or NULL in case of error
2201
 */
2202
xmlPattern *
2203
xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
2204
33.2k
                  const xmlChar **namespaces) {
2205
33.2k
    xmlPatternPtr ret;
2206
33.2k
    xmlPatternCompileSafe(pattern, dict, flags, namespaces, &ret);
2207
33.2k
    return(ret);
2208
33.2k
}
2209
2210
/**
2211
 * Test whether the node matches the pattern
2212
 *
2213
 * @param comp  the precompiled pattern
2214
 * @param node  a node
2215
 * @returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2216
 */
2217
int
2218
xmlPatternMatch(xmlPattern *comp, xmlNode *node)
2219
441
{
2220
441
    int ret = 0;
2221
2222
441
    if ((comp == NULL) || (node == NULL))
2223
0
        return(-1);
2224
2225
5.48k
    while (comp != NULL) {
2226
5.14k
        ret = xmlPatMatch(comp, node);
2227
5.14k
  if (ret != 0)
2228
103
      return(ret);
2229
5.04k
  comp = comp->next;
2230
5.04k
    }
2231
338
    return(ret);
2232
441
}
2233
2234
/**
2235
 * Get a streaming context for that pattern
2236
 * Use #xmlFreeStreamCtxt to free the context.
2237
 *
2238
 * @param comp  the precompiled pattern
2239
 * @returns a pointer to the context or NULL in case of failure
2240
 */
2241
xmlStreamCtxt *
2242
xmlPatternGetStreamCtxt(xmlPattern *comp)
2243
95.8k
{
2244
95.8k
    xmlStreamCtxtPtr ret = NULL, cur;
2245
2246
95.8k
    if ((comp == NULL) || (comp->stream == NULL))
2247
20
        return(NULL);
2248
2249
204k
    while (comp != NULL) {
2250
108k
        if (comp->stream == NULL)
2251
12
      goto failed;
2252
108k
  cur = xmlNewStreamCtxt(comp->stream);
2253
108k
  if (cur == NULL)
2254
16
      goto failed;
2255
108k
  if (ret == NULL)
2256
95.8k
      ret = cur;
2257
12.4k
  else {
2258
12.4k
      cur->next = ret->next;
2259
12.4k
      ret->next = cur;
2260
12.4k
  }
2261
108k
  cur->flags = comp->flags;
2262
108k
  comp = comp->next;
2263
108k
    }
2264
95.8k
    return(ret);
2265
28
failed:
2266
28
    xmlFreeStreamCtxt(ret);
2267
28
    return(NULL);
2268
95.8k
}
2269
2270
/**
2271
 * Check if the pattern is streamable i.e. #xmlPatternGetStreamCtxt
2272
 * should work.
2273
 *
2274
 * @param comp  the precompiled pattern
2275
 * @returns 1 if streamable, 0 if not and -1 in case of error.
2276
 */
2277
int
2278
0
xmlPatternStreamable(xmlPattern *comp) {
2279
0
    if (comp == NULL)
2280
0
        return(-1);
2281
0
    while (comp != NULL) {
2282
0
        if (comp->stream == NULL)
2283
0
      return(0);
2284
0
  comp = comp->next;
2285
0
    }
2286
0
    return(1);
2287
0
}
2288
2289
/**
2290
 * Check the maximum depth reachable by a pattern
2291
 *
2292
 * @param comp  the precompiled pattern
2293
 * @returns -2 if no limit (using //), otherwise the depth,
2294
 *         and -1 in case of error
2295
 */
2296
int
2297
0
xmlPatternMaxDepth(xmlPattern *comp) {
2298
0
    int ret = 0, i;
2299
0
    if (comp == NULL)
2300
0
        return(-1);
2301
0
    while (comp != NULL) {
2302
0
        if (comp->stream == NULL)
2303
0
      return(-1);
2304
0
  for (i = 0;i < comp->stream->nbStep;i++)
2305
0
      if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2306
0
          return(-2);
2307
0
  if (comp->stream->nbStep > ret)
2308
0
      ret = comp->stream->nbStep;
2309
0
  comp = comp->next;
2310
0
    }
2311
0
    return(ret);
2312
0
}
2313
2314
/**
2315
 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2316
 * part of the set.
2317
 *
2318
 * @param comp  the precompiled pattern
2319
 * @returns -1 in case of error otherwise the depth,
2320
 */
2321
int
2322
0
xmlPatternMinDepth(xmlPattern *comp) {
2323
0
    int ret = 12345678;
2324
0
    if (comp == NULL)
2325
0
        return(-1);
2326
0
    while (comp != NULL) {
2327
0
        if (comp->stream == NULL)
2328
0
      return(-1);
2329
0
  if (comp->stream->nbStep < ret)
2330
0
      ret = comp->stream->nbStep;
2331
0
  if (ret == 0)
2332
0
      return(0);
2333
0
  comp = comp->next;
2334
0
    }
2335
0
    return(ret);
2336
0
}
2337
2338
/**
2339
 * Check if the pattern must be looked at from the root.
2340
 *
2341
 * @param comp  the precompiled pattern
2342
 * @returns 1 if true, 0 if false and -1 in case of error
2343
 */
2344
int
2345
0
xmlPatternFromRoot(xmlPattern *comp) {
2346
0
    if (comp == NULL)
2347
0
        return(-1);
2348
0
    while (comp != NULL) {
2349
0
        if (comp->stream == NULL)
2350
0
      return(-1);
2351
0
  if (comp->flags & PAT_FROM_ROOT)
2352
0
      return(1);
2353
0
  comp = comp->next;
2354
0
    }
2355
0
    return(0);
2356
2357
0
}
2358
2359
#endif /* LIBXML_PATTERN_ENABLED */