Home | History | Annotate | Download | only in libxslt
      1 /*
      2  * pattern.c: Implemetation of the template match compilation and lookup
      3  *
      4  * Reference:
      5  *   http://www.w3.org/TR/1999/REC-xslt-19991116
      6  *
      7  * See Copyright for the status of this software.
      8  *
      9  * daniel (at) veillard.com
     10  */
     11 
     12 /*
     13  * TODO: handle pathological cases like *[*[@a="b"]]
     14  * TODO: detect [number] at compilation, optimize accordingly
     15  */
     16 
     17 #define IN_LIBXSLT
     18 #include "libxslt.h"
     19 
     20 #include <string.h>
     21 
     22 #include <libxml/xmlmemory.h>
     23 #include <libxml/tree.h>
     24 #include <libxml/valid.h>
     25 #include <libxml/hash.h>
     26 #include <libxml/xmlerror.h>
     27 #include <libxml/parserInternals.h>
     28 #include "xslt.h"
     29 #include "xsltInternals.h"
     30 #include "xsltutils.h"
     31 #include "imports.h"
     32 #include "templates.h"
     33 #include "keys.h"
     34 #include "pattern.h"
     35 #include "documents.h"
     36 
     37 #ifdef WITH_XSLT_DEBUG
     38 #define WITH_XSLT_DEBUG_PATTERN
     39 #endif
     40 
     41 /*
     42  * Types are private:
     43  */
     44 
     45 typedef enum {
     46     XSLT_OP_END=0,
     47     XSLT_OP_ROOT,
     48     XSLT_OP_ELEM,
     49     XSLT_OP_ATTR,
     50     XSLT_OP_PARENT,
     51     XSLT_OP_ANCESTOR,
     52     XSLT_OP_ID,
     53     XSLT_OP_KEY,
     54     XSLT_OP_NS,
     55     XSLT_OP_ALL,
     56     XSLT_OP_PI,
     57     XSLT_OP_COMMENT,
     58     XSLT_OP_TEXT,
     59     XSLT_OP_NODE,
     60     XSLT_OP_PREDICATE
     61 } xsltOp;
     62 
     63 typedef enum {
     64     AXIS_CHILD=1,
     65     AXIS_ATTRIBUTE
     66 } xsltAxis;
     67 
     68 typedef struct _xsltStepState xsltStepState;
     69 typedef xsltStepState *xsltStepStatePtr;
     70 struct _xsltStepState {
     71     int step;
     72     xmlNodePtr node;
     73 };
     74 
     75 typedef struct _xsltStepStates xsltStepStates;
     76 typedef xsltStepStates *xsltStepStatesPtr;
     77 struct _xsltStepStates {
     78     int nbstates;
     79     int maxstates;
     80     xsltStepStatePtr states;
     81 };
     82 
     83 typedef struct _xsltStepOp xsltStepOp;
     84 typedef xsltStepOp *xsltStepOpPtr;
     85 struct _xsltStepOp {
     86     xsltOp op;
     87     xmlChar *value;
     88     xmlChar *value2;
     89     xmlChar *value3;
     90     xmlXPathCompExprPtr comp;
     91     /*
     92      * Optimisations for count
     93      */
     94     int        previousExtra;
     95     int        indexExtra;
     96     int        lenExtra;
     97 };
     98 
     99 struct _xsltCompMatch {
    100     struct _xsltCompMatch *next; /* siblings in the name hash */
    101     float priority;              /* the priority */
    102     const xmlChar *pattern;       /* the pattern */
    103     const xmlChar *mode;         /* the mode */
    104     const xmlChar *modeURI;      /* the mode URI */
    105     xsltTemplatePtr template;    /* the associated template */
    106 
    107     int direct;
    108     /* TODO fix the statically allocated size steps[] */
    109     int nbStep;
    110     int maxStep;
    111     xmlNsPtr *nsList;		/* the namespaces in scope */
    112     int nsNr;			/* the number of namespaces in scope */
    113     xsltStepOpPtr steps;        /* ops for computation */
    114 };
    115 
    116 typedef struct _xsltParserContext xsltParserContext;
    117 typedef xsltParserContext *xsltParserContextPtr;
    118 struct _xsltParserContext {
    119     xsltStylesheetPtr style;		/* the stylesheet */
    120     xsltTransformContextPtr ctxt;	/* the transformation or NULL */
    121     const xmlChar *cur;			/* the current char being parsed */
    122     const xmlChar *base;		/* the full expression */
    123     xmlDocPtr      doc;			/* the source document */
    124     xmlNodePtr    elem;			/* the source element */
    125     int error;				/* error code */
    126     xsltCompMatchPtr comp;		/* the result */
    127 };
    128 
    129 /************************************************************************
    130  * 									*
    131  * 			Type functions 					*
    132  * 									*
    133  ************************************************************************/
    134 
    135 /**
    136  * xsltNewCompMatch:
    137  *
    138  * Create a new XSLT CompMatch
    139  *
    140  * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
    141  */
    142 static xsltCompMatchPtr
    143 xsltNewCompMatch(void) {
    144     xsltCompMatchPtr cur;
    145 
    146     cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
    147     if (cur == NULL) {
    148 	xsltTransformError(NULL, NULL, NULL,
    149 		"xsltNewCompMatch : out of memory error\n");
    150 	return(NULL);
    151     }
    152     memset(cur, 0, sizeof(xsltCompMatch));
    153     cur->maxStep = 10;
    154     cur->nbStep = 0;
    155     cur-> steps = (xsltStepOpPtr) xmlMalloc(sizeof(xsltStepOp) *
    156                                             cur->maxStep);
    157     if (cur->steps == NULL) {
    158 	xsltTransformError(NULL, NULL, NULL,
    159 		"xsltNewCompMatch : out of memory error\n");
    160 	xmlFree(cur);
    161 	return(NULL);
    162     }
    163     cur->nsNr = 0;
    164     cur->nsList = NULL;
    165     cur->direct = 0;
    166     return(cur);
    167 }
    168 
    169 /**
    170  * xsltFreeCompMatch:
    171  * @comp:  an XSLT comp
    172  *
    173  * Free up the memory allocated by @comp
    174  */
    175 static void
    176 xsltFreeCompMatch(xsltCompMatchPtr comp) {
    177     xsltStepOpPtr op;
    178     int i;
    179 
    180     if (comp == NULL)
    181 	return;
    182     if (comp->pattern != NULL)
    183 	xmlFree((xmlChar *)comp->pattern);
    184     if (comp->nsList != NULL)
    185 	xmlFree(comp->nsList);
    186     for (i = 0;i < comp->nbStep;i++) {
    187 	op = &comp->steps[i];
    188 	if (op->value != NULL)
    189 	    xmlFree(op->value);
    190 	if (op->value2 != NULL)
    191 	    xmlFree(op->value2);
    192 	if (op->value3 != NULL)
    193 	    xmlFree(op->value3);
    194 	if (op->comp != NULL)
    195 	    xmlXPathFreeCompExpr(op->comp);
    196     }
    197     xmlFree(comp->steps);
    198     memset(comp, -1, sizeof(xsltCompMatch));
    199     xmlFree(comp);
    200 }
    201 
    202 /**
    203  * xsltFreeCompMatchList:
    204  * @comp:  an XSLT comp list
    205  *
    206  * Free up the memory allocated by all the elements of @comp
    207  */
    208 void
    209 xsltFreeCompMatchList(xsltCompMatchPtr comp) {
    210     xsltCompMatchPtr cur;
    211 
    212     while (comp != NULL) {
    213 	cur = comp;
    214 	comp = comp->next;
    215 	xsltFreeCompMatch(cur);
    216     }
    217 }
    218 
    219 /**
    220  * xsltNormalizeCompSteps:
    221  * @payload: pointer to template hash table entry
    222  * @data: pointer to the stylesheet
    223  * @name: template match name
    224  *
    225  * This is a hashtable scanner function to normalize the compiled
    226  * steps of an imported stylesheet.
    227  */
    228 void xsltNormalizeCompSteps(void *payload,
    229         void *data, const xmlChar *name ATTRIBUTE_UNUSED) {
    230     xsltCompMatchPtr comp = payload;
    231     xsltStylesheetPtr style = data;
    232     int ix;
    233 
    234     for (ix = 0; ix < comp->nbStep; ix++) {
    235         comp->steps[ix].previousExtra += style->extrasNr;
    236         comp->steps[ix].indexExtra += style->extrasNr;
    237         comp->steps[ix].lenExtra += style->extrasNr;
    238     }
    239 }
    240 
    241 /**
    242  * xsltNewParserContext:
    243  * @style:  the stylesheet
    244  * @ctxt:  the transformation context, if done at run-time
    245  *
    246  * Create a new XSLT ParserContext
    247  *
    248  * Returns the newly allocated xsltParserContextPtr or NULL in case of error
    249  */
    250 static xsltParserContextPtr
    251 xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) {
    252     xsltParserContextPtr cur;
    253 
    254     cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
    255     if (cur == NULL) {
    256 	xsltTransformError(NULL, NULL, NULL,
    257 		"xsltNewParserContext : malloc failed\n");
    258 	return(NULL);
    259     }
    260     memset(cur, 0, sizeof(xsltParserContext));
    261     cur->style = style;
    262     cur->ctxt = ctxt;
    263     return(cur);
    264 }
    265 
    266 /**
    267  * xsltFreeParserContext:
    268  * @ctxt:  an XSLT parser context
    269  *
    270  * Free up the memory allocated by @ctxt
    271  */
    272 static void
    273 xsltFreeParserContext(xsltParserContextPtr ctxt) {
    274     if (ctxt == NULL)
    275 	return;
    276     memset(ctxt, -1, sizeof(xsltParserContext));
    277     xmlFree(ctxt);
    278 }
    279 
    280 /**
    281  * xsltCompMatchAdd:
    282  * @comp:  the compiled match expression
    283  * @op:  an op
    284  * @value:  the first value
    285  * @value2:  the second value
    286  * @novar:  flag to set XML_XPATH_NOVAR
    287  *
    288  * Add an step to an XSLT Compiled Match
    289  *
    290  * Returns -1 in case of failure, 0 otherwise.
    291  */
    292 static int
    293 xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp,
    294                  xsltOp op, xmlChar * value, xmlChar * value2, int novar)
    295 {
    296     if (comp->nbStep >= comp->maxStep) {
    297         xsltStepOpPtr tmp;
    298 
    299 	tmp = (xsltStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
    300 	                                 sizeof(xsltStepOp));
    301 	if (tmp == NULL) {
    302 	    xsltGenericError(xsltGenericErrorContext,
    303 	     "xsltCompMatchAdd: memory re-allocation failure.\n");
    304 	    if (ctxt->style != NULL)
    305 		ctxt->style->errors++;
    306 	    return (-1);
    307 	}
    308         comp->maxStep *= 2;
    309 	comp->steps = tmp;
    310     }
    311     comp->steps[comp->nbStep].op = op;
    312     comp->steps[comp->nbStep].value = value;
    313     comp->steps[comp->nbStep].value2 = value2;
    314     comp->steps[comp->nbStep].value3 = NULL;
    315     comp->steps[comp->nbStep].comp = NULL;
    316     if (ctxt->ctxt != NULL) {
    317 	comp->steps[comp->nbStep].previousExtra =
    318 	    xsltAllocateExtraCtxt(ctxt->ctxt);
    319 	comp->steps[comp->nbStep].indexExtra =
    320 	    xsltAllocateExtraCtxt(ctxt->ctxt);
    321 	comp->steps[comp->nbStep].lenExtra =
    322 	    xsltAllocateExtraCtxt(ctxt->ctxt);
    323     } else {
    324 	comp->steps[comp->nbStep].previousExtra =
    325 	    xsltAllocateExtra(ctxt->style);
    326 	comp->steps[comp->nbStep].indexExtra =
    327 	    xsltAllocateExtra(ctxt->style);
    328 	comp->steps[comp->nbStep].lenExtra =
    329 	    xsltAllocateExtra(ctxt->style);
    330     }
    331     if (op == XSLT_OP_PREDICATE) {
    332 	xmlXPathContextPtr xctxt;
    333 
    334 	if (ctxt->style != NULL)
    335 	    xctxt = xmlXPathNewContext(ctxt->style->doc);
    336 	else
    337 	    xctxt = xmlXPathNewContext(NULL);
    338 #ifdef XML_XPATH_NOVAR
    339 	if (novar != 0)
    340 	    xctxt->flags = XML_XPATH_NOVAR;
    341 #endif
    342 	if (ctxt->style != NULL)
    343 	    xctxt->dict = ctxt->style->dict;
    344 	comp->steps[comp->nbStep].comp = xmlXPathCtxtCompile(xctxt, value);
    345 	xmlXPathFreeContext(xctxt);
    346 	if (comp->steps[comp->nbStep].comp == NULL) {
    347 	    xsltTransformError(NULL, ctxt->style, ctxt->elem,
    348 		    "Failed to compile predicate\n");
    349 	    if (ctxt->style != NULL)
    350 		ctxt->style->errors++;
    351 	}
    352     }
    353     comp->nbStep++;
    354     return (0);
    355 }
    356 
    357 /**
    358  * xsltSwapTopCompMatch:
    359  * @comp:  the compiled match expression
    360  *
    361  * reverse the two top steps.
    362  */
    363 static void
    364 xsltSwapTopCompMatch(xsltCompMatchPtr comp) {
    365     int i;
    366     int j = comp->nbStep - 1;
    367 
    368     if (j > 0) {
    369 	register xmlChar *tmp;
    370 	register xsltOp op;
    371 	register xmlXPathCompExprPtr expr;
    372 	register int t;
    373 	i = j - 1;
    374 	tmp = comp->steps[i].value;
    375 	comp->steps[i].value = comp->steps[j].value;
    376 	comp->steps[j].value = tmp;
    377 	tmp = comp->steps[i].value2;
    378 	comp->steps[i].value2 = comp->steps[j].value2;
    379 	comp->steps[j].value2 = tmp;
    380 	tmp = comp->steps[i].value3;
    381 	comp->steps[i].value3 = comp->steps[j].value3;
    382 	comp->steps[j].value3 = tmp;
    383 	op = comp->steps[i].op;
    384 	comp->steps[i].op = comp->steps[j].op;
    385 	comp->steps[j].op = op;
    386 	expr = comp->steps[i].comp;
    387 	comp->steps[i].comp = comp->steps[j].comp;
    388 	comp->steps[j].comp = expr;
    389 	t = comp->steps[i].previousExtra;
    390 	comp->steps[i].previousExtra = comp->steps[j].previousExtra;
    391 	comp->steps[j].previousExtra = t;
    392 	t = comp->steps[i].indexExtra;
    393 	comp->steps[i].indexExtra = comp->steps[j].indexExtra;
    394 	comp->steps[j].indexExtra = t;
    395 	t = comp->steps[i].lenExtra;
    396 	comp->steps[i].lenExtra = comp->steps[j].lenExtra;
    397 	comp->steps[j].lenExtra = t;
    398     }
    399 }
    400 
    401 /**
    402  * xsltReverseCompMatch:
    403  * @ctxt: the parser context
    404  * @comp:  the compiled match expression
    405  *
    406  * reverse all the stack of expressions
    407  */
    408 static void
    409 xsltReverseCompMatch(xsltParserContextPtr ctxt, xsltCompMatchPtr comp) {
    410     int i = 0;
    411     int j = comp->nbStep - 1;
    412 
    413     while (j > i) {
    414 	register xmlChar *tmp;
    415 	register xsltOp op;
    416 	register xmlXPathCompExprPtr expr;
    417 	register int t;
    418 
    419 	tmp = comp->steps[i].value;
    420 	comp->steps[i].value = comp->steps[j].value;
    421 	comp->steps[j].value = tmp;
    422 	tmp = comp->steps[i].value2;
    423 	comp->steps[i].value2 = comp->steps[j].value2;
    424 	comp->steps[j].value2 = tmp;
    425 	tmp = comp->steps[i].value3;
    426 	comp->steps[i].value3 = comp->steps[j].value3;
    427 	comp->steps[j].value3 = tmp;
    428 	op = comp->steps[i].op;
    429 	comp->steps[i].op = comp->steps[j].op;
    430 	comp->steps[j].op = op;
    431 	expr = comp->steps[i].comp;
    432 	comp->steps[i].comp = comp->steps[j].comp;
    433 	comp->steps[j].comp = expr;
    434 	t = comp->steps[i].previousExtra;
    435 	comp->steps[i].previousExtra = comp->steps[j].previousExtra;
    436 	comp->steps[j].previousExtra = t;
    437 	t = comp->steps[i].indexExtra;
    438 	comp->steps[i].indexExtra = comp->steps[j].indexExtra;
    439 	comp->steps[j].indexExtra = t;
    440 	t = comp->steps[i].lenExtra;
    441 	comp->steps[i].lenExtra = comp->steps[j].lenExtra;
    442 	comp->steps[j].lenExtra = t;
    443 	j--;
    444 	i++;
    445     }
    446     xsltCompMatchAdd(ctxt, comp, XSLT_OP_END, NULL, NULL, 0);
    447 
    448     /*
    449      * detect consecutive XSLT_OP_PREDICATE indicating a direct
    450      * matching should be done.
    451      */
    452     for (i = 0;i < comp->nbStep - 1;i++) {
    453         if ((comp->steps[i].op == XSLT_OP_PREDICATE) &&
    454 	    (comp->steps[i + 1].op == XSLT_OP_PREDICATE)) {
    455 
    456 	    comp->direct = 1;
    457 	    if (comp->pattern[0] != '/') {
    458 		xmlChar *query;
    459 
    460 		query = xmlStrdup((const xmlChar *)"//");
    461 		query = xmlStrcat(query, comp->pattern);
    462 
    463 		xmlFree((xmlChar *) comp->pattern);
    464 		comp->pattern = query;
    465 	    }
    466 	    break;
    467 	}
    468     }
    469 }
    470 
    471 /************************************************************************
    472  * 									*
    473  * 		The interpreter for the precompiled patterns		*
    474  * 									*
    475  ************************************************************************/
    476 
    477 static int
    478 xsltPatPushState(xsltTransformContextPtr ctxt, xsltStepStates *states,
    479                  int step, xmlNodePtr node) {
    480     if ((states->states == NULL) || (states->maxstates <= 0)) {
    481         states->maxstates = 4;
    482 	states->nbstates = 0;
    483 	states->states = xmlMalloc(4 * sizeof(xsltStepState));
    484     }
    485     else if (states->maxstates <= states->nbstates) {
    486         xsltStepState *tmp;
    487 
    488 	tmp = (xsltStepStatePtr) xmlRealloc(states->states,
    489 			       2 * states->maxstates * sizeof(xsltStepState));
    490 	if (tmp == NULL) {
    491 	    xsltGenericError(xsltGenericErrorContext,
    492 	     "xsltPatPushState: memory re-allocation failure.\n");
    493 	    ctxt->state = XSLT_STATE_STOPPED;
    494 	    return(-1);
    495 	}
    496 	states->states = tmp;
    497 	states->maxstates *= 2;
    498     }
    499     states->states[states->nbstates].step = step;
    500     states->states[states->nbstates++].node = node;
    501 #if 0
    502     fprintf(stderr, "Push: %d, %s\n", step, node->name);
    503 #endif
    504     return(0);
    505 }
    506 
    507 /**
    508  * xsltTestCompMatchDirect:
    509  * @ctxt:  a XSLT process context
    510  * @comp: the precompiled pattern
    511  * @node: a node
    512  * @nsList: the namespaces in scope
    513  * @nsNr: the number of namespaces in scope
    514  *
    515  * Test whether the node matches the pattern, do a direct evalutation
    516  * and not a step by step evaluation.
    517  *
    518  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
    519  */
    520 static int
    521 xsltTestCompMatchDirect(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
    522 	                xmlNodePtr node, xmlNsPtr *nsList, int nsNr) {
    523     xsltStepOpPtr sel = NULL;
    524     xmlDocPtr prevdoc;
    525     xmlDocPtr doc;
    526     xmlXPathObjectPtr list;
    527     int ix, j;
    528     int nocache = 0;
    529     int isRVT;
    530 
    531     doc = node->doc;
    532     if (XSLT_IS_RES_TREE_FRAG(doc))
    533 	isRVT = 1;
    534     else
    535 	isRVT = 0;
    536     sel = &comp->steps[0]; /* store extra in first step arbitrarily */
    537 
    538     prevdoc = (xmlDocPtr)
    539 	XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
    540     ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
    541     list = (xmlXPathObjectPtr)
    542 	XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra);
    543 
    544     if ((list == NULL) || (prevdoc != doc)) {
    545 	xmlXPathObjectPtr newlist;
    546 	xmlNodePtr parent = node->parent;
    547 	xmlDocPtr olddoc;
    548 	xmlNodePtr oldnode;
    549 	int oldNsNr;
    550 	xmlNsPtr *oldNamespaces;
    551 
    552 	oldnode = ctxt->xpathCtxt->node;
    553 	olddoc = ctxt->xpathCtxt->doc;
    554 	oldNsNr = ctxt->xpathCtxt->nsNr;
    555 	oldNamespaces = ctxt->xpathCtxt->namespaces;
    556 	ctxt->xpathCtxt->node = node;
    557 	ctxt->xpathCtxt->doc = doc;
    558 	ctxt->xpathCtxt->namespaces = nsList;
    559 	ctxt->xpathCtxt->nsNr = nsNr;
    560 	newlist = xmlXPathEval(comp->pattern, ctxt->xpathCtxt);
    561 	ctxt->xpathCtxt->node = oldnode;
    562 	ctxt->xpathCtxt->doc = olddoc;
    563 	ctxt->xpathCtxt->namespaces = oldNamespaces;
    564 	ctxt->xpathCtxt->nsNr = oldNsNr;
    565 	if (newlist == NULL)
    566 	    return(-1);
    567 	if (newlist->type != XPATH_NODESET) {
    568 	    xmlXPathFreeObject(newlist);
    569 	    return(-1);
    570 	}
    571 	ix = 0;
    572 
    573 	if ((parent == NULL) || (node->doc == NULL) || isRVT)
    574 	    nocache = 1;
    575 
    576 	if (nocache == 0) {
    577 	    if (list != NULL)
    578 		xmlXPathFreeObject(list);
    579 	    list = newlist;
    580 
    581 	    XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) =
    582 		(void *) list;
    583 	    XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
    584 		(void *) doc;
    585 	    XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
    586 		0;
    587 	    XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) =
    588 		(xmlFreeFunc) xmlXPathFreeObject;
    589 	} else
    590 	    list = newlist;
    591     }
    592     if ((list->nodesetval == NULL) ||
    593 	(list->nodesetval->nodeNr <= 0)) {
    594 	if (nocache == 1)
    595 	    xmlXPathFreeObject(list);
    596 	return(0);
    597     }
    598     /* TODO: store the index and use it for the scan */
    599     if (ix == 0) {
    600 	for (j = 0;j < list->nodesetval->nodeNr;j++) {
    601 	    if (list->nodesetval->nodeTab[j] == node) {
    602 		if (nocache == 1)
    603 		    xmlXPathFreeObject(list);
    604 		return(1);
    605 	    }
    606 	}
    607     } else {
    608     }
    609     if (nocache == 1)
    610 	xmlXPathFreeObject(list);
    611     return(0);
    612 }
    613 
    614 /**
    615  * xsltTestCompMatch:
    616  * @ctxt:  a XSLT process context
    617  * @comp: the precompiled pattern
    618  * @node: a node
    619  * @mode:  the mode name or NULL
    620  * @modeURI:  the mode URI or NULL
    621  *
    622  * Test whether the node matches the pattern
    623  *
    624  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
    625  */
    626 static int
    627 xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
    628 	          xmlNodePtr node, const xmlChar *mode,
    629 		  const xmlChar *modeURI) {
    630     int i;
    631     xsltStepOpPtr step, sel = NULL;
    632     xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */
    633 
    634     if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
    635 	xsltTransformError(ctxt, NULL, node,
    636 		"xsltTestCompMatch: null arg\n");
    637         return(-1);
    638     }
    639     if (mode != NULL) {
    640 	if (comp->mode == NULL)
    641 	    return(0);
    642 	/*
    643 	 * both mode strings must be interned on the stylesheet dictionary
    644 	 */
    645 	if (comp->mode != mode)
    646 	    return(0);
    647     } else {
    648 	if (comp->mode != NULL)
    649 	    return(0);
    650     }
    651     if (modeURI != NULL) {
    652 	if (comp->modeURI == NULL)
    653 	    return(0);
    654 	/*
    655 	 * both modeURI strings must be interned on the stylesheet dictionary
    656 	 */
    657 	if (comp->modeURI != modeURI)
    658 	    return(0);
    659     } else {
    660 	if (comp->modeURI != NULL)
    661 	    return(0);
    662     }
    663 
    664     i = 0;
    665 restart:
    666     for (;i < comp->nbStep;i++) {
    667 	step = &comp->steps[i];
    668 	if (step->op != XSLT_OP_PREDICATE)
    669 	    sel = step;
    670 	switch (step->op) {
    671             case XSLT_OP_END:
    672 		goto found;
    673             case XSLT_OP_ROOT:
    674 		if ((node->type == XML_DOCUMENT_NODE) ||
    675 #ifdef LIBXML_DOCB_ENABLED
    676 		    (node->type == XML_DOCB_DOCUMENT_NODE) ||
    677 #endif
    678 		    (node->type == XML_HTML_DOCUMENT_NODE))
    679 		    continue;
    680 		if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' '))
    681 		    continue;
    682 		goto rollback;
    683             case XSLT_OP_ELEM:
    684 		if (node->type != XML_ELEMENT_NODE)
    685 		    goto rollback;
    686 		if (step->value == NULL)
    687 		    continue;
    688 		if (step->value[0] != node->name[0])
    689 		    goto rollback;
    690 		if (!xmlStrEqual(step->value, node->name))
    691 		    goto rollback;
    692 
    693 		/* Namespace test */
    694 		if (node->ns == NULL) {
    695 		    if (step->value2 != NULL)
    696 			goto rollback;
    697 		} else if (node->ns->href != NULL) {
    698 		    if (step->value2 == NULL)
    699 			goto rollback;
    700 		    if (!xmlStrEqual(step->value2, node->ns->href))
    701 			goto rollback;
    702 		}
    703 		continue;
    704             case XSLT_OP_ATTR:
    705 		if (node->type != XML_ATTRIBUTE_NODE)
    706 		    goto rollback;
    707 		if (step->value != NULL) {
    708 		    if (step->value[0] != node->name[0])
    709 			goto rollback;
    710 		    if (!xmlStrEqual(step->value, node->name))
    711 			goto rollback;
    712 		}
    713 		/* Namespace test */
    714 		if (node->ns == NULL) {
    715 		    if (step->value2 != NULL)
    716 			goto rollback;
    717 		} else if (step->value2 != NULL) {
    718 		    if (!xmlStrEqual(step->value2, node->ns->href))
    719 			goto rollback;
    720 		}
    721 		continue;
    722             case XSLT_OP_PARENT:
    723 		if ((node->type == XML_DOCUMENT_NODE) ||
    724 		    (node->type == XML_HTML_DOCUMENT_NODE) ||
    725 #ifdef LIBXML_DOCB_ENABLED
    726 		    (node->type == XML_DOCB_DOCUMENT_NODE) ||
    727 #endif
    728 		    (node->type == XML_NAMESPACE_DECL))
    729 		    goto rollback;
    730 		node = node->parent;
    731 		if (node == NULL)
    732 		    goto rollback;
    733 		if (step->value == NULL)
    734 		    continue;
    735 		if (step->value[0] != node->name[0])
    736 		    goto rollback;
    737 		if (!xmlStrEqual(step->value, node->name))
    738 		    goto rollback;
    739 		/* Namespace test */
    740 		if (node->ns == NULL) {
    741 		    if (step->value2 != NULL)
    742 			goto rollback;
    743 		} else if (node->ns->href != NULL) {
    744 		    if (step->value2 == NULL)
    745 			goto rollback;
    746 		    if (!xmlStrEqual(step->value2, node->ns->href))
    747 			goto rollback;
    748 		}
    749 		continue;
    750             case XSLT_OP_ANCESTOR:
    751 		/* TODO: implement coalescing of ANCESTOR/NODE ops */
    752 		if (step->value == NULL) {
    753 		    step = &comp->steps[i+1];
    754 		    if (step->op == XSLT_OP_ROOT)
    755 			goto found;
    756 		    /* added NS, ID and KEY as a result of bug 168208 */
    757 		    if ((step->op != XSLT_OP_ELEM) &&
    758 			(step->op != XSLT_OP_ALL) &&
    759 			(step->op != XSLT_OP_NS) &&
    760 			(step->op != XSLT_OP_ID) &&
    761 			(step->op != XSLT_OP_KEY))
    762 			goto rollback;
    763 		}
    764 		if (node == NULL)
    765 		    goto rollback;
    766 		if ((node->type == XML_DOCUMENT_NODE) ||
    767 		    (node->type == XML_HTML_DOCUMENT_NODE) ||
    768 #ifdef LIBXML_DOCB_ENABLED
    769 		    (node->type == XML_DOCB_DOCUMENT_NODE) ||
    770 #endif
    771 		    (node->type == XML_NAMESPACE_DECL))
    772 		    goto rollback;
    773 		node = node->parent;
    774 		if ((step->op != XSLT_OP_ELEM) && step->op != XSLT_OP_ALL) {
    775 		    xsltPatPushState(ctxt, &states, i, node);
    776 		    continue;
    777 		}
    778 		i++;
    779 		if (step->value == NULL) {
    780 		    xsltPatPushState(ctxt, &states, i - 1, node);
    781 		    continue;
    782 		}
    783 		while (node != NULL) {
    784 		    if ((node->type == XML_ELEMENT_NODE) &&
    785 			(step->value[0] == node->name[0]) &&
    786 			(xmlStrEqual(step->value, node->name))) {
    787 			/* Namespace test */
    788 			if (node->ns == NULL) {
    789 			    if (step->value2 == NULL)
    790 				break;
    791 			} else if (node->ns->href != NULL) {
    792 			    if ((step->value2 != NULL) &&
    793 			        (xmlStrEqual(step->value2, node->ns->href)))
    794 				break;
    795 			}
    796 		    }
    797 		    node = node->parent;
    798 		}
    799 		if (node == NULL)
    800 		    goto rollback;
    801 		xsltPatPushState(ctxt, &states, i - 1, node);
    802 		continue;
    803             case XSLT_OP_ID: {
    804 		/* TODO Handle IDs decently, must be done differently */
    805 		xmlAttrPtr id;
    806 
    807 		if (node->type != XML_ELEMENT_NODE)
    808 		    goto rollback;
    809 
    810 		id = xmlGetID(node->doc, step->value);
    811 		if ((id == NULL) || (id->parent != node))
    812 		    goto rollback;
    813 		break;
    814 	    }
    815             case XSLT_OP_KEY: {
    816 		xmlNodeSetPtr list;
    817 		int indx;
    818 
    819 		list = xsltGetKey(ctxt, step->value,
    820 			          step->value3, step->value2);
    821 		if (list == NULL)
    822 		    goto rollback;
    823 		for (indx = 0;indx < list->nodeNr;indx++)
    824 		    if (list->nodeTab[indx] == node)
    825 			break;
    826 		if (indx >= list->nodeNr)
    827 		    goto rollback;
    828 		break;
    829 	    }
    830             case XSLT_OP_NS:
    831 		if (node->type != XML_ELEMENT_NODE)
    832 		    goto rollback;
    833 		if (node->ns == NULL) {
    834 		    if (step->value != NULL)
    835 			goto rollback;
    836 		} else if (node->ns->href != NULL) {
    837 		    if (step->value == NULL)
    838 			goto rollback;
    839 		    if (!xmlStrEqual(step->value, node->ns->href))
    840 			goto rollback;
    841 		}
    842 		break;
    843             case XSLT_OP_ALL:
    844 		if (node->type != XML_ELEMENT_NODE)
    845 		    goto rollback;
    846 		break;
    847 	    case XSLT_OP_PREDICATE: {
    848 		xmlNodePtr oldNode;
    849 		xmlDocPtr doc;
    850 		int oldCS, oldCP;
    851 		int pos = 0, len = 0;
    852 		int isRVT;
    853 
    854 		/*
    855 		 * when there is cascading XSLT_OP_PREDICATE, then use a
    856 		 * direct computation approach. It's not done directly
    857 		 * at the beginning of the routine to filter out as much
    858 		 * as possible this costly computation.
    859 		 */
    860 		if (comp->direct) {
    861 		    if (states.states != NULL) {
    862 			/* Free the rollback states */
    863 			xmlFree(states.states);
    864 		    }
    865 		    return(xsltTestCompMatchDirect(ctxt, comp, node,
    866 		    				   comp->nsList, comp->nsNr));
    867 		}
    868 
    869 		doc = node->doc;
    870 		if (XSLT_IS_RES_TREE_FRAG(doc))
    871 		    isRVT = 1;
    872 		else
    873 		    isRVT = 0;
    874 
    875 		/*
    876 		 * Depending on the last selection, one may need to
    877 		 * recompute contextSize and proximityPosition.
    878 		 */
    879 		oldCS = ctxt->xpathCtxt->contextSize;
    880 		oldCP = ctxt->xpathCtxt->proximityPosition;
    881 		if ((sel != NULL) &&
    882 		    (sel->op == XSLT_OP_ELEM) &&
    883 		    (sel->value != NULL) &&
    884 		    (node->type == XML_ELEMENT_NODE) &&
    885 		    (node->parent != NULL)) {
    886 		    xmlNodePtr previous;
    887 		    int ix, nocache = 0;
    888 
    889 		    previous = (xmlNodePtr)
    890 			XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
    891 		    ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
    892 		    if ((previous != NULL) &&
    893 			(previous->parent == node->parent)) {
    894 			/*
    895 			 * just walk back to adjust the index
    896 			 */
    897 			int indx = 0;
    898 			xmlNodePtr sibling = node;
    899 
    900 			while (sibling != NULL) {
    901 			    if (sibling == previous)
    902 				break;
    903 			    if ((previous->type == XML_ELEMENT_NODE) &&
    904 				(previous->name != NULL) &&
    905 				(sibling->name != NULL) &&
    906 				(previous->name[0] == sibling->name[0]) &&
    907 				(xmlStrEqual(previous->name, sibling->name)))
    908 			    {
    909 				if ((sel->value2 == NULL) ||
    910 				    ((sibling->ns != NULL) &&
    911 				     (xmlStrEqual(sel->value2,
    912 						  sibling->ns->href))))
    913 				    indx++;
    914 			    }
    915 			    sibling = sibling->prev;
    916 			}
    917 			if (sibling == NULL) {
    918 			    /* hum going backward in document order ... */
    919 			    indx = 0;
    920 			    sibling = node;
    921 			    while (sibling != NULL) {
    922 				if (sibling == previous)
    923 				    break;
    924 				if ((previous->type == XML_ELEMENT_NODE) &&
    925 				    (previous->name != NULL) &&
    926 				    (sibling->name != NULL) &&
    927 				    (previous->name[0] == sibling->name[0]) &&
    928 				    (xmlStrEqual(previous->name, sibling->name)))
    929 				{
    930 				    if ((sel->value2 == NULL) ||
    931 					((sibling->ns != NULL) &&
    932 					(xmlStrEqual(sel->value2,
    933 					sibling->ns->href))))
    934 				    {
    935 					indx--;
    936 				    }
    937 				}
    938 				sibling = sibling->next;
    939 			    }
    940 			}
    941 			if (sibling != NULL) {
    942 			    pos = ix + indx;
    943 			    /*
    944 			     * If the node is in a Value Tree we need to
    945 			     * save len, but cannot cache the node!
    946 			     * (bugs 153137 and 158840)
    947 			     */
    948 			    if (node->doc != NULL) {
    949 				len = XSLT_RUNTIME_EXTRA(ctxt,
    950 				        sel->lenExtra, ival);
    951 				if (!isRVT) {
    952 				    XSLT_RUNTIME_EXTRA(ctxt,
    953 					sel->previousExtra, ptr) = node;
    954 				    XSLT_RUNTIME_EXTRA(ctxt,
    955 				        sel->indexExtra, ival) = pos;
    956 				}
    957 			    }
    958 			    ix = pos;
    959 			} else
    960 			    pos = 0;
    961 		    } else {
    962 			/*
    963 			 * recompute the index
    964 			 */
    965 			xmlNodePtr parent = node->parent;
    966 			xmlNodePtr siblings = NULL;
    967 
    968                         if (parent) siblings = parent->children;
    969 
    970 			while (siblings != NULL) {
    971 			    if (siblings->type == XML_ELEMENT_NODE) {
    972 				if (siblings == node) {
    973 				    len++;
    974 				    pos = len;
    975 				} else if ((node->name != NULL) &&
    976 					   (siblings->name != NULL) &&
    977 				    (node->name[0] == siblings->name[0]) &&
    978 				    (xmlStrEqual(node->name, siblings->name))) {
    979 				    if ((sel->value2 == NULL) ||
    980 					((siblings->ns != NULL) &&
    981 					 (xmlStrEqual(sel->value2,
    982 						      siblings->ns->href))))
    983 					len++;
    984 				}
    985 			    }
    986 			    siblings = siblings->next;
    987 			}
    988 			if ((parent == NULL) || (node->doc == NULL))
    989 			    nocache = 1;
    990 			else {
    991 			    while (parent->parent != NULL)
    992 				parent = parent->parent;
    993 			    if (((parent->type != XML_DOCUMENT_NODE) &&
    994 				 (parent->type != XML_HTML_DOCUMENT_NODE)) ||
    995 				 (parent != (xmlNodePtr) node->doc))
    996 				nocache = 1;
    997 			}
    998 		    }
    999 		    if (pos != 0) {
   1000 			ctxt->xpathCtxt->contextSize = len;
   1001 			ctxt->xpathCtxt->proximityPosition = pos;
   1002 			/*
   1003 			 * If the node is in a Value Tree we cannot
   1004 			 * cache it !
   1005 			 */
   1006 			if ((!isRVT) && (node->doc != NULL) &&
   1007 			    (nocache == 0)) {
   1008 			    XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
   1009 				node;
   1010 			    XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
   1011 				pos;
   1012 			    XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) =
   1013 				len;
   1014 			}
   1015 		    }
   1016 		} else if ((sel != NULL) && (sel->op == XSLT_OP_ALL) &&
   1017 			   (node->type == XML_ELEMENT_NODE)) {
   1018 		    xmlNodePtr previous;
   1019 		    int ix, nocache = 0;
   1020 
   1021 		    previous = (xmlNodePtr)
   1022 			XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
   1023 		    ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
   1024 		    if ((previous != NULL) &&
   1025 			(previous->parent == node->parent)) {
   1026 			/*
   1027 			 * just walk back to adjust the index
   1028 			 */
   1029 			int indx = 0;
   1030 			xmlNodePtr sibling = node;
   1031 
   1032 			while (sibling != NULL) {
   1033 			    if (sibling == previous)
   1034 				break;
   1035 			    if (sibling->type == XML_ELEMENT_NODE)
   1036 				indx++;
   1037 			    sibling = sibling->prev;
   1038 			}
   1039 			if (sibling == NULL) {
   1040 			    /* hum going backward in document order ... */
   1041 			    indx = 0;
   1042 			    sibling = node;
   1043 			    while (sibling != NULL) {
   1044 				if (sibling == previous)
   1045 				    break;
   1046 				if (sibling->type == XML_ELEMENT_NODE)
   1047 				    indx--;
   1048 				sibling = sibling->next;
   1049 			    }
   1050 			}
   1051 			if (sibling != NULL) {
   1052 			    pos = ix + indx;
   1053 			    /*
   1054 			     * If the node is in a Value Tree we cannot
   1055 			     * cache it !
   1056 			     */
   1057 			    if ((node->doc != NULL) && !isRVT) {
   1058 				len = XSLT_RUNTIME_EXTRA(ctxt,
   1059 				        sel->lenExtra, ival);
   1060 				XSLT_RUNTIME_EXTRA(ctxt,
   1061 					sel->previousExtra, ptr) = node;
   1062 				XSLT_RUNTIME_EXTRA(ctxt,
   1063 					sel->indexExtra, ival) = pos;
   1064 			    }
   1065 			} else
   1066 			    pos = 0;
   1067 		    } else {
   1068 			/*
   1069 			 * recompute the index
   1070 			 */
   1071 			xmlNodePtr parent = node->parent;
   1072 			xmlNodePtr siblings = NULL;
   1073 
   1074                         if (parent) siblings = parent->children;
   1075 
   1076 			while (siblings != NULL) {
   1077 			    if (siblings->type == XML_ELEMENT_NODE) {
   1078 				len++;
   1079 				if (siblings == node) {
   1080 				    pos = len;
   1081 				}
   1082 			    }
   1083 			    siblings = siblings->next;
   1084 			}
   1085 			if ((parent == NULL) || (node->doc == NULL))
   1086 			    nocache = 1;
   1087 			else {
   1088 			    while (parent->parent != NULL)
   1089 				parent = parent->parent;
   1090 			    if (((parent->type != XML_DOCUMENT_NODE) &&
   1091 				 (parent->type != XML_HTML_DOCUMENT_NODE)) ||
   1092 				 (parent != (xmlNodePtr) node->doc))
   1093 				nocache = 1;
   1094 			}
   1095 		    }
   1096 		    if (pos != 0) {
   1097 			ctxt->xpathCtxt->contextSize = len;
   1098 			ctxt->xpathCtxt->proximityPosition = pos;
   1099 			/*
   1100 			 * If the node is in a Value Tree we cannot
   1101 			 * cache it !
   1102 			 */
   1103 			if ((node->doc != NULL) && (nocache == 0) && !isRVT) {
   1104 			    XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
   1105 				node;
   1106 			    XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
   1107 				pos;
   1108 			    XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) =
   1109 				len;
   1110 			}
   1111 		    }
   1112 		}
   1113 		oldNode = ctxt->node;
   1114 		ctxt->node = node;
   1115 
   1116 		if (step->value == NULL)
   1117 		    goto wrong_index;
   1118 		if (step->comp == NULL)
   1119 		    goto wrong_index;
   1120 
   1121 		if (!xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList,
   1122 			                    comp->nsNr))
   1123 		    goto wrong_index;
   1124 
   1125 		if (pos != 0) {
   1126 		    ctxt->xpathCtxt->contextSize = oldCS;
   1127 		    ctxt->xpathCtxt->proximityPosition = oldCP;
   1128 		}
   1129 		ctxt->node = oldNode;
   1130 		break;
   1131 wrong_index:
   1132 		if (pos != 0) {
   1133 		    ctxt->xpathCtxt->contextSize = oldCS;
   1134 		    ctxt->xpathCtxt->proximityPosition = oldCP;
   1135 		}
   1136 		ctxt->node = oldNode;
   1137 		goto rollback;
   1138 	    }
   1139             case XSLT_OP_PI:
   1140 		if (node->type != XML_PI_NODE)
   1141 		    goto rollback;
   1142 		if (step->value != NULL) {
   1143 		    if (!xmlStrEqual(step->value, node->name))
   1144 			goto rollback;
   1145 		}
   1146 		break;
   1147             case XSLT_OP_COMMENT:
   1148 		if (node->type != XML_COMMENT_NODE)
   1149 		    goto rollback;
   1150 		break;
   1151             case XSLT_OP_TEXT:
   1152 		if ((node->type != XML_TEXT_NODE) &&
   1153 		    (node->type != XML_CDATA_SECTION_NODE))
   1154 		    goto rollback;
   1155 		break;
   1156             case XSLT_OP_NODE:
   1157 		switch (node->type) {
   1158 		    case XML_ELEMENT_NODE:
   1159 		    case XML_CDATA_SECTION_NODE:
   1160 		    case XML_PI_NODE:
   1161 		    case XML_COMMENT_NODE:
   1162 		    case XML_TEXT_NODE:
   1163 			break;
   1164 		    default:
   1165 			goto rollback;
   1166 		}
   1167 		break;
   1168 	}
   1169     }
   1170 found:
   1171     if (states.states != NULL) {
   1172         /* Free the rollback states */
   1173 	xmlFree(states.states);
   1174     }
   1175     return(1);
   1176 rollback:
   1177     /* got an error try to rollback */
   1178     if (states.states == NULL)
   1179 	return(0);
   1180     if (states.nbstates <= 0) {
   1181 	xmlFree(states.states);
   1182 	return(0);
   1183     }
   1184     states.nbstates--;
   1185     i = states.states[states.nbstates].step;
   1186     node = states.states[states.nbstates].node;
   1187 #if 0
   1188     fprintf(stderr, "Pop: %d, %s\n", i, node->name);
   1189 #endif
   1190     goto restart;
   1191 }
   1192 
   1193 /**
   1194  * xsltTestCompMatchList:
   1195  * @ctxt:  a XSLT process context
   1196  * @node: a node
   1197  * @comp: the precompiled pattern list
   1198  *
   1199  * Test whether the node matches one of the patterns in the list
   1200  *
   1201  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
   1202  */
   1203 int
   1204 xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node,
   1205 	              xsltCompMatchPtr comp) {
   1206     int ret;
   1207 
   1208     if ((ctxt == NULL) || (node == NULL))
   1209 	return(-1);
   1210     while (comp != NULL) {
   1211 	ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL);
   1212 	if (ret == 1)
   1213 	    return(1);
   1214 	comp = comp->next;
   1215     }
   1216     return(0);
   1217 }
   1218 
   1219 /************************************************************************
   1220  *									*
   1221  *			Dedicated parser for templates			*
   1222  *									*
   1223  ************************************************************************/
   1224 
   1225 #define CUR (*ctxt->cur)
   1226 #define SKIP(val) ctxt->cur += (val)
   1227 #define NXT(val) ctxt->cur[(val)]
   1228 #define CUR_PTR ctxt->cur
   1229 
   1230 #define SKIP_BLANKS 							\
   1231     while (IS_BLANK_CH(CUR)) NEXT
   1232 
   1233 #define CURRENT (*ctxt->cur)
   1234 #define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
   1235 
   1236 
   1237 #define PUSH(op, val, val2, novar) 						\
   1238     if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto error;
   1239 
   1240 #define SWAP() 						\
   1241     xsltSwapTopCompMatch(ctxt->comp);
   1242 
   1243 #define XSLT_ERROR(X)							\
   1244     { xsltError(ctxt, __FILE__, __LINE__, X);			\
   1245       ctxt->error = (X); return; }
   1246 
   1247 #define XSLT_ERROR0(X)							\
   1248     { xsltError(ctxt, __FILE__, __LINE__, X);			\
   1249       ctxt->error = (X); return(0); }
   1250 
   1251 /**
   1252  * xsltScanLiteral:
   1253  * @ctxt:  the XPath Parser context
   1254  *
   1255  * Parse an XPath Litteral:
   1256  *
   1257  * [29] Literal ::= '"' [^"]* '"'
   1258  *                | "'" [^']* "'"
   1259  *
   1260  * Returns the Literal parsed or NULL
   1261  */
   1262 
   1263 static xmlChar *
   1264 xsltScanLiteral(xsltParserContextPtr ctxt) {
   1265     const xmlChar *q, *cur;
   1266     xmlChar *ret = NULL;
   1267     int val, len;
   1268 
   1269     SKIP_BLANKS;
   1270     if (CUR == '"') {
   1271         NEXT;
   1272 	cur = q = CUR_PTR;
   1273 	val = xmlStringCurrentChar(NULL, cur, &len);
   1274 	while ((IS_CHAR(val)) && (val != '"')) {
   1275 	    cur += len;
   1276 	    val = xmlStringCurrentChar(NULL, cur, &len);
   1277 	}
   1278 	if (!IS_CHAR(val)) {
   1279 	    ctxt->error = 1;
   1280 	    return(NULL);
   1281 	} else {
   1282 	    ret = xmlStrndup(q, cur - q);
   1283         }
   1284 	cur += len;
   1285 	CUR_PTR = cur;
   1286     } else if (CUR == '\'') {
   1287         NEXT;
   1288 	cur = q = CUR_PTR;
   1289 	val = xmlStringCurrentChar(NULL, cur, &len);
   1290 	while ((IS_CHAR(val)) && (val != '\'')) {
   1291 	    cur += len;
   1292 	    val = xmlStringCurrentChar(NULL, cur, &len);
   1293 	}
   1294 	if (!IS_CHAR(val)) {
   1295 	    ctxt->error = 1;
   1296 	    return(NULL);
   1297 	} else {
   1298 	    ret = xmlStrndup(q, cur - q);
   1299         }
   1300 	cur += len;
   1301 	CUR_PTR = cur;
   1302     } else {
   1303 	/* XP_ERROR(XPATH_START_LITERAL_ERROR); */
   1304 	ctxt->error = 1;
   1305 	return(NULL);
   1306     }
   1307     return(ret);
   1308 }
   1309 
   1310 /**
   1311  * xsltScanNCName:
   1312  * @ctxt:  the XPath Parser context
   1313  *
   1314  * Parses a non qualified name
   1315  *
   1316  * Returns the Name parsed or NULL
   1317  */
   1318 
   1319 static xmlChar *
   1320 xsltScanNCName(xsltParserContextPtr ctxt) {
   1321     const xmlChar *q, *cur;
   1322     xmlChar *ret = NULL;
   1323     int val, len;
   1324 
   1325     SKIP_BLANKS;
   1326 
   1327     cur = q = CUR_PTR;
   1328     val = xmlStringCurrentChar(NULL, cur, &len);
   1329     if (!IS_LETTER(val) && (val != '_'))
   1330 	return(NULL);
   1331 
   1332     while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
   1333            (val == '.') || (val == '-') ||
   1334 	   (val == '_') ||
   1335 	   (IS_COMBINING(val)) ||
   1336 	   (IS_EXTENDER(val))) {
   1337 	cur += len;
   1338 	val = xmlStringCurrentChar(NULL, cur, &len);
   1339     }
   1340     ret = xmlStrndup(q, cur - q);
   1341     CUR_PTR = cur;
   1342     return(ret);
   1343 }
   1344 
   1345 /*
   1346  * xsltCompileIdKeyPattern:
   1347  * @ctxt:  the compilation context
   1348  * @name:  a preparsed name
   1349  * @aid:  whether id/key are allowed there
   1350  * @novar:  flag to prohibit xslt var
   1351  *
   1352  * Compile the XSLT LocationIdKeyPattern
   1353  * [3] IdKeyPattern ::= 'id' '(' Literal ')'
   1354  *                    | 'key' '(' Literal ',' Literal ')'
   1355  *
   1356  * also handle NodeType and PI from:
   1357  *
   1358  * [7]  NodeTest ::= NameTest
   1359  *                 | NodeType '(' ')'
   1360  *                 | 'processing-instruction' '(' Literal ')'
   1361  */
   1362 static void
   1363 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name,
   1364 		int aid, int novar, xsltAxis axis) {
   1365     xmlChar *lit = NULL;
   1366     xmlChar *lit2 = NULL;
   1367 
   1368     if (CUR != '(') {
   1369 	xsltTransformError(NULL, NULL, NULL,
   1370 		"xsltCompileIdKeyPattern : ( expected\n");
   1371 	ctxt->error = 1;
   1372 	return;
   1373     }
   1374     if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
   1375 	if (axis != 0) {
   1376 	    xsltTransformError(NULL, NULL, NULL,
   1377 		    "xsltCompileIdKeyPattern : NodeTest expected\n");
   1378 	    ctxt->error = 1;
   1379 	    return;
   1380 	}
   1381 	NEXT;
   1382 	SKIP_BLANKS;
   1383         lit = xsltScanLiteral(ctxt);
   1384 	if (ctxt->error)
   1385 	    return;
   1386 	SKIP_BLANKS;
   1387 	if (CUR != ')') {
   1388 	    xsltTransformError(NULL, NULL, NULL,
   1389 		    "xsltCompileIdKeyPattern : ) expected\n");
   1390 	    ctxt->error = 1;
   1391 	    return;
   1392 	}
   1393 	NEXT;
   1394 	PUSH(XSLT_OP_ID, lit, NULL, novar);
   1395     } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
   1396 	if (axis != 0) {
   1397 	    xsltTransformError(NULL, NULL, NULL,
   1398 		    "xsltCompileIdKeyPattern : NodeTest expected\n");
   1399 	    ctxt->error = 1;
   1400 	    return;
   1401 	}
   1402 	NEXT;
   1403 	SKIP_BLANKS;
   1404         lit = xsltScanLiteral(ctxt);
   1405 	if (ctxt->error)
   1406 	    return;
   1407 	SKIP_BLANKS;
   1408 	if (CUR != ',') {
   1409 	    xsltTransformError(NULL, NULL, NULL,
   1410 		    "xsltCompileIdKeyPattern : , expected\n");
   1411 	    ctxt->error = 1;
   1412 	    return;
   1413 	}
   1414 	NEXT;
   1415 	SKIP_BLANKS;
   1416         lit2 = xsltScanLiteral(ctxt);
   1417 	if (ctxt->error)
   1418 	    return;
   1419 	SKIP_BLANKS;
   1420 	if (CUR != ')') {
   1421 	    xsltTransformError(NULL, NULL, NULL,
   1422 		    "xsltCompileIdKeyPattern : ) expected\n");
   1423 	    ctxt->error = 1;
   1424 	    return;
   1425 	}
   1426 	NEXT;
   1427 	/* URGENT TODO: support namespace in keys */
   1428 	PUSH(XSLT_OP_KEY, lit, lit2, novar);
   1429     } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
   1430 	NEXT;
   1431 	SKIP_BLANKS;
   1432 	if (CUR != ')') {
   1433 	    lit = xsltScanLiteral(ctxt);
   1434 	    if (ctxt->error)
   1435 		return;
   1436 	    SKIP_BLANKS;
   1437 	    if (CUR != ')') {
   1438 		xsltTransformError(NULL, NULL, NULL,
   1439 			"xsltCompileIdKeyPattern : ) expected\n");
   1440 		ctxt->error = 1;
   1441 		return;
   1442 	    }
   1443 	}
   1444 	NEXT;
   1445 	PUSH(XSLT_OP_PI, lit, NULL, novar);
   1446     } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
   1447 	NEXT;
   1448 	SKIP_BLANKS;
   1449 	if (CUR != ')') {
   1450 	    xsltTransformError(NULL, NULL, NULL,
   1451 		    "xsltCompileIdKeyPattern : ) expected\n");
   1452 	    ctxt->error = 1;
   1453 	    return;
   1454 	}
   1455 	NEXT;
   1456 	PUSH(XSLT_OP_TEXT, NULL, NULL, novar);
   1457     } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
   1458 	NEXT;
   1459 	SKIP_BLANKS;
   1460 	if (CUR != ')') {
   1461 	    xsltTransformError(NULL, NULL, NULL,
   1462 		    "xsltCompileIdKeyPattern : ) expected\n");
   1463 	    ctxt->error = 1;
   1464 	    return;
   1465 	}
   1466 	NEXT;
   1467 	PUSH(XSLT_OP_COMMENT, NULL, NULL, novar);
   1468     } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
   1469 	NEXT;
   1470 	SKIP_BLANKS;
   1471 	if (CUR != ')') {
   1472 	    xsltTransformError(NULL, NULL, NULL,
   1473 		    "xsltCompileIdKeyPattern : ) expected\n");
   1474 	    ctxt->error = 1;
   1475 	    return;
   1476 	}
   1477 	NEXT;
   1478 	if (axis == AXIS_ATTRIBUTE) {
   1479 	    PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
   1480 	}
   1481 	else {
   1482 	    PUSH(XSLT_OP_NODE, NULL, NULL, novar);
   1483 	}
   1484     } else if (aid) {
   1485 	xsltTransformError(NULL, NULL, NULL,
   1486 	    "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
   1487 	ctxt->error = 1;
   1488 	return;
   1489     } else {
   1490 	xsltTransformError(NULL, NULL, NULL,
   1491 	    "xsltCompileIdKeyPattern : node type\n");
   1492 	ctxt->error = 1;
   1493 	return;
   1494     }
   1495 error:
   1496     if (name != NULL)
   1497 	xmlFree(name);
   1498 }
   1499 
   1500 /**
   1501  * xsltCompileStepPattern:
   1502  * @ctxt:  the compilation context
   1503  * @token:  a posible precompiled name
   1504  * @novar: flag to prohibit xslt variables from pattern
   1505  *
   1506  * Compile the XSLT StepPattern and generates a precompiled
   1507  * form suitable for fast matching.
   1508  *
   1509  * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
   1510  * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
   1511  *                                     | ('child' | 'attribute') '::'
   1512  * from XPath
   1513  * [7]  NodeTest ::= NameTest
   1514  *                 | NodeType '(' ')'
   1515  *                 | 'processing-instruction' '(' Literal ')'
   1516  * [8] Predicate ::= '[' PredicateExpr ']'
   1517  * [9] PredicateExpr ::= Expr
   1518  * [13] AbbreviatedAxisSpecifier ::= '@'?
   1519  * [37] NameTest ::= '*' | NCName ':' '*' | QName
   1520  */
   1521 
   1522 static void
   1523 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
   1524     xmlChar *name = NULL;
   1525     const xmlChar *URI = NULL;
   1526     xmlChar *URL = NULL;
   1527     int level;
   1528     xsltAxis axis = 0;
   1529 
   1530     SKIP_BLANKS;
   1531     if ((token == NULL) && (CUR == '@')) {
   1532 	NEXT;
   1533         axis = AXIS_ATTRIBUTE;
   1534     }
   1535 parse_node_test:
   1536     if (token == NULL)
   1537 	token = xsltScanNCName(ctxt);
   1538     if (token == NULL) {
   1539 	if (CUR == '*') {
   1540 	    NEXT;
   1541 	    if (axis == AXIS_ATTRIBUTE) {
   1542                 PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
   1543             }
   1544             else {
   1545                 PUSH(XSLT_OP_ALL, NULL, NULL, novar);
   1546             }
   1547 	    goto parse_predicate;
   1548 	} else {
   1549 	    xsltTransformError(NULL, NULL, NULL,
   1550 		    "xsltCompileStepPattern : Name expected\n");
   1551 	    ctxt->error = 1;
   1552 	    goto error;
   1553 	}
   1554     }
   1555 
   1556 
   1557     SKIP_BLANKS;
   1558     if (CUR == '(') {
   1559 	xsltCompileIdKeyPattern(ctxt, token, 0, novar, axis);
   1560 	if (ctxt->error)
   1561 	    goto error;
   1562     } else if (CUR == ':') {
   1563 	NEXT;
   1564 	if (CUR != ':') {
   1565 	    xmlChar *prefix = token;
   1566 	    xmlNsPtr ns;
   1567 
   1568 	    /*
   1569 	     * This is a namespace match
   1570 	     */
   1571 	    token = xsltScanNCName(ctxt);
   1572 	    ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
   1573 	    if (ns == NULL) {
   1574 		xsltTransformError(NULL, NULL, NULL,
   1575 	    "xsltCompileStepPattern : no namespace bound to prefix %s\n",
   1576 				 prefix);
   1577 		xmlFree(prefix);
   1578 		ctxt->error = 1;
   1579 		goto error;
   1580 	    } else {
   1581 		URL = xmlStrdup(ns->href);
   1582 	    }
   1583 	    xmlFree(prefix);
   1584 	    if (token == NULL) {
   1585 		if (CUR == '*') {
   1586 		    NEXT;
   1587                     if (axis == AXIS_ATTRIBUTE) {
   1588                         PUSH(XSLT_OP_ATTR, NULL, URL, novar);
   1589                     }
   1590                     else {
   1591                         PUSH(XSLT_OP_NS, URL, NULL, novar);
   1592                     }
   1593 		} else {
   1594 		    xsltTransformError(NULL, NULL, NULL,
   1595 			    "xsltCompileStepPattern : Name expected\n");
   1596 		    ctxt->error = 1;
   1597 		    goto error;
   1598 		}
   1599 	    } else {
   1600                 if (axis == AXIS_ATTRIBUTE) {
   1601                     PUSH(XSLT_OP_ATTR, token, URL, novar);
   1602                 }
   1603                 else {
   1604                     PUSH(XSLT_OP_ELEM, token, URL, novar);
   1605                 }
   1606 	    }
   1607 	} else {
   1608 	    if (axis != 0) {
   1609 		xsltTransformError(NULL, NULL, NULL,
   1610 		    "xsltCompileStepPattern : NodeTest expected\n");
   1611 		ctxt->error = 1;
   1612 		goto error;
   1613 	    }
   1614 	    NEXT;
   1615 	    if (xmlStrEqual(token, (const xmlChar *) "child")) {
   1616 	        axis = AXIS_CHILD;
   1617 	    } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
   1618 	        axis = AXIS_ATTRIBUTE;
   1619 	    } else {
   1620 		xsltTransformError(NULL, NULL, NULL,
   1621 		    "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
   1622 		ctxt->error = 1;
   1623 		goto error;
   1624 	    }
   1625 	    xmlFree(token);
   1626             SKIP_BLANKS;
   1627             token = xsltScanNCName(ctxt);
   1628 	    goto parse_node_test;
   1629 	}
   1630     } else {
   1631 	URI = xsltGetQNameURI(ctxt->elem, &token);
   1632 	if (token == NULL) {
   1633 	    ctxt->error = 1;
   1634 	    goto error;
   1635 	}
   1636 	if (URI != NULL)
   1637 	    URL = xmlStrdup(URI);
   1638         if (axis == AXIS_ATTRIBUTE) {
   1639             PUSH(XSLT_OP_ATTR, token, URL, novar);
   1640         }
   1641         else {
   1642             PUSH(XSLT_OP_ELEM, token, URL, novar);
   1643         }
   1644     }
   1645 parse_predicate:
   1646     SKIP_BLANKS;
   1647     level = 0;
   1648     while (CUR == '[') {
   1649 	const xmlChar *q;
   1650 	xmlChar *ret = NULL;
   1651 
   1652 	level++;
   1653 	NEXT;
   1654 	q = CUR_PTR;
   1655 	while (CUR != 0) {
   1656 	    /* Skip over nested predicates */
   1657 	    if (CUR == '[')
   1658 		level++;
   1659 	    else if (CUR == ']') {
   1660 		level--;
   1661 		if (level == 0)
   1662 		    break;
   1663 	    } else if (CUR == '"') {
   1664 		NEXT;
   1665 		while ((CUR != 0) && (CUR != '"'))
   1666 		    NEXT;
   1667 	    } else if (CUR == '\'') {
   1668 		NEXT;
   1669 		while ((CUR != 0) && (CUR != '\''))
   1670 		    NEXT;
   1671 	    }
   1672 	    NEXT;
   1673 	}
   1674 	if (CUR == 0) {
   1675 	    xsltTransformError(NULL, NULL, NULL,
   1676 		    "xsltCompileStepPattern : ']' expected\n");
   1677 	    ctxt->error = 1;
   1678 	    return;
   1679         }
   1680 	ret = xmlStrndup(q, CUR_PTR - q);
   1681 	PUSH(XSLT_OP_PREDICATE, ret, NULL, novar);
   1682 	/* push the predicate lower than local test */
   1683 	SWAP();
   1684 	NEXT;
   1685 	SKIP_BLANKS;
   1686     }
   1687     return;
   1688 error:
   1689     if (token != NULL)
   1690 	xmlFree(token);
   1691     if (name != NULL)
   1692 	xmlFree(name);
   1693 }
   1694 
   1695 /**
   1696  * xsltCompileRelativePathPattern:
   1697  * @comp:  the compilation context
   1698  * @token:  a posible precompiled name
   1699  * @novar:  flag to prohibit xslt variables
   1700  *
   1701  * Compile the XSLT RelativePathPattern and generates a precompiled
   1702  * form suitable for fast matching.
   1703  *
   1704  * [4] RelativePathPattern ::= StepPattern
   1705  *                           | RelativePathPattern '/' StepPattern
   1706  *                           | RelativePathPattern '//' StepPattern
   1707  */
   1708 static void
   1709 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
   1710     xsltCompileStepPattern(ctxt, token, novar);
   1711     if (ctxt->error)
   1712 	goto error;
   1713     SKIP_BLANKS;
   1714     while ((CUR != 0) && (CUR != '|')) {
   1715 	if ((CUR == '/') && (NXT(1) == '/')) {
   1716 	    PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
   1717 	    NEXT;
   1718 	    NEXT;
   1719 	    SKIP_BLANKS;
   1720 	    xsltCompileStepPattern(ctxt, NULL, novar);
   1721 	} else if (CUR == '/') {
   1722 	    PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
   1723 	    NEXT;
   1724 	    SKIP_BLANKS;
   1725 	    if ((CUR != 0) && (CUR != '|')) {
   1726 		xsltCompileRelativePathPattern(ctxt, NULL, novar);
   1727 	    }
   1728 	} else {
   1729 	    ctxt->error = 1;
   1730 	}
   1731 	if (ctxt->error)
   1732 	    goto error;
   1733 	SKIP_BLANKS;
   1734     }
   1735 error:
   1736     return;
   1737 }
   1738 
   1739 /**
   1740  * xsltCompileLocationPathPattern:
   1741  * @ctxt:  the compilation context
   1742  * @novar:  flag to prohibit xslt variables
   1743  *
   1744  * Compile the XSLT LocationPathPattern and generates a precompiled
   1745  * form suitable for fast matching.
   1746  *
   1747  * [2] LocationPathPattern ::= '/' RelativePathPattern?
   1748  *                           | IdKeyPattern (('/' | '//') RelativePathPattern)?
   1749  *                           | '//'? RelativePathPattern
   1750  */
   1751 static void
   1752 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) {
   1753     SKIP_BLANKS;
   1754     if ((CUR == '/') && (NXT(1) == '/')) {
   1755 	/*
   1756 	 * since we reverse the query
   1757 	 * a leading // can be safely ignored
   1758 	 */
   1759 	NEXT;
   1760 	NEXT;
   1761 	ctxt->comp->priority = 0.5;	/* '//' means not 0 priority */
   1762 	xsltCompileRelativePathPattern(ctxt, NULL, novar);
   1763     } else if (CUR == '/') {
   1764 	/*
   1765 	 * We need to find root as the parent
   1766 	 */
   1767 	NEXT;
   1768 	SKIP_BLANKS;
   1769 	PUSH(XSLT_OP_ROOT, NULL, NULL, novar);
   1770 	if ((CUR != 0) && (CUR != '|')) {
   1771 	    PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
   1772 	    xsltCompileRelativePathPattern(ctxt, NULL, novar);
   1773 	}
   1774     } else if (CUR == '*') {
   1775 	xsltCompileRelativePathPattern(ctxt, NULL, novar);
   1776     } else if (CUR == '@') {
   1777 	xsltCompileRelativePathPattern(ctxt, NULL, novar);
   1778     } else {
   1779 	xmlChar *name;
   1780 	name = xsltScanNCName(ctxt);
   1781 	if (name == NULL) {
   1782 	    xsltTransformError(NULL, NULL, NULL,
   1783 		    "xsltCompileLocationPathPattern : Name expected\n");
   1784 	    ctxt->error = 1;
   1785 	    return;
   1786 	}
   1787 	SKIP_BLANKS;
   1788 	if ((CUR == '(') && !xmlXPathIsNodeType(name)) {
   1789 	    xsltCompileIdKeyPattern(ctxt, name, 1, novar, 0);
   1790 	    if (ctxt->error)
   1791 		return;
   1792 	    if ((CUR == '/') && (NXT(1) == '/')) {
   1793 		PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
   1794 		NEXT;
   1795 		NEXT;
   1796 		SKIP_BLANKS;
   1797 		xsltCompileRelativePathPattern(ctxt, NULL, novar);
   1798 	    } else if (CUR == '/') {
   1799 		PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
   1800 		NEXT;
   1801 		SKIP_BLANKS;
   1802 		xsltCompileRelativePathPattern(ctxt, NULL, novar);
   1803 	    }
   1804 	    return;
   1805 	}
   1806 	xsltCompileRelativePathPattern(ctxt, name, novar);
   1807     }
   1808 error:
   1809     return;
   1810 }
   1811 
   1812 /**
   1813  * xsltCompilePatternInternal:
   1814  * @pattern: an XSLT pattern
   1815  * @doc:  the containing document
   1816  * @node:  the containing element
   1817  * @style:  the stylesheet
   1818  * @runtime:  the transformation context, if done at run-time
   1819  * @novar:  flag to prohibit xslt variables
   1820  *
   1821  * Compile the XSLT pattern and generates a list of precompiled form suitable
   1822  * for fast matching.
   1823  *
   1824  * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
   1825  *
   1826  * Returns the generated pattern list or NULL in case of failure
   1827  */
   1828 
   1829 static xsltCompMatchPtr
   1830 xsltCompilePatternInternal(const xmlChar *pattern, xmlDocPtr doc,
   1831 	           xmlNodePtr node, xsltStylesheetPtr style,
   1832 		   xsltTransformContextPtr runtime, int novar) {
   1833     xsltParserContextPtr ctxt = NULL;
   1834     xsltCompMatchPtr element, first = NULL, previous = NULL;
   1835     int current, start, end, level, j;
   1836 
   1837     if (pattern == NULL) {
   1838 	xsltTransformError(NULL, NULL, node,
   1839 			 "xsltCompilePattern : NULL pattern\n");
   1840 	return(NULL);
   1841     }
   1842 
   1843     ctxt = xsltNewParserContext(style, runtime);
   1844     if (ctxt == NULL)
   1845 	return(NULL);
   1846     ctxt->doc = doc;
   1847     ctxt->elem = node;
   1848     current = end = 0;
   1849     while (pattern[current] != 0) {
   1850 	start = current;
   1851 	while (IS_BLANK_CH(pattern[current]))
   1852 	    current++;
   1853 	end = current;
   1854 	level = 0;
   1855 	while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) {
   1856 	    if (pattern[end] == '[')
   1857 		level++;
   1858 	    else if (pattern[end] == ']')
   1859 		level--;
   1860 	    else if (pattern[end] == '\'') {
   1861 		end++;
   1862 		while ((pattern[end] != 0) && (pattern[end] != '\''))
   1863 		    end++;
   1864 	    } else if (pattern[end] == '"') {
   1865 		end++;
   1866 		while ((pattern[end] != 0) && (pattern[end] != '"'))
   1867 		    end++;
   1868 	    }
   1869       if (pattern[end] == 0)
   1870           break;
   1871 	    end++;
   1872 	}
   1873 	if (current == end) {
   1874 	    xsltTransformError(NULL, NULL, node,
   1875 			     "xsltCompilePattern : NULL pattern\n");
   1876 	    goto error;
   1877 	}
   1878 	element = xsltNewCompMatch();
   1879 	if (element == NULL) {
   1880 	    goto error;
   1881 	}
   1882 	if (first == NULL)
   1883 	    first = element;
   1884 	else if (previous != NULL)
   1885 	    previous->next = element;
   1886 	previous = element;
   1887 
   1888 	ctxt->comp = element;
   1889 	ctxt->base = xmlStrndup(&pattern[start], end - start);
   1890 	if (ctxt->base == NULL)
   1891 	    goto error;
   1892 	ctxt->cur = &(ctxt->base)[current - start];
   1893 	element->pattern = ctxt->base;
   1894 	element->nsList = xmlGetNsList(doc, node);
   1895 	j = 0;
   1896 	if (element->nsList != NULL) {
   1897 	    while (element->nsList[j] != NULL)
   1898 		j++;
   1899 	}
   1900 	element->nsNr = j;
   1901 
   1902 
   1903 #ifdef WITH_XSLT_DEBUG_PATTERN
   1904 	xsltGenericDebug(xsltGenericDebugContext,
   1905 			 "xsltCompilePattern : parsing '%s'\n",
   1906 			 element->pattern);
   1907 #endif
   1908 	/*
   1909 	 Preset default priority to be zero.
   1910 	 This may be changed by xsltCompileLocationPathPattern.
   1911 	 */
   1912 	element->priority = 0;
   1913 	xsltCompileLocationPathPattern(ctxt, novar);
   1914 	if (ctxt->error) {
   1915 	    xsltTransformError(NULL, style, node,
   1916 			     "xsltCompilePattern : failed to compile '%s'\n",
   1917 			     element->pattern);
   1918 	    if (style != NULL) style->errors++;
   1919 	    goto error;
   1920 	}
   1921 
   1922 	/*
   1923 	 * Reverse for faster interpretation.
   1924 	 */
   1925 	xsltReverseCompMatch(ctxt, element);
   1926 
   1927 	/*
   1928 	 * Set-up the priority
   1929 	 */
   1930 	if (element->priority == 0) {	/* if not yet determined */
   1931 	    if (((element->steps[0].op == XSLT_OP_ELEM) ||
   1932 		 (element->steps[0].op == XSLT_OP_ATTR) ||
   1933 		 (element->steps[0].op == XSLT_OP_PI)) &&
   1934 		(element->steps[0].value != NULL) &&
   1935 		(element->steps[1].op == XSLT_OP_END)) {
   1936 		;	/* previously preset */
   1937 	    } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
   1938 		       (element->steps[0].value2 != NULL) &&
   1939 		       (element->steps[1].op == XSLT_OP_END)) {
   1940 			element->priority = -0.25;
   1941 	    } else if ((element->steps[0].op == XSLT_OP_NS) &&
   1942 		       (element->steps[0].value != NULL) &&
   1943 		       (element->steps[1].op == XSLT_OP_END)) {
   1944 			element->priority = -0.25;
   1945 	    } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
   1946 		       (element->steps[0].value == NULL) &&
   1947 		       (element->steps[0].value2 == NULL) &&
   1948 		       (element->steps[1].op == XSLT_OP_END)) {
   1949 			element->priority = -0.5;
   1950 	    } else if (((element->steps[0].op == XSLT_OP_PI) ||
   1951 		       (element->steps[0].op == XSLT_OP_TEXT) ||
   1952 		       (element->steps[0].op == XSLT_OP_ALL) ||
   1953 		       (element->steps[0].op == XSLT_OP_NODE) ||
   1954 		       (element->steps[0].op == XSLT_OP_COMMENT)) &&
   1955 		       (element->steps[1].op == XSLT_OP_END)) {
   1956 			element->priority = -0.5;
   1957 	    } else {
   1958 		element->priority = 0.5;
   1959 	    }
   1960 	}
   1961 #ifdef WITH_XSLT_DEBUG_PATTERN
   1962 	xsltGenericDebug(xsltGenericDebugContext,
   1963 		     "xsltCompilePattern : parsed %s, default priority %f\n",
   1964 			 element->pattern, element->priority);
   1965 #endif
   1966 	if (pattern[end] == '|')
   1967 	    end++;
   1968 	current = end;
   1969     }
   1970     if (end == 0) {
   1971 	xsltTransformError(NULL, style, node,
   1972 			 "xsltCompilePattern : NULL pattern\n");
   1973 	if (style != NULL) style->errors++;
   1974 	goto error;
   1975     }
   1976 
   1977     xsltFreeParserContext(ctxt);
   1978     return(first);
   1979 
   1980 error:
   1981     if (ctxt != NULL)
   1982 	xsltFreeParserContext(ctxt);
   1983     if (first != NULL)
   1984 	xsltFreeCompMatchList(first);
   1985     return(NULL);
   1986 }
   1987 
   1988 /**
   1989  * xsltCompilePattern:
   1990  * @pattern: an XSLT pattern
   1991  * @doc:  the containing document
   1992  * @node:  the containing element
   1993  * @style:  the stylesheet
   1994  * @runtime:  the transformation context, if done at run-time
   1995  *
   1996  * Compile the XSLT pattern and generates a list of precompiled form suitable
   1997  * for fast matching.
   1998  *
   1999  * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
   2000  *
   2001  * Returns the generated pattern list or NULL in case of failure
   2002  */
   2003 
   2004 xsltCompMatchPtr
   2005 xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc,
   2006 	           xmlNodePtr node, xsltStylesheetPtr style,
   2007 		   xsltTransformContextPtr runtime) {
   2008     return (xsltCompilePatternInternal(pattern, doc, node, style, runtime, 0));
   2009 }
   2010 
   2011 /************************************************************************
   2012  *									*
   2013  *			Module interfaces				*
   2014  *									*
   2015  ************************************************************************/
   2016 
   2017 /**
   2018  * xsltAddTemplate:
   2019  * @style: an XSLT stylesheet
   2020  * @cur: an XSLT template
   2021  * @mode:  the mode name or NULL
   2022  * @modeURI:  the mode URI or NULL
   2023  *
   2024  * Register the XSLT pattern associated to @cur
   2025  *
   2026  * Returns -1 in case of error, 0 otherwise
   2027  */
   2028 int
   2029 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
   2030 	        const xmlChar *mode, const xmlChar *modeURI) {
   2031     xsltCompMatchPtr pat, list, next;
   2032     /*
   2033      * 'top' will point to style->xxxMatch ptr - declaring as 'void'
   2034      *  avoids gcc 'type-punned pointer' warning.
   2035      */
   2036     void **top = NULL;
   2037     const xmlChar *name = NULL;
   2038     float priority;              /* the priority */
   2039 
   2040     if ((style == NULL) || (cur == NULL) || (cur->match == NULL))
   2041 	return(-1);
   2042 
   2043     priority = cur->priority;
   2044     pat = xsltCompilePatternInternal(cur->match, style->doc, cur->elem,
   2045 		    style, NULL, 1);
   2046     if (pat == NULL)
   2047     	return(-1);
   2048     while (pat) {
   2049 	next = pat->next;
   2050 	pat->next = NULL;
   2051 	name = NULL;
   2052 
   2053 	pat->template = cur;
   2054 	if (mode != NULL)
   2055 	    pat->mode = xmlDictLookup(style->dict, mode, -1);
   2056 	if (modeURI != NULL)
   2057 	    pat->modeURI = xmlDictLookup(style->dict, modeURI, -1);
   2058 	if (priority != XSLT_PAT_NO_PRIORITY)
   2059 	    pat->priority = priority;
   2060 
   2061 	/*
   2062 	 * insert it in the hash table list corresponding to its lookup name
   2063 	 */
   2064 	switch (pat->steps[0].op) {
   2065         case XSLT_OP_ATTR:
   2066 	    if (pat->steps[0].value != NULL)
   2067 		name = pat->steps[0].value;
   2068 	    else
   2069 		top = &(style->attrMatch);
   2070 	    break;
   2071         case XSLT_OP_PARENT:
   2072         case XSLT_OP_ANCESTOR:
   2073 	    top = &(style->elemMatch);
   2074 	    break;
   2075         case XSLT_OP_ROOT:
   2076 	    top = &(style->rootMatch);
   2077 	    break;
   2078         case XSLT_OP_KEY:
   2079 	    top = &(style->keyMatch);
   2080 	    break;
   2081         case XSLT_OP_ID:
   2082 	    /* TODO optimize ID !!! */
   2083         case XSLT_OP_NS:
   2084         case XSLT_OP_ALL:
   2085 	    top = &(style->elemMatch);
   2086 	    break;
   2087         case XSLT_OP_END:
   2088 	case XSLT_OP_PREDICATE:
   2089 	    xsltTransformError(NULL, style, NULL,
   2090 			     "xsltAddTemplate: invalid compiled pattern\n");
   2091 	    xsltFreeCompMatch(pat);
   2092 	    return(-1);
   2093 	    /*
   2094 	     * TODO: some flags at the top level about type based patterns
   2095 	     *       would be faster than inclusion in the hash table.
   2096 	     */
   2097 	case XSLT_OP_PI:
   2098 	    if (pat->steps[0].value != NULL)
   2099 		name = pat->steps[0].value;
   2100 	    else
   2101 		top = &(style->piMatch);
   2102 	    break;
   2103 	case XSLT_OP_COMMENT:
   2104 	    top = &(style->commentMatch);
   2105 	    break;
   2106 	case XSLT_OP_TEXT:
   2107 	    top = &(style->textMatch);
   2108 	    break;
   2109         case XSLT_OP_ELEM:
   2110 	case XSLT_OP_NODE:
   2111 	    if (pat->steps[0].value != NULL)
   2112 		name = pat->steps[0].value;
   2113 	    else
   2114 		top = &(style->elemMatch);
   2115 	    break;
   2116 	}
   2117 	if (name != NULL) {
   2118 	    if (style->templatesHash == NULL) {
   2119 		style->templatesHash = xmlHashCreate(1024);
   2120 		if (style->templatesHash == NULL) {
   2121 		    xsltFreeCompMatch(pat);
   2122 		    return(-1);
   2123 		}
   2124 		xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat);
   2125 	    } else {
   2126 		list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
   2127 							 name, mode, modeURI);
   2128 		if (list == NULL) {
   2129 		    xmlHashAddEntry3(style->templatesHash, name,
   2130 				     mode, modeURI, pat);
   2131 		} else {
   2132 		    /*
   2133 		     * Note '<=' since one must choose among the matching
   2134 		     * template rules that are left, the one that occurs
   2135 		     * last in the stylesheet
   2136 		     */
   2137 		    if (list->priority <= pat->priority) {
   2138 			pat->next = list;
   2139 			xmlHashUpdateEntry3(style->templatesHash, name,
   2140 					    mode, modeURI, pat, NULL);
   2141 		    } else {
   2142 			while (list->next != NULL) {
   2143 			    if (list->next->priority <= pat->priority)
   2144 				break;
   2145 			    list = list->next;
   2146 			}
   2147 			pat->next = list->next;
   2148 			list->next = pat;
   2149 		    }
   2150 		}
   2151 	    }
   2152 	} else if (top != NULL) {
   2153 	    list = *top;
   2154 	    if (list == NULL) {
   2155 		*top = pat;
   2156 		pat->next = NULL;
   2157 	    } else if (list->priority <= pat->priority) {
   2158 		pat->next = list;
   2159 		*top = pat;
   2160 	    } else {
   2161 		while (list->next != NULL) {
   2162 		    if (list->next->priority <= pat->priority)
   2163 			break;
   2164 		    list = list->next;
   2165 		}
   2166 		pat->next = list->next;
   2167 		list->next = pat;
   2168 	    }
   2169 	} else {
   2170 	    xsltTransformError(NULL, style, NULL,
   2171 			     "xsltAddTemplate: invalid compiled pattern\n");
   2172 	    xsltFreeCompMatch(pat);
   2173 	    return(-1);
   2174 	}
   2175 #ifdef WITH_XSLT_DEBUG_PATTERN
   2176 	if (mode)
   2177 	    xsltGenericDebug(xsltGenericDebugContext,
   2178 			 "added pattern : '%s' mode '%s' priority %f\n",
   2179 			     pat->pattern, pat->mode, pat->priority);
   2180 	else
   2181 	    xsltGenericDebug(xsltGenericDebugContext,
   2182 			 "added pattern : '%s' priority %f\n",
   2183 			     pat->pattern, pat->priority);
   2184 #endif
   2185 
   2186 	pat = next;
   2187     }
   2188     return(0);
   2189 }
   2190 
   2191 static int
   2192 xsltComputeAllKeys(xsltTransformContextPtr ctxt, xmlNodePtr contextNode)
   2193 {
   2194     if ((ctxt == NULL) || (contextNode == NULL)) {
   2195 	xsltTransformError(ctxt, NULL, ctxt->inst,
   2196 	    "Internal error in xsltComputeAllKeys(): "
   2197 	    "Bad arguments.\n");
   2198 	return(-1);
   2199     }
   2200 
   2201     if (ctxt->document == NULL) {
   2202 	/*
   2203 	* The document info will only be NULL if we have a RTF.
   2204 	*/
   2205 	if (contextNode->doc->_private != NULL)
   2206 	    goto doc_info_mismatch;
   2207 	/*
   2208 	* On-demand creation of the document info (needed for keys).
   2209 	*/
   2210 	ctxt->document = xsltNewDocument(ctxt, contextNode->doc);
   2211 	if (ctxt->document == NULL)
   2212 	    return(-1);
   2213     }
   2214     return xsltInitAllDocKeys(ctxt);
   2215 
   2216 doc_info_mismatch:
   2217     xsltTransformError(ctxt, NULL, ctxt->inst,
   2218 	"Internal error in xsltComputeAllKeys(): "
   2219 	"The context's document info doesn't match the "
   2220 	"document info of the current result tree.\n");
   2221     ctxt->state = XSLT_STATE_STOPPED;
   2222     return(-1);
   2223 }
   2224 
   2225 /**
   2226  * xsltGetTemplate:
   2227  * @ctxt:  a XSLT process context
   2228  * @node:  the node being processed
   2229  * @style:  the current style
   2230  *
   2231  * Finds the template applying to this node, if @style is non-NULL
   2232  * it means one needs to look for the next imported template in scope.
   2233  *
   2234  * Returns the xsltTemplatePtr or NULL if not found
   2235  */
   2236 xsltTemplatePtr
   2237 xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
   2238 	        xsltStylesheetPtr style)
   2239 {
   2240     xsltStylesheetPtr curstyle;
   2241     xsltTemplatePtr ret = NULL;
   2242     const xmlChar *name = NULL;
   2243     xsltCompMatchPtr list = NULL;
   2244     float priority;
   2245     int keyed = 0;
   2246 
   2247     if ((ctxt == NULL) || (node == NULL))
   2248 	return(NULL);
   2249 
   2250     if (style == NULL) {
   2251 	curstyle = ctxt->style;
   2252     } else {
   2253 	curstyle = xsltNextImport(style);
   2254     }
   2255 
   2256     while ((curstyle != NULL) && (curstyle != style)) {
   2257 	priority = XSLT_PAT_NO_PRIORITY;
   2258 	/* TODO : handle IDs/keys here ! */
   2259 	if (curstyle->templatesHash != NULL) {
   2260 	    /*
   2261 	     * Use the top name as selector
   2262 	     */
   2263 	    switch (node->type) {
   2264 		case XML_ELEMENT_NODE:
   2265 		    if (node->name[0] == ' ')
   2266 			break;
   2267 		case XML_ATTRIBUTE_NODE:
   2268 		case XML_PI_NODE:
   2269 		    name = node->name;
   2270 		    break;
   2271 		case XML_DOCUMENT_NODE:
   2272 		case XML_HTML_DOCUMENT_NODE:
   2273 		case XML_TEXT_NODE:
   2274 		case XML_CDATA_SECTION_NODE:
   2275 		case XML_COMMENT_NODE:
   2276 		case XML_ENTITY_REF_NODE:
   2277 		case XML_ENTITY_NODE:
   2278 		case XML_DOCUMENT_TYPE_NODE:
   2279 		case XML_DOCUMENT_FRAG_NODE:
   2280 		case XML_NOTATION_NODE:
   2281 		case XML_DTD_NODE:
   2282 		case XML_ELEMENT_DECL:
   2283 		case XML_ATTRIBUTE_DECL:
   2284 		case XML_ENTITY_DECL:
   2285 		case XML_NAMESPACE_DECL:
   2286 		case XML_XINCLUDE_START:
   2287 		case XML_XINCLUDE_END:
   2288 		    break;
   2289 		default:
   2290 		    return(NULL);
   2291 
   2292 	    }
   2293 	}
   2294 	if (name != NULL) {
   2295 	    /*
   2296 	     * find the list of applicable expressions based on the name
   2297 	     */
   2298 	    list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash,
   2299 					     name, ctxt->mode, ctxt->modeURI);
   2300 	} else
   2301 	    list = NULL;
   2302 	while (list != NULL) {
   2303 	    if (xsltTestCompMatch(ctxt, list, node,
   2304 			          ctxt->mode, ctxt->modeURI)) {
   2305 		ret = list->template;
   2306 		priority = list->priority;
   2307 		break;
   2308 	    }
   2309 	    list = list->next;
   2310 	}
   2311 	list = NULL;
   2312 
   2313 	/*
   2314 	 * find alternate generic matches
   2315 	 */
   2316 	switch (node->type) {
   2317 	    case XML_ELEMENT_NODE:
   2318 		if (node->name[0] == ' ')
   2319 		    list = curstyle->rootMatch;
   2320 		else
   2321 		    list = curstyle->elemMatch;
   2322 		if (node->psvi != NULL) keyed = 1;
   2323 		break;
   2324 	    case XML_ATTRIBUTE_NODE: {
   2325 	        xmlAttrPtr attr;
   2326 
   2327 		list = curstyle->attrMatch;
   2328 		attr = (xmlAttrPtr) node;
   2329 		if (attr->psvi != NULL) keyed = 1;
   2330 		break;
   2331 	    }
   2332 	    case XML_PI_NODE:
   2333 		list = curstyle->piMatch;
   2334 		if (node->psvi != NULL) keyed = 1;
   2335 		break;
   2336 	    case XML_DOCUMENT_NODE:
   2337 	    case XML_HTML_DOCUMENT_NODE: {
   2338 	        xmlDocPtr doc;
   2339 
   2340 		list = curstyle->rootMatch;
   2341 		doc = (xmlDocPtr) node;
   2342 		if (doc->psvi != NULL) keyed = 1;
   2343 		break;
   2344 	    }
   2345 	    case XML_TEXT_NODE:
   2346 	    case XML_CDATA_SECTION_NODE:
   2347 		list = curstyle->textMatch;
   2348 		if (node->psvi != NULL) keyed = 1;
   2349 		break;
   2350 	    case XML_COMMENT_NODE:
   2351 		list = curstyle->commentMatch;
   2352 		if (node->psvi != NULL) keyed = 1;
   2353 		break;
   2354 	    case XML_ENTITY_REF_NODE:
   2355 	    case XML_ENTITY_NODE:
   2356 	    case XML_DOCUMENT_TYPE_NODE:
   2357 	    case XML_DOCUMENT_FRAG_NODE:
   2358 	    case XML_NOTATION_NODE:
   2359 	    case XML_DTD_NODE:
   2360 	    case XML_ELEMENT_DECL:
   2361 	    case XML_ATTRIBUTE_DECL:
   2362 	    case XML_ENTITY_DECL:
   2363 	    case XML_NAMESPACE_DECL:
   2364 	    case XML_XINCLUDE_START:
   2365 	    case XML_XINCLUDE_END:
   2366 		break;
   2367 	    default:
   2368 		break;
   2369 	}
   2370 	while ((list != NULL) &&
   2371 	       ((ret == NULL)  || (list->priority > priority))) {
   2372 	    if (xsltTestCompMatch(ctxt, list, node,
   2373 			          ctxt->mode, ctxt->modeURI)) {
   2374 		ret = list->template;
   2375 		priority = list->priority;
   2376 		break;
   2377 	    }
   2378 	    list = list->next;
   2379 	}
   2380 	/*
   2381 	 * Some of the tests for elements can also apply to documents
   2382 	 */
   2383 	if ((node->type == XML_DOCUMENT_NODE) ||
   2384 	    (node->type == XML_HTML_DOCUMENT_NODE) ||
   2385 	    (node->type == XML_TEXT_NODE)) {
   2386 	    list = curstyle->elemMatch;
   2387 	    while ((list != NULL) &&
   2388 		   ((ret == NULL)  || (list->priority > priority))) {
   2389 		if (xsltTestCompMatch(ctxt, list, node,
   2390 				      ctxt->mode, ctxt->modeURI)) {
   2391 		    ret = list->template;
   2392 		    priority = list->priority;
   2393 		    break;
   2394 		}
   2395 		list = list->next;
   2396 	    }
   2397 	} else if ((node->type == XML_PI_NODE) ||
   2398 		   (node->type == XML_COMMENT_NODE)) {
   2399 	    list = curstyle->elemMatch;
   2400 	    while ((list != NULL) &&
   2401 		   ((ret == NULL)  || (list->priority > priority))) {
   2402 		if (xsltTestCompMatch(ctxt, list, node,
   2403 				      ctxt->mode, ctxt->modeURI)) {
   2404 		    ret = list->template;
   2405 		    priority = list->priority;
   2406 		    break;
   2407 		}
   2408 		list = list->next;
   2409 	    }
   2410 	}
   2411 
   2412 keyed_match:
   2413 	if (keyed) {
   2414 	    list = curstyle->keyMatch;
   2415 	    while ((list != NULL) &&
   2416 		   ((ret == NULL)  || (list->priority > priority))) {
   2417 		if (xsltTestCompMatch(ctxt, list, node,
   2418 				      ctxt->mode, ctxt->modeURI)) {
   2419 		    ret = list->template;
   2420 		    priority = list->priority;
   2421 		    break;
   2422 		}
   2423 		list = list->next;
   2424 	    }
   2425 	}
   2426 	else if (ctxt->hasTemplKeyPatterns &&
   2427 	    ((ctxt->document == NULL) ||
   2428 	     (ctxt->document->nbKeysComputed < ctxt->nbKeys)))
   2429 	{
   2430 	    /*
   2431 	    * Compute all remaining keys for this document.
   2432 	    *
   2433 	    * REVISIT TODO: I think this could be further optimized.
   2434 	    */
   2435 	    if (xsltComputeAllKeys(ctxt, node) == -1)
   2436 		goto error;
   2437 
   2438 	    switch (node->type) {
   2439 		case XML_ELEMENT_NODE:
   2440 		    if (node->psvi != NULL) keyed = 1;
   2441 		    break;
   2442 		case XML_ATTRIBUTE_NODE:
   2443 		    if (((xmlAttrPtr) node)->psvi != NULL) keyed = 1;
   2444 		    break;
   2445 		case XML_TEXT_NODE:
   2446 		case XML_CDATA_SECTION_NODE:
   2447 		case XML_COMMENT_NODE:
   2448 		case XML_PI_NODE:
   2449 		    if (node->psvi != NULL) keyed = 1;
   2450 		    break;
   2451 		case XML_DOCUMENT_NODE:
   2452 		case XML_HTML_DOCUMENT_NODE:
   2453 		    if (((xmlDocPtr) node)->psvi != NULL) keyed = 1;
   2454 		    break;
   2455 		default:
   2456 		    break;
   2457 	    }
   2458 	    if (keyed)
   2459 		goto keyed_match;
   2460 	}
   2461 	if (ret != NULL)
   2462 	    return(ret);
   2463 
   2464 	/*
   2465 	 * Cycle on next curstylesheet import.
   2466 	 */
   2467 	curstyle = xsltNextImport(curstyle);
   2468     }
   2469 
   2470 error:
   2471     return(NULL);
   2472 }
   2473 
   2474 /**
   2475  * xsltCleanupTemplates:
   2476  * @style: an XSLT stylesheet
   2477  *
   2478  * Cleanup the state of the templates used by the stylesheet and
   2479  * the ones it imports.
   2480  */
   2481 void
   2482 xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) {
   2483 }
   2484 
   2485 /**
   2486  * xsltFreeTemplateHashes:
   2487  * @style: an XSLT stylesheet
   2488  *
   2489  * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
   2490  */
   2491 void
   2492 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
   2493     if (style->templatesHash != NULL)
   2494 	xmlHashFree((xmlHashTablePtr) style->templatesHash,
   2495 		    (xmlHashDeallocator) xsltFreeCompMatchList);
   2496     if (style->rootMatch != NULL)
   2497         xsltFreeCompMatchList(style->rootMatch);
   2498     if (style->keyMatch != NULL)
   2499         xsltFreeCompMatchList(style->keyMatch);
   2500     if (style->elemMatch != NULL)
   2501         xsltFreeCompMatchList(style->elemMatch);
   2502     if (style->attrMatch != NULL)
   2503         xsltFreeCompMatchList(style->attrMatch);
   2504     if (style->parentMatch != NULL)
   2505         xsltFreeCompMatchList(style->parentMatch);
   2506     if (style->textMatch != NULL)
   2507         xsltFreeCompMatchList(style->textMatch);
   2508     if (style->piMatch != NULL)
   2509         xsltFreeCompMatchList(style->piMatch);
   2510     if (style->commentMatch != NULL)
   2511         xsltFreeCompMatchList(style->commentMatch);
   2512 }
   2513 
   2514