Skip to content
Snippets Groups Projects
pattern.c 61.7 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * pattern.c: Implementation of selectors for nodes
    
     *
     * Reference:
     *   http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
    
     *   to some extent
    
     *   http://www.w3.org/TR/1999/REC-xml-19991116
     *
     * See Copyright for the status of this software.
     *
     * daniel@veillard.com
     */
    
    
    /*
     * TODO:
     * - compilation flags to check for specific syntaxes
     *   using flags of xmlPatterncompile()
     * - making clear how pattern starting with / or . need to be handled,
     *   currently push(NULL, NULL) means a reset of the streaming context
     *   and indicating we are on / (the document node), probably need
     *   something similar for .
    
     * - get rid of the "compile" starting with lowercase
    
     * - DONE (2006-05-16): get rid of the Strdup/Strndup in case of dictionary
    
    #define IN_LIBXML
    #include "libxml.h"
    
    #include <string.h>
    #include <libxml/xmlmemory.h>
    #include <libxml/tree.h>
    #include <libxml/hash.h>
    #include <libxml/dict.h>
    #include <libxml/xmlerror.h>
    #include <libxml/parserInternals.h>
    #include <libxml/pattern.h>
    
    
    #ifdef ERROR
    #undef ERROR
    #endif
    
    #define ERROR(a, b, c, d)
    #define ERROR5(a, b, c, d, e)
    
    
    #define XML_STREAM_STEP_DESC	1
    #define XML_STREAM_STEP_FINAL	2
    #define XML_STREAM_STEP_ROOT	4
    
    * NOTE: Those private flags (XML_STREAM_xxx) are used
    *   in _xmlStreamCtxt->flag. They extend the public
    
    *   xmlPatternFlags, so be careful not to interfere with the
    
    *   reserved values for xmlPatternFlags.
    
    #define XML_STREAM_FINAL_IS_ANY_NODE 1<<14
    #define XML_STREAM_FROM_ROOT 1<<15
    
    /*
    * XML_STREAM_ANY_NODE is used for comparison against
    * xmlElementType enums, to indicate a node of any type.
    */
    #define XML_STREAM_ANY_NODE 100
    
    
    #define XML_PATTERN_NOTPATTERN  (XML_PATTERN_XPATH | \
    				 XML_PATTERN_XSSEL | \
    				 XML_PATTERN_XSFIELD)
    
    #define XML_STREAM_XS_IDC(c) ((c)->flags & \
    
        (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
    
    #define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL)
    
    #define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD)
    
    
    #define XML_PAT_COPY_NSNAME(c, r, nsname) \
        if ((c)->comp->dict) \
    	r = (xmlChar *) xmlDictLookup((c)->comp->dict, BAD_CAST nsname, -1); \
        else r = xmlStrdup(BAD_CAST nsname);
    
    #define XML_PAT_FREE_STRING(c, r) if ((c)->comp->dict == NULL) xmlFree(r);
    
    
    typedef struct _xmlStreamStep xmlStreamStep;
    typedef xmlStreamStep *xmlStreamStepPtr;
    struct _xmlStreamStep {
        int flags;			/* properties of that step */
        const xmlChar *name;	/* first string value if NULL accept all */
        const xmlChar *ns;		/* second string value */
    
    };
    
    typedef struct _xmlStreamComp xmlStreamComp;
    typedef xmlStreamComp *xmlStreamCompPtr;
    struct _xmlStreamComp {
    
        xmlDict *dict;		/* the dictionary if any */
    
        int nbStep;			/* number of steps in the automata */
        int maxStep;		/* allocated number of steps */
        xmlStreamStepPtr steps;	/* the array of steps */
    
        struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
    
        xmlStreamCompPtr comp;	/* the compiled stream */
    
        int nbState;		/* number of states in the automata */
        int maxState;		/* allocated number of states */
    
        int level;			/* how deep are we ? */
        int *states;		/* the array of step indexes */
    
    };
    
    static void xmlFreeStreamComp(xmlStreamCompPtr comp);
    
    
    /*
     * Types are private:
     */
    
    typedef enum {
        XML_OP_END=0,
        XML_OP_ROOT,
        XML_OP_ELEM,
        XML_OP_CHILD,
        XML_OP_ATTR,
        XML_OP_PARENT,
        XML_OP_ANCESTOR,
        XML_OP_NS,
        XML_OP_ALL
    } xmlPatOp;
    
    
    
    typedef struct _xmlStepState xmlStepState;
    typedef xmlStepState *xmlStepStatePtr;
    struct _xmlStepState {
        int step;
        xmlNodePtr node;
    };
    
    typedef struct _xmlStepStates xmlStepStates;
    typedef xmlStepStates *xmlStepStatesPtr;
    struct _xmlStepStates {
        int nbstates;
        int maxstates;
        xmlStepStatePtr states;
    };
    
    
    typedef struct _xmlStepOp xmlStepOp;
    typedef xmlStepOp *xmlStepOpPtr;
    struct _xmlStepOp {
        xmlPatOp op;
        const xmlChar *value;
    
        const xmlChar *value2; /* The namespace name */
    
    #define PAT_FROM_ROOT	(1<<8)
    #define PAT_FROM_CUR	(1<<9)
    
        void *data;		/* the associated template */
    
        xmlDictPtr dict;		/* the optional dictionary */
    
        struct _xmlPattern *next;	/* next pattern if | is used */
    
        const xmlChar *pattern;	/* the pattern */
    
        xmlStepOpPtr steps;        /* ops for computation */
    
        xmlStreamCompPtr stream;	/* the streaming data if any */
    
    };
    
    typedef struct _xmlPatParserContext xmlPatParserContext;
    typedef xmlPatParserContext *xmlPatParserContextPtr;
    struct _xmlPatParserContext {
        const xmlChar *cur;			/* the current char being parsed */
        const xmlChar *base;		/* the full expression */
        int	           error;		/* error code */
    
        xmlDictPtr     dict;		/* the dictionary if any */
    
        xmlPatternPtr  comp;		/* the result */
    
        xmlNodePtr     elem;		/* the current node if any */
    
        const xmlChar **namespaces;		/* the namespaces definitions */
        int   nb_namespaces;		/* the number of namespaces */
    
    };
    
    /************************************************************************
    
     *									*
     *			Type functions					*
     *									*
    
     ************************************************************************/
    
    /**
     * xmlNewPattern:
     *
     * Create a new XSLT Pattern
     *
     * Returns the newly allocated xmlPatternPtr or NULL in case of error
     */
    static xmlPatternPtr
    xmlNewPattern(void) {
        xmlPatternPtr cur;
    
        cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
        if (cur == NULL) {
    	ERROR(NULL, NULL, NULL,
    		"xmlNewPattern : malloc failed\n");
    	return(NULL);
        }
        memset(cur, 0, sizeof(xmlPattern));
        cur->maxStep = 10;
    
        cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
        if (cur->steps == NULL) {
            xmlFree(cur);
    	ERROR(NULL, NULL, NULL,
    		"xmlNewPattern : malloc failed\n");
    	return(NULL);
        }
    
        return(cur);
    }
    
    /**
     * xmlFreePattern:
     * @comp:  an XSLT comp
     *
     * Free up the memory allocated by @comp
     */
    void
    xmlFreePattern(xmlPatternPtr comp) {
    
        xmlFreePatternList(comp);
    }
    
    static void
    xmlFreePatternInternal(xmlPatternPtr comp) {
    
        if (comp->stream != NULL)
            xmlFreeStreamComp(comp->stream);
    
        if (comp->pattern != NULL)
    	xmlFree((xmlChar *)comp->pattern);
    
            if (comp->dict == NULL) {
    	    for (i = 0;i < comp->nbStep;i++) {
    		op = &comp->steps[i];
    		if (op->value != NULL)
    		    xmlFree((xmlChar *) op->value);
    		if (op->value2 != NULL)
    		    xmlFree((xmlChar *) op->value2);
    	    }
    
        if (comp->dict != NULL)
            xmlDictFree(comp->dict);
    
    
        memset(comp, -1, sizeof(xmlPattern));
        xmlFree(comp);
    }
    
    /**
     * xmlFreePatternList:
     * @comp:  an XSLT comp list
     *
     * Free up the memory allocated by all the elements of @comp
     */
    void
    xmlFreePatternList(xmlPatternPtr comp) {
        xmlPatternPtr cur;
    
        while (comp != NULL) {
    	cur = comp;
    	comp = comp->next;
    
    	xmlFreePatternInternal(cur);
    
        }
    }
    
    /**
     * xmlNewPatParserContext:
     * @pattern:  the pattern context
    
     * @dict:  the inherited dictionary or NULL
    
     * @namespaces: the prefix definitions, array of [URI, prefix] terminated
     *              with [NULL, NULL] or NULL if no namespace is used
    
     *
     * Create a new XML pattern parser context
     *
     * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
     */
    static xmlPatParserContextPtr
    
    xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
                           const xmlChar **namespaces) {
    
        xmlPatParserContextPtr cur;
    
        if (pattern == NULL)
            return(NULL);
    
        cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
        if (cur == NULL) {
    	ERROR(NULL, NULL, NULL,
    		"xmlNewPatParserContext : malloc failed\n");
    	return(NULL);
        }
        memset(cur, 0, sizeof(xmlPatParserContext));
        cur->dict = dict;
        cur->cur = pattern;
        cur->base = pattern;
    
            for (i = 0;namespaces[2 * i] != NULL;i++)
                ;
    
            cur->nb_namespaces = i;
        } else {
            cur->nb_namespaces = 0;
        }
        cur->namespaces = namespaces;
    
        return(cur);
    }
    
    /**
     * xmlFreePatParserContext:
     * @ctxt:  an XSLT parser context
     *
     * Free up the memory allocated by @ctxt
     */
    static void
    xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
        if (ctxt == NULL)
    
    	return;
    
        memset(ctxt, -1, sizeof(xmlPatParserContext));
        xmlFree(ctxt);
    }
    
    /**
     * xmlPatternAdd:
     * @comp:  the compiled match expression
     * @op:  an op
     * @value:  the first value
     * @value2:  the second value
     *
    
     * Add a step to an XSLT Compiled Match
    
     *
     * Returns -1 in case of failure, 0 otherwise.
     */
    static int
    xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
                    xmlPatternPtr comp,
                    xmlPatOp op, xmlChar * value, xmlChar * value2)
    {
    
        if (comp->nbStep >= comp->maxStep) {
            xmlStepOpPtr temp;
    	temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
    	                                 sizeof(xmlStepOp));
            if (temp == NULL) {
    	    ERROR(ctxt, NULL, NULL,
    			     "xmlPatternAdd: realloc failed\n");
    	    return (-1);
    	}
    	comp->steps = temp;
    	comp->maxStep *= 2;
    
        }
        comp->steps[comp->nbStep].op = op;
        comp->steps[comp->nbStep].value = value;
        comp->steps[comp->nbStep].value2 = value2;
        comp->nbStep++;
        return (0);
    }
    
    #if 0
    /**
     * xsltSwapTopPattern:
     * @comp:  the compiled match expression
     *
     * reverse the two top steps.
     */
    static void
    xsltSwapTopPattern(xmlPatternPtr comp) {
        int i;
        int j = comp->nbStep - 1;
    
        if (j > 0) {
    	register const xmlChar *tmp;
    	register xmlPatOp op;
    	i = j - 1;
    	tmp = comp->steps[i].value;
    	comp->steps[i].value = comp->steps[j].value;
    	comp->steps[j].value = tmp;
    	tmp = comp->steps[i].value2;
    	comp->steps[i].value2 = comp->steps[j].value2;
    	comp->steps[j].value2 = tmp;
    	op = comp->steps[i].op;
    	comp->steps[i].op = comp->steps[j].op;
    	comp->steps[j].op = op;
        }
    }
    #endif
    
    /**
     * xmlReversePattern:
     * @comp:  the compiled match expression
     *
     * reverse all the stack of expressions
    
     *
     * returns 0 in case of success and -1 in case of error.
    
    xmlReversePattern(xmlPatternPtr comp) {
    
        /*
         * remove the leading // for //a or .//a
         */
        if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
            for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
    	    comp->steps[i].value = comp->steps[j].value;
    	    comp->steps[i].value2 = comp->steps[j].value2;
    	    comp->steps[i].op = comp->steps[j].op;
    	}
    	comp->nbStep--;
        }
    
        if (comp->nbStep >= comp->maxStep) {
            xmlStepOpPtr temp;
    	temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
    	                                 sizeof(xmlStepOp));
            if (temp == NULL) {
    	    ERROR(ctxt, NULL, NULL,
    			     "xmlReversePattern: realloc failed\n");
    	    return (-1);
    	}
    	comp->steps = temp;
    	comp->maxStep *= 2;
        }
    
        while (j > i) {
    	register const xmlChar *tmp;
    	register xmlPatOp op;
    	tmp = comp->steps[i].value;
    	comp->steps[i].value = comp->steps[j].value;
    	comp->steps[j].value = tmp;
    	tmp = comp->steps[i].value2;
    	comp->steps[i].value2 = comp->steps[j].value2;
    	comp->steps[j].value2 = tmp;
    	op = comp->steps[i].op;
    	comp->steps[i].op = comp->steps[j].op;
    	comp->steps[j].op = op;
    	j--;
    	i++;
        }
    
        comp->steps[comp->nbStep].value = NULL;
        comp->steps[comp->nbStep].value2 = NULL;
    
        comp->steps[comp->nbStep++].op = XML_OP_END;
    
    }
    
    /************************************************************************
    
     *									*
     *		The interpreter for the precompiled patterns		*
     *									*
    
     ************************************************************************/
    
    
    static int
    xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
        if ((states->states == NULL) || (states->maxstates <= 0)) {
            states->maxstates = 4;
    	states->nbstates = 0;
    	states->states = xmlMalloc(4 * sizeof(xmlStepState));
        }
        else if (states->maxstates <= states->nbstates) {
            xmlStepState *tmp;
    
    	tmp = (xmlStepStatePtr) xmlRealloc(states->states,
    			       2 * states->maxstates * sizeof(xmlStepState));
    	if (tmp == NULL)
    	    return(-1);
    	states->states = tmp;
    	states->maxstates *= 2;
        }
        states->states[states->nbstates].step = step;
        states->states[states->nbstates++].node = node;
    #if 0
        fprintf(stderr, "Push: %d, %s\n", step, node->name);
    #endif
        return(0);
    }
    
    
    /**
     * xmlPatMatch:
     * @comp: the precompiled pattern
     * @node: a node
     *
    
     * Test whether the node matches the pattern
    
     *
     * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
     */
    static int
    xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
        int i;
        xmlStepOpPtr step;
    
        xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
    
    
        if ((comp == NULL) || (node == NULL)) return(-1);
    
    	step = &comp->steps[i];
    	switch (step->op) {
                case XML_OP_END:
    
    		if (node->type == XML_NAMESPACE_DECL)
    
    		if ((node->type == XML_DOCUMENT_NODE) ||
    		    (node->type == XML_HTML_DOCUMENT_NODE))
    		    continue;
    
                case XML_OP_ELEM:
    		if (node->type != XML_ELEMENT_NODE)
    
    		if (step->value == NULL)
    		    continue;
    		if (step->value[0] != node->name[0])
    
    		if (!xmlStrEqual(step->value, node->name))
    
    
    		/* Namespace test */
    		if (node->ns == NULL) {
    		    if (step->value2 != NULL)
    
    		} else if (node->ns->href != NULL) {
    		    if (step->value2 == NULL)
    
    		    if (!xmlStrEqual(step->value2, node->ns->href))
    
    		}
    		continue;
                case XML_OP_CHILD: {
    		xmlNodePtr lst;
    
    		if ((node->type != XML_ELEMENT_NODE) &&
    		    (node->type != XML_DOCUMENT_NODE) &&
    		    (node->type != XML_HTML_DOCUMENT_NODE))
    
    
    		lst = node->children;
    
    		if (step->value != NULL) {
    		    while (lst != NULL) {
    			if ((lst->type == XML_ELEMENT_NODE) &&
    			    (step->value[0] == lst->name[0]) &&
    			    (xmlStrEqual(step->value, lst->name)))
    			    break;
    			lst = lst->next;
    		    }
    		    if (lst != NULL)
    			continue;
    		}
    
    	    }
                case XML_OP_ATTR:
    		if (node->type != XML_ATTRIBUTE_NODE)
    
    		if (step->value != NULL) {
    		    if (step->value[0] != node->name[0])
    
    		    if (!xmlStrEqual(step->value, node->name))
    
    		}
    		/* Namespace test */
    		if (node->ns == NULL) {
    		    if (step->value2 != NULL)
    
    		} else if (step->value2 != NULL) {
    		    if (!xmlStrEqual(step->value2, node->ns->href))
    
    		}
    		continue;
                case XML_OP_PARENT:
    		if ((node->type == XML_DOCUMENT_NODE) ||
    		    (node->type == XML_HTML_DOCUMENT_NODE) ||
    		    (node->type == XML_NAMESPACE_DECL))
    
    		if (step->value == NULL)
    		    continue;
    		if (step->value[0] != node->name[0])
    
    		if (!xmlStrEqual(step->value, node->name))
    
    		/* Namespace test */
    		if (node->ns == NULL) {
    		    if (step->value2 != NULL)
    
    		} else if (node->ns->href != NULL) {
    		    if (step->value2 == NULL)
    
    		    if (!xmlStrEqual(step->value2, node->ns->href))
    
    		}
    		continue;
                case XML_OP_ANCESTOR:
    		/* TODO: implement coalescing of ANCESTOR/NODE ops */
    		if (step->value == NULL) {
    		    i++;
    		    step = &comp->steps[i];
    		    if (step->op == XML_OP_ROOT)
    
    		    if (step->value == NULL)
    			return(-1);
    		}
    		if (node == NULL)
    
    		if ((node->type == XML_DOCUMENT_NODE) ||
    		    (node->type == XML_HTML_DOCUMENT_NODE) ||
    		    (node->type == XML_NAMESPACE_DECL))
    
    		node = node->parent;
    		while (node != NULL) {
    		    if ((node->type == XML_ELEMENT_NODE) &&
    			(step->value[0] == node->name[0]) &&
    			(xmlStrEqual(step->value, node->name))) {
    			/* Namespace test */
    			if (node->ns == NULL) {
    			    if (step->value2 == NULL)
    				break;
    			} else if (node->ns->href != NULL) {
    			    if ((step->value2 != NULL) &&
    			        (xmlStrEqual(step->value2, node->ns->href)))
    				break;
    			}
    		    }
    		    node = node->parent;
    		}
    		if (node == NULL)
    
    		    goto rollback;
    		/*
    		 * prepare a potential rollback from here
    		 * for ancestors of that node.
    		 */
    		if (step->op == XML_OP_ANCESTOR)
    		    xmlPatPushState(&states, i, node);
    		else
    		    xmlPatPushState(&states, i - 1, node);
    
    		continue;
                case XML_OP_NS:
    		if (node->type != XML_ELEMENT_NODE)
    
    		if (node->ns == NULL) {
    		    if (step->value != NULL)
    
    		} else if (node->ns->href != NULL) {
    		    if (step->value == NULL)
    
    		    if (!xmlStrEqual(step->value, node->ns->href))
    
    		}
    		break;
                case XML_OP_ALL:
    		if (node->type != XML_ELEMENT_NODE)
    
    found:
        if (states.states != NULL) {
            /* Free the rollback states */
    	xmlFree(states.states);
        }
    
    rollback:
        /* got an error try to rollback */
        if (states.states == NULL)
    	return(0);
        if (states.nbstates <= 0) {
    	xmlFree(states.states);
    	return(0);
        }
        states.nbstates--;
        i = states.states[states.nbstates].step;
        node = states.states[states.nbstates].node;
    #if 0
        fprintf(stderr, "Pop: %d, %s\n", i, node->name);
    #endif
        goto restart;
    
    }
    
    /************************************************************************
     *									*
     *			Dedicated parser for templates			*
     *									*
     ************************************************************************/
    
    
    #define TODO								\
    
        xmlGenericError(xmlGenericErrorContext,				\
    	    "Unimplemented block at %s:%d\n",				\
                __FILE__, __LINE__);
    #define CUR (*ctxt->cur)
    #define SKIP(val) ctxt->cur += (val)
    #define NXT(val) ctxt->cur[(val)]
    
    #define PEEKPREV(val) ctxt->cur[-(val)]
    
    #define SKIP_BLANKS							\
    
        while (IS_BLANK_CH(CUR)) NEXT
    
    
    #define CURRENT (*ctxt->cur)
    #define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
    
    
    
    #define PUSH(op, val, val2)						\
    
        if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
    
    #define XSLT_ERROR(X)							\
    
        { xsltError(ctxt, __FILE__, __LINE__, X);				\
    
          ctxt->error = (X); return; }
    
    #define XSLT_ERROR0(X)							\
    
        { xsltError(ctxt, __FILE__, __LINE__, X);				\
    
          ctxt->error = (X); return(0); }
    
    #if 0
    /**
     * xmlPatScanLiteral:
     * @ctxt:  the XPath Parser context
     *
    
     * Parse an XPath Literal:
    
     *
     * [29] Literal ::= '"' [^"]* '"'
     *                | "'" [^']* "'"
     *
     * Returns the Literal parsed or NULL
     */
    
    static xmlChar *
    xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
        const xmlChar *q, *cur;
        xmlChar *ret = NULL;
        int val, len;
    
        SKIP_BLANKS;
        if (CUR == '"') {
            NEXT;
    	cur = q = CUR_PTR;
    	val = xmlStringCurrentChar(NULL, cur, &len);
    	while ((IS_CHAR(val)) && (val != '"')) {
    	    cur += len;
    	    val = xmlStringCurrentChar(NULL, cur, &len);
    	}
    	if (!IS_CHAR(val)) {
    	    ctxt->error = 1;
    	    return(NULL);
    	} else {
    
    	    if (ctxt->dict)
    		ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
    	    else
    
    		ret = xmlStrndup(q, cur - q);
    
            }
    	cur += len;
    	CUR_PTR = cur;
        } else if (CUR == '\'') {
            NEXT;
    	cur = q = CUR_PTR;
    	val = xmlStringCurrentChar(NULL, cur, &len);
    	while ((IS_CHAR(val)) && (val != '\'')) {
    	    cur += len;
    	    val = xmlStringCurrentChar(NULL, cur, &len);
    	}
    	if (!IS_CHAR(val)) {
    	    ctxt->error = 1;
    	    return(NULL);
    	} else {
    
    	    if (ctxt->dict)
    		ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
    	    else
    
    		ret = xmlStrndup(q, cur - q);
    
            }
    	cur += len;
    	CUR_PTR = cur;
        } else {
    	/* XP_ERROR(XPATH_START_LITERAL_ERROR); */
    	ctxt->error = 1;
    	return(NULL);
        }
        return(ret);
    }
    #endif
    
    /**
     * xmlPatScanName:
     * @ctxt:  the XPath Parser context
     *
    
     * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
    
     *                  CombiningChar | Extender
     *
     * [5] Name ::= (Letter | '_' | ':') (NameChar)*
     *
     * [6] Names ::= Name (S Name)*
     *
     * Returns the Name parsed or NULL
     */
    
    static xmlChar *
    xmlPatScanName(xmlPatParserContextPtr ctxt) {
        const xmlChar *q, *cur;
        xmlChar *ret = NULL;
        int val, len;
    
        SKIP_BLANKS;
    
        cur = q = CUR_PTR;
        val = xmlStringCurrentChar(NULL, cur, &len);
        if (!IS_LETTER(val) && (val != '_') && (val != ':'))
    	return(NULL);
    
        while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
               (val == '.') || (val == '-') ||
    
    	   (val == '_') ||
    
    	   (IS_COMBINING(val)) ||
    	   (IS_EXTENDER(val))) {
    	cur += len;
    	val = xmlStringCurrentChar(NULL, cur, &len);
        }
    
        if (ctxt->dict)
    	ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
        else
    
    	ret = xmlStrndup(q, cur - q);
    
        CUR_PTR = cur;
        return(ret);
    }
    
    /**
     * xmlPatScanNCName:
     * @ctxt:  the XPath Parser context
     *
     * Parses a non qualified name
     *
     * Returns the Name parsed or NULL
     */
    
    static xmlChar *
    xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
        const xmlChar *q, *cur;
        xmlChar *ret = NULL;
        int val, len;
    
        SKIP_BLANKS;
    
        cur = q = CUR_PTR;
        val = xmlStringCurrentChar(NULL, cur, &len);
        if (!IS_LETTER(val) && (val != '_'))
    	return(NULL);
    
        while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
               (val == '.') || (val == '-') ||
    	   (val == '_') ||
    	   (IS_COMBINING(val)) ||
    	   (IS_EXTENDER(val))) {
    	cur += len;
    	val = xmlStringCurrentChar(NULL, cur, &len);
        }
    
        if (ctxt->dict)
    	ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
        else
    	ret = xmlStrndup(q, cur - q);
    
        CUR_PTR = cur;
        return(ret);
    }
    
    #if 0
    /**
     * xmlPatScanQName:
     * @ctxt:  the XPath Parser context
     * @prefix:  the place to store the prefix
     *
     * Parse a qualified name
     *
     * Returns the Name parsed or NULL
     */
    
    static xmlChar *
    xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
        xmlChar *ret = NULL;
    
        *prefix = NULL;
        ret = xmlPatScanNCName(ctxt);
        if (CUR == ':') {
            *prefix = ret;
    	NEXT;
    	ret = xmlPatScanNCName(ctxt);
        }
        return(ret);
    }
    #endif
    
    /**
    
     * xmlCompileAttributeTest:
     * @ctxt:  the compilation context
     *
     * Compile an attribute test.
     */
    static void
    xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
        xmlChar *token = NULL;
        xmlChar *name = NULL;
        xmlChar *URL = NULL;
    
        name = xmlPatScanNCName(ctxt);
        if (name == NULL) {
    	if (CUR == '*') {
    	    PUSH(XML_OP_ATTR, NULL, NULL);
    
    	} else {
    	    ERROR(NULL, NULL, NULL,
    		"xmlCompileAttributeTest : Name expected\n");
    	    ctxt->error = 1;
    	}
    	return;
        }
        if (CUR == ':') {
    	int i;
    	xmlChar *prefix = name;
    
    	if (IS_BLANK_CH(CUR)) {
    
    	    ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
    
    	    XML_PAT_FREE_STRING(ctxt, prefix);
    
    	/*
    	* This is a namespace match
    	*/
    	token = xmlPatScanName(ctxt);
    
    	if ((prefix[0] == 'x') &&
    	    (prefix[1] == 'm') &&
    	    (prefix[2] == 'l') &&
    
    	    XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
    
    	} else {
    	    for (i = 0;i < ctxt->nb_namespaces;i++) {
    		if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
    
    		    XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
    
    		    break;
    		}
    	    }
    	    if (i >= ctxt->nb_namespaces) {
    		ERROR5(NULL, NULL, NULL,
    		    "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
    		    prefix);
    
    	        XML_PAT_FREE_STRING(ctxt, prefix);
    
    		ctxt->error = 1;
    
    	XML_PAT_FREE_STRING(ctxt, prefix);
    
    	if (token == NULL) {
    	    if (CUR == '*') {
    		NEXT;
    		PUSH(XML_OP_ATTR, NULL, URL);
    	    } else {
    		ERROR(NULL, NULL, NULL,
    		    "xmlCompileAttributeTest : Name expected\n");
    		ctxt->error = 1;
    		goto error;
    
    	} else {
    	    PUSH(XML_OP_ATTR, token, URL);
    	}
        } else {
    	PUSH(XML_OP_ATTR, name, NULL);
        }
        return;
    error:
        if (URL != NULL)
    
    	XML_PAT_FREE_STRING(ctxt, URL)
    
     * xmlCompileStepPattern:
     * @ctxt:  the compilation context
     *
     * Compile the Step Pattern and generates a precompiled
     * form suitable for fast matching.
     *
     * [3]    Step    ::=    '.' | NameTest