Coverage Report

Created: 2024-08-17 06:44

/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
53.4k
#define XML_STREAM_STEP_DESC  1
49
39.4k
#define XML_STREAM_STEP_FINAL 2
50
38.1k
#define XML_STREAM_STEP_ROOT  4
51
0
#define XML_STREAM_STEP_ATTR  8
52
137
#define XML_STREAM_STEP_NODE  16
53
2.44k
#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
14.3k
#define XML_STREAM_FINAL_IS_ANY_NODE 1<<14
62
1.88k
#define XML_STREAM_FROM_ROOT 1<<15
63
52.6k
#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
39.4k
#define XML_STREAM_ANY_NODE 100
70
71
53.7k
#define XML_PATTERN_NOTPATTERN  (XML_PATTERN_XPATH | \
72
53.7k
         XML_PATTERN_XSSEL | \
73
53.7k
         XML_PATTERN_XSFIELD)
74
75
29.0k
#define XML_STREAM_XS_IDC(c) ((c)->flags & \
76
29.0k
    (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
77
78
0
#define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL)
79
80
#define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD)
81
82
#define XML_PAT_COPY_NSNAME(c, r, nsname) \
83
0
    if ((c)->comp->dict) \
84
0
  r = (xmlChar *) xmlDictLookup((c)->comp->dict, BAD_CAST nsname, -1); \
85
0
    else r = xmlStrdup(BAD_CAST nsname);
86
87
152
#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
38.5k
#define PAT_FROM_ROOT (1<<8)
162
24.1k
#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
21.2k
xmlNewPattern(void) {
204
21.2k
    xmlPatternPtr cur;
205
206
21.2k
    cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
207
21.2k
    if (cur == NULL) {
208
0
  ERROR(NULL, NULL, NULL,
209
0
    "xmlNewPattern : malloc failed\n");
210
0
  return(NULL);
211
0
    }
212
21.2k
    memset(cur, 0, sizeof(xmlPattern));
213
21.2k
    cur->maxStep = 10;
214
21.2k
    cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
215
21.2k
    if (cur->steps == NULL) {
216
0
        xmlFree(cur);
217
0
  ERROR(NULL, NULL, NULL,
218
0
    "xmlNewPattern : malloc failed\n");
219
0
  return(NULL);
220
0
    }
221
21.2k
    return(cur);
222
21.2k
}
223
224
/**
225
 * xmlFreePattern:
226
 * @comp:  an XSLT comp
227
 *
228
 * Free up the memory allocated by @comp
229
 */
230
void
231
5.83k
xmlFreePattern(xmlPatternPtr comp) {
232
5.83k
    xmlFreePatternList(comp);
233
5.83k
}
234
235
static void
236
21.2k
xmlFreePatternInternal(xmlPatternPtr comp) {
237
21.2k
    xmlStepOpPtr op;
238
21.2k
    int i;
239
240
21.2k
    if (comp == NULL)
241
0
  return;
242
21.2k
    if (comp->stream != NULL)
243
8.54k
        xmlFreeStreamComp(comp->stream);
244
21.2k
    if (comp->pattern != NULL)
245
0
  xmlFree((xmlChar *)comp->pattern);
246
21.2k
    if (comp->steps != NULL) {
247
21.2k
        if (comp->dict == NULL) {
248
108k
      for (i = 0;i < comp->nbStep;i++) {
249
87.6k
    op = &comp->steps[i];
250
87.6k
    if (op->value != NULL)
251
35.8k
        xmlFree((xmlChar *) op->value);
252
87.6k
    if (op->value2 != NULL)
253
0
        xmlFree((xmlChar *) op->value2);
254
87.6k
      }
255
21.2k
  }
256
21.2k
  xmlFree(comp->steps);
257
21.2k
    }
258
21.2k
    if (comp->dict != NULL)
259
0
        xmlDictFree(comp->dict);
260
261
21.2k
    memset(comp, -1, sizeof(xmlPattern));
262
21.2k
    xmlFree(comp);
263
21.2k
}
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
6.30k
xmlFreePatternList(xmlPatternPtr comp) {
273
6.30k
    xmlPatternPtr cur;
274
275
27.5k
    while (comp != NULL) {
276
21.2k
  cur = comp;
277
21.2k
  comp = comp->next;
278
21.2k
  cur->next = NULL;
279
21.2k
  xmlFreePatternInternal(cur);
280
21.2k
    }
281
6.30k
}
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
21.2k
                       const xmlChar **namespaces) {
297
21.2k
    xmlPatParserContextPtr cur;
298
299
21.2k
    if (pattern == NULL)
300
0
        return(NULL);
301
302
21.2k
    cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
303
21.2k
    if (cur == NULL) {
304
0
  ERROR(NULL, NULL, NULL,
305
0
    "xmlNewPatParserContext : malloc failed\n");
306
0
  return(NULL);
307
0
    }
308
21.2k
    memset(cur, 0, sizeof(xmlPatParserContext));
309
21.2k
    cur->dict = dict;
310
21.2k
    cur->cur = pattern;
311
21.2k
    cur->base = pattern;
312
21.2k
    if (namespaces != NULL) {
313
0
        int i;
314
0
        for (i = 0;namespaces[2 * i] != NULL;i++)
315
0
            ;
316
0
        cur->nb_namespaces = i;
317
21.2k
    } else {
318
21.2k
        cur->nb_namespaces = 0;
319
21.2k
    }
320
21.2k
    cur->namespaces = namespaces;
321
21.2k
    return(cur);
322
21.2k
}
323
324
/**
325
 * xmlFreePatParserContext:
326
 * @ctxt:  an XSLT parser context
327
 *
328
 * Free up the memory allocated by @ctxt
329
 */
330
static void
331
21.2k
xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
332
21.2k
    if (ctxt == NULL)
333
0
  return;
334
21.2k
    memset(ctxt, -1, sizeof(xmlPatParserContext));
335
21.2k
    xmlFree(ctxt);
336
21.2k
}
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
74.6k
{
354
74.6k
    if (comp->nbStep >= comp->maxStep) {
355
771
        xmlStepOpPtr temp;
356
771
  temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
357
771
                                   sizeof(xmlStepOp));
358
771
        if (temp == NULL) {
359
0
      ERROR(ctxt, NULL, NULL,
360
0
           "xmlPatternAdd: realloc failed\n");
361
0
      return (-1);
362
0
  }
363
771
  comp->steps = temp;
364
771
  comp->maxStep *= 2;
365
771
    }
366
74.6k
    comp->steps[comp->nbStep].op = op;
367
74.6k
    comp->steps[comp->nbStep].value = value;
368
74.6k
    comp->steps[comp->nbStep].value2 = value2;
369
74.6k
    comp->nbStep++;
370
74.6k
    return (0);
371
74.6k
}
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
18.4k
xmlReversePattern(xmlPatternPtr comp) {
412
18.4k
    int i, j;
413
414
    /*
415
     * remove the leading // for //a or .//a
416
     */
417
18.4k
    if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
418
27.2k
        for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
419
21.7k
      comp->steps[i].value = comp->steps[j].value;
420
21.7k
      comp->steps[i].value2 = comp->steps[j].value2;
421
21.7k
      comp->steps[i].op = comp->steps[j].op;
422
21.7k
  }
423
5.48k
  comp->nbStep--;
424
5.48k
    }
425
18.4k
    if (comp->nbStep >= comp->maxStep) {
426
269
        xmlStepOpPtr temp;
427
269
  temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
428
269
                                   sizeof(xmlStepOp));
429
269
        if (temp == NULL) {
430
0
      ERROR(ctxt, NULL, NULL,
431
0
           "xmlReversePattern: realloc failed\n");
432
0
      return (-1);
433
0
  }
434
269
  comp->steps = temp;
435
269
  comp->maxStep *= 2;
436
269
    }
437
18.4k
    i = 0;
438
18.4k
    j = comp->nbStep - 1;
439
43.0k
    while (j > i) {
440
24.5k
  register const xmlChar *tmp;
441
24.5k
  register xmlPatOp op;
442
24.5k
  tmp = comp->steps[i].value;
443
24.5k
  comp->steps[i].value = comp->steps[j].value;
444
24.5k
  comp->steps[j].value = tmp;
445
24.5k
  tmp = comp->steps[i].value2;
446
24.5k
  comp->steps[i].value2 = comp->steps[j].value2;
447
24.5k
  comp->steps[j].value2 = tmp;
448
24.5k
  op = comp->steps[i].op;
449
24.5k
  comp->steps[i].op = comp->steps[j].op;
450
24.5k
  comp->steps[j].op = op;
451
24.5k
  j--;
452
24.5k
  i++;
453
24.5k
    }
454
18.4k
    comp->steps[comp->nbStep].value = NULL;
455
18.4k
    comp->steps[comp->nbStep].value2 = NULL;
456
18.4k
    comp->steps[comp->nbStep++].op = XML_OP_END;
457
18.4k
    return(0);
458
18.4k
}
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
361k
#define CUR (*ctxt->cur)
708
#define SKIP(val) ctxt->cur += (val)
709
34.9k
#define NXT(val) ctxt->cur[(val)]
710
#define PEEKPREV(val) ctxt->cur[-(val)]
711
73.4k
#define CUR_PTR ctxt->cur
712
713
#define SKIP_BLANKS             \
714
152k
    while (IS_BLANK_CH(CUR)) NEXT
715
716
#define CURRENT (*ctxt->cur)
717
118k
#define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
718
719
720
#define PUSH(op, val, val2)           \
721
74.6k
    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
0
xmlPatScanName(xmlPatParserContextPtr ctxt) {
814
0
    const xmlChar *q, *cur;
815
0
    xmlChar *ret = NULL;
816
0
    int val, len;
817
818
0
    SKIP_BLANKS;
819
820
0
    cur = q = CUR_PTR;
821
0
    val = xmlStringCurrentChar(NULL, cur, &len);
822
0
    if (!IS_LETTER(val) && (val != '_') && (val != ':'))
823
0
  return(NULL);
824
825
0
    while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
826
0
           (val == '.') || (val == '-') ||
827
0
     (val == '_') ||
828
0
     (IS_COMBINING(val)) ||
829
0
     (IS_EXTENDER(val))) {
830
0
  cur += len;
831
0
  val = xmlStringCurrentChar(NULL, cur, &len);
832
0
    }
833
0
    if (ctxt->dict)
834
0
  ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
835
0
    else
836
0
  ret = xmlStrndup(q, cur - q);
837
0
    CUR_PTR = cur;
838
0
    return(ret);
839
0
}
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
37.4k
xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
852
37.4k
    const xmlChar *q, *cur;
853
37.4k
    xmlChar *ret = NULL;
854
37.4k
    int val, len;
855
856
37.4k
    SKIP_BLANKS;
857
858
37.4k
    cur = q = CUR_PTR;
859
37.4k
    val = xmlStringCurrentChar(NULL, cur, &len);
860
37.4k
    if (!IS_LETTER(val) && (val != '_'))
861
1.40k
  return(NULL);
862
863
158k
    while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
864
158k
           (val == '.') || (val == '-') ||
865
158k
     (val == '_') ||
866
158k
     (IS_COMBINING(val)) ||
867
158k
     (IS_EXTENDER(val))) {
868
122k
  cur += len;
869
122k
  val = xmlStringCurrentChar(NULL, cur, &len);
870
122k
    }
871
36.0k
    if (ctxt->dict)
872
0
  ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
873
36.0k
    else
874
36.0k
  ret = xmlStrndup(q, cur - q);
875
36.0k
    CUR_PTR = cur;
876
36.0k
    return(ret);
877
37.4k
}
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
0
xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
913
0
    xmlChar *token = NULL;
914
0
    xmlChar *name = NULL;
915
0
    xmlChar *URL = NULL;
916
917
0
    SKIP_BLANKS;
918
0
    name = xmlPatScanNCName(ctxt);
919
0
    if (name == NULL) {
920
0
  if (CUR == '*') {
921
0
      PUSH(XML_OP_ATTR, NULL, NULL);
922
0
      NEXT;
923
0
  } else {
924
0
      ERROR(NULL, NULL, NULL,
925
0
    "xmlCompileAttributeTest : Name expected\n");
926
0
      ctxt->error = 1;
927
0
  }
928
0
  return;
929
0
    }
930
0
    if (CUR == ':') {
931
0
  int i;
932
0
  xmlChar *prefix = name;
933
934
0
  NEXT;
935
936
0
  if (IS_BLANK_CH(CUR)) {
937
0
      ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
938
0
      XML_PAT_FREE_STRING(ctxt, prefix);
939
0
      ctxt->error = 1;
940
0
      goto error;
941
0
  }
942
  /*
943
  * This is a namespace match
944
  */
945
0
  token = xmlPatScanName(ctxt);
946
0
  if ((prefix[0] == 'x') &&
947
0
      (prefix[1] == 'm') &&
948
0
      (prefix[2] == 'l') &&
949
0
      (prefix[3] == 0))
950
0
  {
951
0
      XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
952
0
  } else {
953
0
      for (i = 0;i < ctxt->nb_namespaces;i++) {
954
0
    if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
955
0
        XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
956
0
        break;
957
0
    }
958
0
      }
959
0
      if (i >= ctxt->nb_namespaces) {
960
0
    ERROR5(NULL, NULL, NULL,
961
0
        "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
962
0
        prefix);
963
0
          XML_PAT_FREE_STRING(ctxt, prefix);
964
0
    ctxt->error = 1;
965
0
    goto error;
966
0
      }
967
0
  }
968
0
  XML_PAT_FREE_STRING(ctxt, prefix);
969
0
  if (token == NULL) {
970
0
      if (CUR == '*') {
971
0
    NEXT;
972
0
    PUSH(XML_OP_ATTR, NULL, URL);
973
0
      } else {
974
0
    ERROR(NULL, NULL, NULL,
975
0
        "xmlCompileAttributeTest : Name expected\n");
976
0
    ctxt->error = 1;
977
0
    goto error;
978
0
      }
979
0
  } else {
980
0
      PUSH(XML_OP_ATTR, token, URL);
981
0
  }
982
0
    } else {
983
0
  PUSH(XML_OP_ATTR, name, NULL);
984
0
    }
985
0
    return;
986
0
error:
987
0
    if (URL != NULL)
988
0
  XML_PAT_FREE_STRING(ctxt, URL)
989
0
    if (token != NULL)
990
0
  XML_PAT_FREE_STRING(ctxt, token);
991
0
}
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
42.9k
xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
1006
42.9k
    xmlChar *token = NULL;
1007
42.9k
    xmlChar *name = NULL;
1008
42.9k
    xmlChar *URL = NULL;
1009
42.9k
    int hasBlanks = 0;
1010
1011
42.9k
    SKIP_BLANKS;
1012
42.9k
    if (CUR == '.') {
1013
  /*
1014
  * Context node.
1015
  */
1016
5.47k
  NEXT;
1017
5.47k
  PUSH(XML_OP_ELEM, NULL, NULL);
1018
5.47k
  return;
1019
5.47k
    }
1020
37.4k
    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
37.4k
    name = xmlPatScanNCName(ctxt);
1037
37.4k
    if (name == NULL) {
1038
1.40k
  if (CUR == '*') {
1039
241
      NEXT;
1040
241
      PUSH(XML_OP_ALL, NULL, NULL);
1041
241
      return;
1042
1.16k
  } else {
1043
1.16k
      ERROR(NULL, NULL, NULL,
1044
1.16k
        "xmlCompileStepPattern : Name expected\n");
1045
1.16k
      ctxt->error = 1;
1046
1.16k
      return;
1047
1.16k
  }
1048
1.40k
    }
1049
36.0k
    if (IS_BLANK_CH(CUR)) {
1050
516
  hasBlanks = 1;
1051
516
  SKIP_BLANKS;
1052
516
    }
1053
36.0k
    if (CUR == ':') {
1054
0
  NEXT;
1055
0
  if (CUR != ':') {
1056
0
      xmlChar *prefix = name;
1057
0
      int i;
1058
1059
0
      if (hasBlanks || IS_BLANK_CH(CUR)) {
1060
0
    ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1061
0
    ctxt->error = 1;
1062
0
    goto error;
1063
0
      }
1064
      /*
1065
       * This is a namespace match
1066
       */
1067
0
      token = xmlPatScanName(ctxt);
1068
0
      if ((prefix[0] == 'x') &&
1069
0
    (prefix[1] == 'm') &&
1070
0
    (prefix[2] == 'l') &&
1071
0
    (prefix[3] == 0))
1072
0
      {
1073
0
    XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1074
0
      } else {
1075
0
    for (i = 0;i < ctxt->nb_namespaces;i++) {
1076
0
        if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1077
0
      XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1078
0
      break;
1079
0
        }
1080
0
    }
1081
0
    if (i >= ctxt->nb_namespaces) {
1082
0
        ERROR5(NULL, NULL, NULL,
1083
0
      "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1084
0
      prefix);
1085
0
        ctxt->error = 1;
1086
0
        goto error;
1087
0
    }
1088
0
      }
1089
0
      XML_PAT_FREE_STRING(ctxt, prefix);
1090
0
      name = NULL;
1091
0
      if (token == NULL) {
1092
0
    if (CUR == '*') {
1093
0
        NEXT;
1094
0
        PUSH(XML_OP_NS, URL, NULL);
1095
0
    } else {
1096
0
        ERROR(NULL, NULL, NULL,
1097
0
          "xmlCompileStepPattern : Name expected\n");
1098
0
        ctxt->error = 1;
1099
0
        goto error;
1100
0
    }
1101
0
      } else {
1102
0
    PUSH(XML_OP_ELEM, token, URL);
1103
0
      }
1104
0
  } else {
1105
0
      NEXT;
1106
0
      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
0
      } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1175
0
    XML_PAT_FREE_STRING(ctxt, name)
1176
0
    name = NULL;
1177
0
    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
0
    xmlCompileAttributeTest(ctxt);
1184
0
    if (ctxt->error != 0)
1185
0
        goto error;
1186
0
    return;
1187
0
      } else {
1188
0
    ERROR5(NULL, NULL, NULL,
1189
0
        "The 'element' or 'attribute' axis is expected.\n", NULL);
1190
0
    ctxt->error = 1;
1191
0
    goto error;
1192
0
      }
1193
0
  }
1194
36.0k
    } else if (CUR == '*') {
1195
152
        if (name != NULL) {
1196
152
      ctxt->error = 1;
1197
152
      goto error;
1198
152
  }
1199
0
  NEXT;
1200
0
  PUSH(XML_OP_ALL, token, NULL);
1201
35.8k
    } else {
1202
35.8k
  PUSH(XML_OP_ELEM, name, NULL);
1203
35.8k
    }
1204
35.8k
    return;
1205
35.8k
error:
1206
152
    if (URL != NULL)
1207
0
  XML_PAT_FREE_STRING(ctxt, URL)
1208
152
    if (token != NULL)
1209
0
  XML_PAT_FREE_STRING(ctxt, token)
1210
152
    if (name != NULL)
1211
152
  XML_PAT_FREE_STRING(ctxt, name)
1212
152
}
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
21.2k
xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1225
21.2k
    SKIP_BLANKS;
1226
21.2k
    if (CUR == '/') {
1227
6.60k
        ctxt->comp->flags |= PAT_FROM_ROOT;
1228
14.6k
    } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
1229
14.6k
        ctxt->comp->flags |= PAT_FROM_CUR;
1230
14.6k
    }
1231
1232
21.2k
    if ((CUR == '/') && (NXT(1) == '/')) {
1233
2.86k
  PUSH(XML_OP_ANCESTOR, NULL, NULL);
1234
2.86k
  NEXT;
1235
2.86k
  NEXT;
1236
18.3k
    } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1237
2.87k
  PUSH(XML_OP_ANCESTOR, NULL, NULL);
1238
2.87k
  NEXT;
1239
2.87k
  NEXT;
1240
2.87k
  NEXT;
1241
  /* Check for incompleteness. */
1242
2.87k
  SKIP_BLANKS;
1243
2.87k
  if (CUR == 0) {
1244
15
      ERROR5(NULL, NULL, NULL,
1245
15
         "Incomplete expression '%s'.\n", ctxt->base);
1246
15
      ctxt->error = 1;
1247
15
      goto error;
1248
15
  }
1249
2.87k
    }
1250
21.2k
    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
21.2k
    } else {
1261
21.2k
        if (CUR == '/') {
1262
5.55k
      PUSH(XML_OP_ROOT, NULL, NULL);
1263
5.55k
      NEXT;
1264
      /* Check for incompleteness. */
1265
5.55k
      SKIP_BLANKS;
1266
5.55k
      if (CUR == 0) {
1267
59
    ERROR5(NULL, NULL, NULL,
1268
59
        "Incomplete expression '%s'.\n", ctxt->base);
1269
59
    ctxt->error = 1;
1270
59
    goto error;
1271
59
      }
1272
5.55k
  }
1273
21.1k
  xmlCompileStepPattern(ctxt);
1274
21.1k
  if (ctxt->error != 0)
1275
1.11k
      goto error;
1276
20.0k
  SKIP_BLANKS;
1277
41.5k
  while (CUR == '/') {
1278
21.7k
      if (NXT(1) == '/') {
1279
2.21k
          PUSH(XML_OP_ANCESTOR, NULL, NULL);
1280
2.21k
    NEXT;
1281
2.21k
    NEXT;
1282
2.21k
    SKIP_BLANKS;
1283
2.21k
    xmlCompileStepPattern(ctxt);
1284
2.21k
    if (ctxt->error != 0)
1285
102
        goto error;
1286
19.5k
      } else {
1287
19.5k
          PUSH(XML_OP_PARENT, NULL, NULL);
1288
19.5k
    NEXT;
1289
19.5k
    SKIP_BLANKS;
1290
19.5k
    if (CUR == 0) {
1291
45
        ERROR5(NULL, NULL, NULL,
1292
45
        "Incomplete expression '%s'.\n", ctxt->base);
1293
45
        ctxt->error = 1;
1294
45
        goto error;
1295
45
    }
1296
19.5k
    xmlCompileStepPattern(ctxt);
1297
19.5k
    if (ctxt->error != 0)
1298
98
        goto error;
1299
19.5k
      }
1300
21.7k
  }
1301
20.0k
    }
1302
19.8k
    if (CUR != 0) {
1303
1.39k
  ERROR5(NULL, NULL, NULL,
1304
1.39k
         "Failed to compile pattern %s\n", ctxt->base);
1305
1.39k
  ctxt->error = 1;
1306
1.39k
    }
1307
21.2k
error:
1308
21.2k
    return;
1309
19.8k
}
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
9.21k
xmlNewStreamComp(int size) {
1484
9.21k
    xmlStreamCompPtr cur;
1485
1486
9.21k
    if (size < 4)
1487
7.29k
        size  = 4;
1488
1489
9.21k
    cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1490
9.21k
    if (cur == NULL) {
1491
0
  ERROR(NULL, NULL, NULL,
1492
0
    "xmlNewStreamComp: malloc failed\n");
1493
0
  return(NULL);
1494
0
    }
1495
9.21k
    memset(cur, 0, sizeof(xmlStreamComp));
1496
9.21k
    cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1497
9.21k
    if (cur->steps == NULL) {
1498
0
  xmlFree(cur);
1499
0
  ERROR(NULL, NULL, NULL,
1500
0
        "xmlNewStreamComp: malloc failed\n");
1501
0
  return(NULL);
1502
0
    }
1503
9.21k
    cur->nbStep = 0;
1504
9.21k
    cur->maxStep = size;
1505
9.21k
    return(cur);
1506
9.21k
}
1507
1508
/**
1509
 * xmlFreeStreamComp:
1510
 * @comp: the compiled pattern for streaming
1511
 *
1512
 * Free the compiled pattern for streaming
1513
 */
1514
static void
1515
9.21k
xmlFreeStreamComp(xmlStreamCompPtr comp) {
1516
9.21k
    if (comp != NULL) {
1517
9.21k
        if (comp->steps != NULL)
1518
9.21k
      xmlFree(comp->steps);
1519
9.21k
  if (comp->dict != NULL)
1520
0
      xmlDictFree(comp->dict);
1521
9.21k
        xmlFree(comp);
1522
9.21k
    }
1523
9.21k
}
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
15.6k
                     const xmlChar *ns, int nodeType, int flags) {
1539
15.6k
    xmlStreamStepPtr cur;
1540
1541
15.6k
    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
15.6k
    cur = &comp->steps[comp->nbStep++];
1553
15.6k
    cur->flags = flags;
1554
15.6k
    cur->name = name;
1555
15.6k
    cur->ns = ns;
1556
15.6k
    cur->nodeType = nodeType;
1557
15.6k
    return(comp->nbStep - 1);
1558
15.6k
}
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
9.21k
xmlStreamCompile(xmlPatternPtr comp) {
1570
9.21k
    xmlStreamCompPtr stream;
1571
9.21k
    int i, s = 0, root = 0, flags = 0, prevs = -1;
1572
9.21k
    xmlStepOp step;
1573
1574
9.21k
    if ((comp == NULL) || (comp->steps == NULL))
1575
0
        return(-1);
1576
    /*
1577
     * special case for .
1578
     */
1579
9.21k
    if ((comp->nbStep == 1) &&
1580
9.21k
        (comp->steps[0].op == XML_OP_ELEM) &&
1581
9.21k
  (comp->steps[0].value == NULL) &&
1582
9.21k
  (comp->steps[0].value2 == NULL)) {
1583
127
  stream = xmlNewStreamComp(0);
1584
127
  if (stream == NULL)
1585
0
      return(-1);
1586
  /* Note that the stream will have no steps in this case. */
1587
127
  stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1588
127
  comp->stream = stream;
1589
127
  return(0);
1590
127
    }
1591
1592
9.08k
    stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1593
9.08k
    if (stream == NULL)
1594
0
        return(-1);
1595
9.08k
    if (comp->dict != NULL) {
1596
0
        stream->dict = comp->dict;
1597
0
  xmlDictReference(stream->dict);
1598
0
    }
1599
1600
9.08k
    i = 0;
1601
9.08k
    if (comp->flags & PAT_FROM_ROOT)
1602
976
  stream->flags |= XML_STREAM_FROM_ROOT;
1603
1604
41.5k
    for (;i < comp->nbStep;i++) {
1605
32.5k
  step = comp->steps[i];
1606
32.5k
        switch (step.op) {
1607
0
      case XML_OP_END:
1608
0
          break;
1609
644
      case XML_OP_ROOT:
1610
644
          if (i != 0)
1611
65
        goto error;
1612
579
    root = 1;
1613
579
    break;
1614
0
      case XML_OP_NS:
1615
0
    s = xmlStreamCompAddStep(stream, NULL, step.value,
1616
0
        XML_ELEMENT_NODE, flags);
1617
0
    if (s < 0)
1618
0
        goto error;
1619
0
    prevs = s;
1620
0
    flags = 0;
1621
0
    break;
1622
0
      case XML_OP_ATTR:
1623
0
    flags |= XML_STREAM_STEP_ATTR;
1624
0
    prevs = -1;
1625
0
    s = xmlStreamCompAddStep(stream,
1626
0
        step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
1627
0
    flags = 0;
1628
0
    if (s < 0)
1629
0
        goto error;
1630
0
    break;
1631
18.7k
      case XML_OP_ELEM:
1632
18.7k
          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
3.35k
        if ((comp->nbStep == i + 1) &&
1642
3.35k
      (flags & XML_STREAM_STEP_DESC)) {
1643
      /*
1644
      * Mark the special case where the expression resolves
1645
      * to any type of node.
1646
      */
1647
137
      if (comp->nbStep == i + 1) {
1648
137
          stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1649
137
      }
1650
137
      flags |= XML_STREAM_STEP_NODE;
1651
137
      s = xmlStreamCompAddStep(stream, NULL, NULL,
1652
137
          XML_STREAM_ANY_NODE, flags);
1653
137
      if (s < 0)
1654
0
          goto error;
1655
137
      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
137
      if (prevs != -1) {
1663
79
          stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1664
79
          prevs = -1;
1665
79
      }
1666
137
      break;
1667
1668
3.22k
        } else {
1669
      /* Just skip this one. */
1670
3.22k
      continue;
1671
3.22k
        }
1672
3.35k
    }
1673
    /* An element node. */
1674
15.3k
          s = xmlStreamCompAddStep(stream, step.value, step.value2,
1675
15.3k
        XML_ELEMENT_NODE, flags);
1676
15.3k
    if (s < 0)
1677
0
        goto error;
1678
15.3k
    prevs = s;
1679
15.3k
    flags = 0;
1680
15.3k
    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
151
      case XML_OP_ALL:
1691
151
          s = xmlStreamCompAddStep(stream, NULL, NULL,
1692
151
        XML_ELEMENT_NODE, flags);
1693
151
    if (s < 0)
1694
0
        goto error;
1695
151
    prevs = s;
1696
151
    flags = 0;
1697
151
    break;
1698
8.85k
      case XML_OP_PARENT:
1699
8.85k
          break;
1700
4.14k
      case XML_OP_ANCESTOR:
1701
    /* Skip redundant continuations. */
1702
4.14k
    if (flags & XML_STREAM_STEP_DESC)
1703
164
        break;
1704
3.98k
          flags |= XML_STREAM_STEP_DESC;
1705
    /*
1706
    * Mark the expression as having "//".
1707
    */
1708
3.98k
    if ((stream->flags & XML_STREAM_DESC) == 0)
1709
3.76k
        stream->flags |= XML_STREAM_DESC;
1710
3.98k
    break;
1711
32.5k
  }
1712
32.5k
    }
1713
9.02k
    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
9.02k
    if (stream->nbStep <= s)
1728
22
  goto error;
1729
9.00k
    stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1730
9.00k
    if (root)
1731
559
  stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1732
#ifdef DEBUG_STREAMING
1733
    xmlDebugStreamComp(stream);
1734
#endif
1735
9.00k
    comp->stream = stream;
1736
9.00k
    return(0);
1737
87
error:
1738
87
    xmlFreeStreamComp(stream);
1739
87
    return(0);
1740
9.02k
}
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
5.59k
xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1752
5.59k
    xmlStreamCtxtPtr cur;
1753
1754
5.59k
    cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1755
5.59k
    if (cur == NULL) {
1756
0
  ERROR(NULL, NULL, NULL,
1757
0
    "xmlNewStreamCtxt: malloc failed\n");
1758
0
  return(NULL);
1759
0
    }
1760
5.59k
    memset(cur, 0, sizeof(xmlStreamCtxt));
1761
5.59k
    cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1762
5.59k
    if (cur->states == NULL) {
1763
0
  xmlFree(cur);
1764
0
  ERROR(NULL, NULL, NULL,
1765
0
        "xmlNewStreamCtxt: malloc failed\n");
1766
0
  return(NULL);
1767
0
    }
1768
5.59k
    cur->nbState = 0;
1769
5.59k
    cur->maxState = 4;
1770
5.59k
    cur->level = 0;
1771
5.59k
    cur->comp = stream;
1772
5.59k
    cur->blockLevel = -1;
1773
5.59k
    return(cur);
1774
5.59k
}
1775
1776
/**
1777
 * xmlFreeStreamCtxt:
1778
 * @stream: the stream context
1779
 *
1780
 * Free the stream context
1781
 */
1782
void
1783
975
xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
1784
975
    xmlStreamCtxtPtr next;
1785
1786
6.57k
    while (stream != NULL) {
1787
5.59k
        next = stream->next;
1788
5.59k
        if (stream->states != NULL)
1789
5.59k
      xmlFree(stream->states);
1790
5.59k
        xmlFree(stream);
1791
5.59k
  stream = next;
1792
5.59k
    }
1793
975
}
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
3.76k
xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1806
3.76k
    int i;
1807
8.58k
    for (i = 0;i < comp->nbState;i++) {
1808
4.82k
        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
4.82k
    }
1814
3.76k
    if (comp->nbState >= comp->maxState) {
1815
36
        int *cur;
1816
1817
36
  cur = (int *) xmlRealloc(comp->states,
1818
36
         comp->maxState * 4 * sizeof(int));
1819
36
  if (cur == NULL) {
1820
0
      ERROR(NULL, NULL, NULL,
1821
0
      "xmlNewStreamCtxt: malloc failed\n");
1822
0
      return(-1);
1823
0
  }
1824
36
  comp->states = cur;
1825
36
        comp->maxState *= 2;
1826
36
    }
1827
3.76k
    comp->states[2 * comp->nbState] = idx;
1828
3.76k
    comp->states[2 * comp->nbState++ + 1] = level;
1829
3.76k
    return(comp->nbState - 1);
1830
3.76k
}
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
10.9k
          int nodeType) {
1852
10.9k
    int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc;
1853
10.9k
    xmlStreamCompPtr comp;
1854
10.9k
    xmlStreamStep step;
1855
#ifdef DEBUG_STREAMING
1856
    xmlStreamCtxtPtr orig = stream;
1857
#endif
1858
1859
10.9k
    if ((stream == NULL) || (stream->nbState < 0))
1860
0
        return(-1);
1861
1862
95.2k
    while (stream != NULL) {
1863
84.3k
  comp = stream->comp;
1864
1865
84.3k
  if ((nodeType == XML_ELEMENT_NODE) &&
1866
84.3k
      (name == NULL) && (ns == NULL)) {
1867
      /* We have a document node here (or a reset). */
1868
911
      stream->nbState = 0;
1869
911
      stream->level = 0;
1870
911
      stream->blockLevel = -1;
1871
911
      if (comp->flags & XML_STREAM_FROM_ROOT) {
1872
911
    if (comp->nbStep == 0) {
1873
        /* TODO: We have a "/." here? */
1874
0
        ret = 1;
1875
911
    } else {
1876
911
        if ((comp->nbStep == 1) &&
1877
911
      (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
1878
911
      (comp->steps[0].flags & XML_STREAM_STEP_DESC))
1879
20
        {
1880
      /*
1881
      * In the case of "//." the document node will match
1882
      * as well.
1883
      */
1884
20
      ret = 1;
1885
891
        } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1886
      /* TODO: Do we need this ? */
1887
528
      tmp = xmlStreamCtxtAddState(stream, 0, 0);
1888
528
      if (tmp < 0)
1889
0
          err++;
1890
528
        }
1891
911
    }
1892
911
      }
1893
911
      stream = stream->next;
1894
911
      continue; /* while */
1895
911
  }
1896
1897
  /*
1898
  * Fast check for ".".
1899
  */
1900
83.4k
  if (comp->nbStep == 0) {
1901
      /*
1902
       * / and . are handled at the XPath node set creation
1903
       * level by checking min depth
1904
       */
1905
2.19k
      if (stream->flags & XML_PATTERN_XPATH) {
1906
2.19k
    stream = stream->next;
1907
2.19k
    continue; /* while */
1908
2.19k
      }
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
2.19k
  }
1922
81.2k
  if (stream->blockLevel != -1) {
1923
      /*
1924
      * Skip blocked expressions.
1925
      */
1926
37.7k
      stream->level++;
1927
37.7k
      goto stream_next;
1928
37.7k
  }
1929
1930
43.5k
  if ((nodeType != XML_ELEMENT_NODE) &&
1931
43.5k
      (nodeType != XML_ATTRIBUTE_NODE) &&
1932
43.5k
      ((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
6.80k
      stream->level++;
1939
6.80k
      goto stream_next;
1940
6.80k
  }
1941
1942
  /*
1943
   * Check evolution of existing states
1944
   */
1945
36.6k
  i = 0;
1946
36.6k
  m = stream->nbState;
1947
46.8k
  while (i < m) {
1948
10.1k
      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
3.22k
    stepNr = stream->states[2 * (stream->nbState -1)];
1954
    /*
1955
    * TODO: Security check, should not happen, remove it.
1956
    */
1957
3.22k
    if (stream->states[(2 * (stream->nbState -1)) + 1] <
1958
3.22k
        stream->level) {
1959
0
        return (-1);
1960
0
    }
1961
3.22k
    desc = 0;
1962
    /* loop-stopper */
1963
3.22k
    i = m;
1964
6.94k
      } 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
6.94k
    stepNr = stream->states[2 * i];
1971
1972
    /* TODO: should not happen anymore: dead states */
1973
6.94k
    if (stepNr < 0)
1974
0
        goto next_state;
1975
1976
6.94k
    tmp = stream->states[(2 * i) + 1];
1977
1978
    /* skip new states just added */
1979
6.94k
    if (tmp > stream->level)
1980
0
        goto next_state;
1981
1982
    /* skip states at ancestor levels, except if "//" */
1983
6.94k
    desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC;
1984
6.94k
    if ((tmp < stream->level) && (!desc))
1985
1.61k
        goto next_state;
1986
6.94k
      }
1987
      /*
1988
      * Check for correct node-type.
1989
      */
1990
8.55k
      step = comp->steps[stepNr];
1991
8.55k
      if (step.nodeType != nodeType) {
1992
2.61k
    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
2.61k
    } else if (step.nodeType != XML_STREAM_ANY_NODE)
2000
941
        goto next_state;
2001
2.61k
      }
2002
      /*
2003
      * Compare local/namespace-name.
2004
      */
2005
7.60k
      match = 0;
2006
7.60k
      if (step.nodeType == XML_STREAM_ANY_NODE) {
2007
1.67k
    match = 1;
2008
5.93k
      } else if (step.name == NULL) {
2009
968
    if (step.ns == NULL) {
2010
        /*
2011
        * This lets through all elements/attributes.
2012
        */
2013
968
        match = 1;
2014
968
    } else if (ns != NULL)
2015
0
        match = xmlStrEqual(step.ns, ns);
2016
4.96k
      } else if (((step.ns != NULL) == (ns != NULL)) &&
2017
4.96k
    (name != NULL) &&
2018
4.96k
    (step.name[0] == name[0]) &&
2019
4.96k
    xmlStrEqual(step.name, name) &&
2020
4.96k
    ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2021
682
      {
2022
682
    match = 1;
2023
682
      }
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
7.60k
      if (match) {
2041
3.32k
    final = step.flags & XML_STREAM_STEP_FINAL;
2042
3.32k
    if (desc) {
2043
2.18k
        if (final) {
2044
1.73k
      ret = 1;
2045
1.73k
        } else {
2046
      /* descending match create a new state */
2047
447
      xmlStreamCtxtAddState(stream, stepNr + 1,
2048
447
                            stream->level + 1);
2049
447
        }
2050
2.18k
    } else {
2051
1.13k
        if (final) {
2052
127
      ret = 1;
2053
1.01k
        } else {
2054
1.01k
      xmlStreamCtxtAddState(stream, stepNr + 1,
2055
1.01k
                            stream->level + 1);
2056
1.01k
        }
2057
1.13k
    }
2058
3.32k
    if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2059
        /*
2060
        * Check if we have a special case like "foo/bar//.", where
2061
        * "foo" is selected as well.
2062
        */
2063
174
        ret = 1;
2064
174
    }
2065
3.32k
      }
2066
7.60k
      if (((comp->flags & XML_STREAM_DESC) == 0) &&
2067
7.60k
    ((! match) || final))  {
2068
    /*
2069
    * Mark this expression as blocked for any evaluation at
2070
    * deeper levels. Note that this includes "/foo"
2071
    * expressions if the *pattern* behaviour is used.
2072
    */
2073
2.58k
    stream->blockLevel = stream->level +1;
2074
2.58k
      }
2075
10.1k
next_state:
2076
10.1k
      i++;
2077
10.1k
  }
2078
2079
36.6k
  stream->level++;
2080
2081
  /*
2082
  * Re/enter the expression.
2083
  * Don't reenter if it's an absolute expression like "/foo",
2084
  *   except "//foo".
2085
  */
2086
36.6k
  step = comp->steps[0];
2087
36.6k
  if (step.flags & XML_STREAM_STEP_ROOT)
2088
2.66k
      goto stream_next;
2089
2090
34.0k
  desc = step.flags & XML_STREAM_STEP_DESC;
2091
34.0k
  if (stream->flags & XML_PATTERN_NOTPATTERN) {
2092
      /*
2093
      * Re/enter the expression if it is a "descendant" one,
2094
      * or if we are at the 1st level of evaluation.
2095
      */
2096
2097
34.0k
      if (stream->level == 1) {
2098
5.06k
    if (XML_STREAM_XS_IDC(stream)) {
2099
        /*
2100
        * XS-IDC: The missing "self::node()" will always
2101
        * match the first given node.
2102
        */
2103
0
        goto stream_next;
2104
0
    } else
2105
5.06k
        goto compare;
2106
5.06k
      }
2107
      /*
2108
      * A "//" is always reentrant.
2109
      */
2110
28.9k
      if (desc)
2111
22.9k
    goto compare;
2112
2113
      /*
2114
      * XS-IDC: Process the 2nd level, since the missing
2115
      * "self::node()" is responsible for the 2nd level being
2116
      * the real start level.
2117
      */
2118
6.01k
      if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
2119
0
    goto compare;
2120
2121
6.01k
      goto stream_next;
2122
6.01k
  }
2123
2124
28.0k
compare:
2125
  /*
2126
  * Check expected node-type.
2127
  */
2128
28.0k
  if (step.nodeType != nodeType) {
2129
1.72k
      if (nodeType == XML_ATTRIBUTE_NODE)
2130
0
    goto stream_next;
2131
1.72k
      else if (step.nodeType != XML_STREAM_ANY_NODE)
2132
871
    goto stream_next;
2133
1.72k
  }
2134
  /*
2135
  * Compare local/namespace-name.
2136
  */
2137
27.1k
  match = 0;
2138
27.1k
  if (step.nodeType == XML_STREAM_ANY_NODE) {
2139
858
      match = 1;
2140
26.2k
  } else if (step.name == NULL) {
2141
884
      if (step.ns == NULL) {
2142
    /*
2143
    * This lets through all elements/attributes.
2144
    */
2145
884
    match = 1;
2146
884
      } else if (ns != NULL)
2147
0
    match = xmlStrEqual(step.ns, ns);
2148
25.4k
  } else if (((step.ns != NULL) == (ns != NULL)) &&
2149
25.4k
      (name != NULL) &&
2150
25.4k
      (step.name[0] == name[0]) &&
2151
25.4k
      xmlStrEqual(step.name, name) &&
2152
25.4k
      ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2153
2.97k
  {
2154
2.97k
      match = 1;
2155
2.97k
  }
2156
27.1k
  final = step.flags & XML_STREAM_STEP_FINAL;
2157
27.1k
  if (match) {
2158
4.71k
      if (final)
2159
2.93k
    ret = 1;
2160
1.77k
      else
2161
1.77k
    xmlStreamCtxtAddState(stream, 1, stream->level);
2162
4.71k
      if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2163
    /*
2164
    * Check if we have a special case like "foo//.", where
2165
    * "foo" is selected as well.
2166
    */
2167
14
    ret = 1;
2168
14
      }
2169
4.71k
  }
2170
27.1k
  if (((comp->flags & XML_STREAM_DESC) == 0) &&
2171
27.1k
      ((! match) || final))  {
2172
      /*
2173
      * Mark this expression as blocked for any evaluation at
2174
      * deeper levels.
2175
      */
2176
2.33k
      stream->blockLevel = stream->level;
2177
2.33k
  }
2178
2179
81.2k
stream_next:
2180
81.2k
        stream = stream->next;
2181
81.2k
    } /* while stream != NULL */
2182
2183
10.9k
    if (err > 0)
2184
0
        ret = -1;
2185
#ifdef DEBUG_STREAMING
2186
    xmlDebugStreamCtxt(orig, ret);
2187
#endif
2188
10.9k
    return(ret);
2189
10.9k
}
2190
2191
/**
2192
 * xmlStreamPush:
2193
 * @stream: the stream context
2194
 * @name: the current name
2195
 * @ns: the namespace name
2196
 *
2197
 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2198
 * indicated a dictionary, then strings for name and ns will be expected
2199
 * to come from the dictionary.
2200
 * Both @name and @ns being NULL means the / i.e. the root of the document.
2201
 * This can also act as a reset.
2202
 * Otherwise the function will act as if it has been given an element-node.
2203
 *
2204
 * Returns: -1 in case of error, 1 if the current state in the stream is a
2205
 *    match and 0 otherwise.
2206
 */
2207
int
2208
xmlStreamPush(xmlStreamCtxtPtr stream,
2209
7.67k
              const xmlChar *name, const xmlChar *ns) {
2210
7.67k
    return (xmlStreamPushInternal(stream, name, ns, XML_ELEMENT_NODE));
2211
7.67k
}
2212
2213
/**
2214
 * xmlStreamPushNode:
2215
 * @stream: the stream context
2216
 * @name: the current name
2217
 * @ns: the namespace name
2218
 * @nodeType: the type of the node being pushed
2219
 *
2220
 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2221
 * indicated a dictionary, then strings for name and ns will be expected
2222
 * to come from the dictionary.
2223
 * Both @name and @ns being NULL means the / i.e. the root of the document.
2224
 * This can also act as a reset.
2225
 * Different from xmlStreamPush() this function can be fed with nodes of type:
2226
 * element-, attribute-, text-, cdata-section-, comment- and
2227
 * processing-instruction-node.
2228
 *
2229
 * Returns: -1 in case of error, 1 if the current state in the stream is a
2230
 *    match and 0 otherwise.
2231
 */
2232
int
2233
xmlStreamPushNode(xmlStreamCtxtPtr stream,
2234
      const xmlChar *name, const xmlChar *ns,
2235
      int nodeType)
2236
3.25k
{
2237
3.25k
    return (xmlStreamPushInternal(stream, name, ns,
2238
3.25k
  nodeType));
2239
3.25k
}
2240
2241
/**
2242
* xmlStreamPushAttr:
2243
* @stream: the stream context
2244
* @name: the current name
2245
* @ns: the namespace name
2246
*
2247
* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2248
* indicated a dictionary, then strings for name and ns will be expected
2249
* to come from the dictionary.
2250
* Both @name and @ns being NULL means the / i.e. the root of the document.
2251
* This can also act as a reset.
2252
* Otherwise the function will act as if it has been given an attribute-node.
2253
*
2254
* Returns: -1 in case of error, 1 if the current state in the stream is a
2255
*    match and 0 otherwise.
2256
*/
2257
int
2258
xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2259
0
      const xmlChar *name, const xmlChar *ns) {
2260
0
    return (xmlStreamPushInternal(stream, name, ns, XML_ATTRIBUTE_NODE));
2261
0
}
2262
2263
/**
2264
 * xmlStreamPop:
2265
 * @stream: the stream context
2266
 *
2267
 * push one level from the stream.
2268
 *
2269
 * Returns: -1 in case of error, 0 otherwise.
2270
 */
2271
int
2272
10.3k
xmlStreamPop(xmlStreamCtxtPtr stream) {
2273
10.3k
    int i, lev;
2274
2275
10.3k
    if (stream == NULL)
2276
0
        return(-1);
2277
93.8k
    while (stream != NULL) {
2278
  /*
2279
  * Reset block-level.
2280
  */
2281
83.4k
  if (stream->blockLevel == stream->level)
2282
4.91k
      stream->blockLevel = -1;
2283
2284
  /*
2285
   *  stream->level can be zero when XML_FINAL_IS_ANY_NODE is set
2286
   *  (see the thread at
2287
   *  http://mail.gnome.org/archives/xslt/2008-July/msg00027.html)
2288
   */
2289
83.4k
  if (stream->level)
2290
81.2k
      stream->level--;
2291
  /*
2292
   * Check evolution of existing states
2293
   */
2294
86.6k
  for (i = stream->nbState -1; i >= 0; i--) {
2295
      /* discard obsoleted states */
2296
24.1k
      lev = stream->states[(2 * i) + 1];
2297
24.1k
      if (lev > stream->level)
2298
3.23k
    stream->nbState--;
2299
24.1k
      if (lev <= stream->level)
2300
20.9k
    break;
2301
24.1k
  }
2302
83.4k
  stream = stream->next;
2303
83.4k
    }
2304
10.3k
    return(0);
2305
10.3k
}
2306
2307
/**
2308
 * xmlStreamWantsAnyNode:
2309
 * @streamCtxt: the stream context
2310
 *
2311
 * Query if the streaming pattern additionally needs to be fed with
2312
 * text-, cdata-section-, comment- and processing-instruction-nodes.
2313
 * If the result is 0 then only element-nodes and attribute-nodes
2314
 * need to be pushed.
2315
 *
2316
 * Returns: 1 in case of need of nodes of the above described types,
2317
 *          0 otherwise. -1 on API errors.
2318
 */
2319
int
2320
xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
2321
975
{
2322
975
    if (streamCtxt == NULL)
2323
0
  return(-1);
2324
5.78k
    while (streamCtxt != NULL) {
2325
4.95k
  if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
2326
145
      return(1);
2327
4.80k
  streamCtxt = streamCtxt->next;
2328
4.80k
    }
2329
830
    return(0);
2330
975
}
2331
2332
/************************************************************************
2333
 *                  *
2334
 *      The public interfaces       *
2335
 *                  *
2336
 ************************************************************************/
2337
2338
/**
2339
 * xmlPatterncompile:
2340
 * @pattern: the pattern to compile
2341
 * @dict: an optional dictionary for interned strings
2342
 * @flags: compilation flags, see xmlPatternFlags
2343
 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
2344
 *
2345
 * Compile a pattern.
2346
 *
2347
 * Returns the compiled form of the pattern or NULL in case of error
2348
 */
2349
xmlPatternPtr
2350
xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
2351
3.48k
                  const xmlChar **namespaces) {
2352
3.48k
    xmlPatternPtr ret = NULL, cur;
2353
3.48k
    xmlPatParserContextPtr ctxt = NULL;
2354
3.48k
    const xmlChar *or, *start;
2355
3.48k
    xmlChar *tmp = NULL;
2356
3.48k
    int type = 0;
2357
3.48k
    int streamable = 1;
2358
2359
3.48k
    if (pattern == NULL)
2360
0
        return(NULL);
2361
2362
3.48k
    start = pattern;
2363
3.48k
    or = start;
2364
21.9k
    while (*or != 0) {
2365
21.2k
  tmp = NULL;
2366
579k
  while ((*or != 0) && (*or != '|')) or++;
2367
21.2k
        if (*or == 0)
2368
3.21k
      ctxt = xmlNewPatParserContext(start, dict, namespaces);
2369
18.0k
  else {
2370
18.0k
      tmp = xmlStrndup(start, or - start);
2371
18.0k
      if (tmp != NULL) {
2372
18.0k
    ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2373
18.0k
      }
2374
18.0k
      or++;
2375
18.0k
  }
2376
21.2k
  if (ctxt == NULL) goto error;
2377
21.2k
  cur = xmlNewPattern();
2378
21.2k
  if (cur == NULL) goto error;
2379
  /*
2380
  * Assign string dict.
2381
  */
2382
21.2k
  if (dict) {
2383
0
      cur->dict = dict;
2384
0
      xmlDictReference(dict);
2385
0
  }
2386
21.2k
  if (ret == NULL)
2387
3.48k
      ret = cur;
2388
17.7k
  else {
2389
17.7k
      cur->next = ret->next;
2390
17.7k
      ret->next = cur;
2391
17.7k
  }
2392
21.2k
  cur->flags = flags;
2393
21.2k
  ctxt->comp = cur;
2394
2395
21.2k
  if (XML_STREAM_XS_IDC(cur))
2396
0
      xmlCompileIDCXPathPath(ctxt);
2397
21.2k
  else
2398
21.2k
      xmlCompilePathPattern(ctxt);
2399
21.2k
  if (ctxt->error != 0)
2400
2.82k
      goto error;
2401
18.4k
  xmlFreePatParserContext(ctxt);
2402
18.4k
  ctxt = NULL;
2403
2404
2405
18.4k
        if (streamable) {
2406
9.51k
      if (type == 0) {
2407
1.01k
          type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2408
8.50k
      } else if (type == PAT_FROM_ROOT) {
2409
737
          if (cur->flags & PAT_FROM_CUR)
2410
94
        streamable = 0;
2411
7.76k
      } else if (type == PAT_FROM_CUR) {
2412
7.76k
          if (cur->flags & PAT_FROM_ROOT)
2413
200
        streamable = 0;
2414
7.76k
      }
2415
9.51k
  }
2416
18.4k
  if (streamable)
2417
9.21k
      xmlStreamCompile(cur);
2418
18.4k
  if (xmlReversePattern(cur) < 0)
2419
0
      goto error;
2420
18.4k
  if (tmp != NULL) {
2421
17.8k
      xmlFree(tmp);
2422
17.8k
      tmp = NULL;
2423
17.8k
  }
2424
18.4k
  start = or;
2425
18.4k
    }
2426
659
    if (streamable == 0) {
2427
148
        cur = ret;
2428
4.50k
  while (cur != NULL) {
2429
4.35k
      if (cur->stream != NULL) {
2430
580
    xmlFreeStreamComp(cur->stream);
2431
580
    cur->stream = NULL;
2432
580
      }
2433
4.35k
      cur = cur->next;
2434
4.35k
  }
2435
148
    }
2436
2437
659
    return(ret);
2438
2.82k
error:
2439
2.82k
    if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2440
2.82k
    if (ret != NULL) xmlFreePattern(ret);
2441
2.82k
    if (tmp != NULL) xmlFree(tmp);
2442
2.82k
    return(NULL);
2443
3.48k
}
2444
2445
/**
2446
 * xmlPatternMatch:
2447
 * @comp: the precompiled pattern
2448
 * @node: a node
2449
 *
2450
 * Test whether the node matches the pattern
2451
 *
2452
 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2453
 */
2454
int
2455
xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2456
0
{
2457
0
    int ret = 0;
2458
2459
0
    if ((comp == NULL) || (node == NULL))
2460
0
        return(-1);
2461
2462
0
    while (comp != NULL) {
2463
0
        ret = xmlPatMatch(comp, node);
2464
0
  if (ret != 0)
2465
0
      return(ret);
2466
0
  comp = comp->next;
2467
0
    }
2468
0
    return(ret);
2469
0
}
2470
2471
/**
2472
 * xmlPatternGetStreamCtxt:
2473
 * @comp: the precompiled pattern
2474
 *
2475
 * Get a streaming context for that pattern
2476
 * Use xmlFreeStreamCtxt to free the context.
2477
 *
2478
 * Returns a pointer to the context or NULL in case of failure
2479
 */
2480
xmlStreamCtxtPtr
2481
xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2482
975
{
2483
975
    xmlStreamCtxtPtr ret = NULL, cur;
2484
2485
975
    if ((comp == NULL) || (comp->stream == NULL))
2486
0
        return(NULL);
2487
2488
6.57k
    while (comp != NULL) {
2489
5.59k
        if (comp->stream == NULL)
2490
0
      goto failed;
2491
5.59k
  cur = xmlNewStreamCtxt(comp->stream);
2492
5.59k
  if (cur == NULL)
2493
0
      goto failed;
2494
5.59k
  if (ret == NULL)
2495
975
      ret = cur;
2496
4.62k
  else {
2497
4.62k
      cur->next = ret->next;
2498
4.62k
      ret->next = cur;
2499
4.62k
  }
2500
5.59k
  cur->flags = comp->flags;
2501
5.59k
  comp = comp->next;
2502
5.59k
    }
2503
975
    return(ret);
2504
0
failed:
2505
0
    xmlFreeStreamCtxt(ret);
2506
0
    return(NULL);
2507
975
}
2508
2509
/**
2510
 * xmlPatternStreamable:
2511
 * @comp: the precompiled pattern
2512
 *
2513
 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2514
 * should work.
2515
 *
2516
 * Returns 1 if streamable, 0 if not and -1 in case of error.
2517
 */
2518
int
2519
657
xmlPatternStreamable(xmlPatternPtr comp) {
2520
657
    if (comp == NULL)
2521
0
        return(-1);
2522
5.84k
    while (comp != NULL) {
2523
5.37k
        if (comp->stream == NULL)
2524
183
      return(0);
2525
5.19k
  comp = comp->next;
2526
5.19k
    }
2527
474
    return(1);
2528
657
}
2529
2530
/**
2531
 * xmlPatternMaxDepth:
2532
 * @comp: the precompiled pattern
2533
 *
2534
 * Check the maximum depth reachable by a pattern
2535
 *
2536
 * Returns -2 if no limit (using //), otherwise the depth,
2537
 *         and -1 in case of error
2538
 */
2539
int
2540
1.31k
xmlPatternMaxDepth(xmlPatternPtr comp) {
2541
1.31k
    int ret = 0, i;
2542
1.31k
    if (comp == NULL)
2543
0
        return(-1);
2544
3.05k
    while (comp != NULL) {
2545
2.19k
        if (comp->stream == NULL)
2546
0
      return(-1);
2547
5.89k
  for (i = 0;i < comp->stream->nbStep;i++)
2548
4.15k
      if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2549
461
          return(-2);
2550
1.73k
  if (comp->stream->nbStep > ret)
2551
917
      ret = comp->stream->nbStep;
2552
1.73k
  comp = comp->next;
2553
1.73k
    }
2554
856
    return(ret);
2555
1.31k
}
2556
2557
/**
2558
 * xmlPatternMinDepth:
2559
 * @comp: the precompiled pattern
2560
 *
2561
 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2562
 * part of the set.
2563
 *
2564
 * Returns -1 in case of error otherwise the depth,
2565
 *
2566
 */
2567
int
2568
1.31k
xmlPatternMinDepth(xmlPatternPtr comp) {
2569
1.31k
    int ret = 12345678;
2570
1.31k
    if (comp == NULL)
2571
0
        return(-1);
2572
6.47k
    while (comp != NULL) {
2573
5.27k
        if (comp->stream == NULL)
2574
0
      return(-1);
2575
5.27k
  if (comp->stream->nbStep < ret)
2576
1.38k
      ret = comp->stream->nbStep;
2577
5.27k
  if (ret == 0)
2578
124
      return(0);
2579
5.15k
  comp = comp->next;
2580
5.15k
    }
2581
1.19k
    return(ret);
2582
1.31k
}
2583
2584
/**
2585
 * xmlPatternFromRoot:
2586
 * @comp: the precompiled pattern
2587
 *
2588
 * Check if the pattern must be looked at from the root.
2589
 *
2590
 * Returns 1 if true, 0 if false and -1 in case of error
2591
 */
2592
int
2593
1.31k
xmlPatternFromRoot(xmlPatternPtr comp) {
2594
1.31k
    if (comp == NULL)
2595
0
        return(-1);
2596
6.34k
    while (comp != NULL) {
2597
5.56k
        if (comp->stream == NULL)
2598
0
      return(-1);
2599
5.56k
  if (comp->flags & PAT_FROM_ROOT)
2600
535
      return(1);
2601
5.02k
  comp = comp->next;
2602
5.02k
    }
2603
782
    return(0);
2604
2605
1.31k
}
2606
2607
#endif /* LIBXML_PATTERN_ENABLED */