Coverage Report

Created: 2025-07-23 08:13

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