 */
static int
xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
{
    int total = 0;
    int equal, ret;
    xmlXPathCompExprPtr comp;
    xmlXPathObjectPtr arg1, arg2;
    xmlNodePtr bak;
    xmlDocPtr bakd;
    int pp;
    int cs;

    CHECK_ERROR0;
    comp = ctxt->comp;
    switch (op->op) {
        case XPATH_OP_END:
            return (0);
        case XPATH_OP_AND:
	    bakd = ctxt->context->doc;
	    bak = ctxt->context->node;
	    pp = ctxt->context->proximityPosition;
	    cs = ctxt->context->contextSize;
            total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
	    CHECK_ERROR0;
            xmlXPathBooleanFunction(ctxt, 1);
            if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
                return (total);
            arg2 = valuePop(ctxt);
	    ctxt->context->doc = bakd;
	    ctxt->context->node = bak;
	    ctxt->context->proximityPosition = pp;
	    ctxt->context->contextSize = cs;
            total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
	    if (ctxt->error) {
		xmlXPathFreeObject(arg2);
		return(0);
	    }
            xmlXPathBooleanFunction(ctxt, 1);
            arg1 = valuePop(ctxt);
            arg1->boolval &= arg2->boolval;
            valuePush(ctxt, arg1);
	    xmlXPathReleaseObject(ctxt->context, arg2);
            return (total);
        case XPATH_OP_OR:
	    bakd = ctxt->context->doc;
	    bak = ctxt->context->node;
	    pp = ctxt->context->proximityPosition;
	    cs = ctxt->context->contextSize;
            total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
	    CHECK_ERROR0;
            xmlXPathBooleanFunction(ctxt, 1);
            if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
                return (total);
            arg2 = valuePop(ctxt);
	    ctxt->context->doc = bakd;
	    ctxt->context->node = bak;
	    ctxt->context->proximityPosition = pp;
	    ctxt->context->contextSize = cs;
            total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
	    if (ctxt->error) {
		xmlXPathFreeObject(arg2);
		return(0);
	    }
            xmlXPathBooleanFunction(ctxt, 1);
            arg1 = valuePop(ctxt);
            arg1->boolval |= arg2->boolval;
            valuePush(ctxt, arg1);
	    xmlXPathReleaseObject(ctxt->context, arg2);
            return (total);
        case XPATH_OP_EQUAL:
	    bakd = ctxt->context->doc;
	    bak = ctxt->context->node;
	    pp = ctxt->context->proximityPosition;
	    cs = ctxt->context->contextSize;
            total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
	    CHECK_ERROR0;
	    ctxt->context->doc = bakd;
	    ctxt->context->node = bak;
	    ctxt->context->proximityPosition = pp;
	    ctxt->context->contextSize = cs;
            total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
	    CHECK_ERROR0;
	    if (op->value)
		equal = xmlXPathEqualValues(ctxt);
	    else
		equal = xmlXPathNotEqualValues(ctxt);
	    valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, equal));
            return (total);
        case XPATH_OP_CMP:
	    bakd = ctxt->context->doc;
	    bak = ctxt->context->node;
	    pp = ctxt->context->proximityPosition;
	    cs = ctxt->context->contextSize;
            total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
	    CHECK_ERROR0;
	    ctxt->context->doc = bakd;
	    ctxt->context->node = bak;
	    ctxt->context->proximityPosition = pp;
	    ctxt->context->contextSize = cs;
            total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
	    CHECK_ERROR0;
            ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
	    valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
            return (total);
        case XPATH_OP_PLUS:
	    bakd = ctxt->context->doc;
	    bak = ctxt->context->node;
	    pp = ctxt->context->proximityPosition;
	    cs = ctxt->context->contextSize;
            total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
	    CHECK_ERROR0;
            if (op->ch2 != -1) {
		ctxt->context->doc = bakd;
		ctxt->context->node = bak;
		ctxt->context->proximityPosition = pp;
		ctxt->context->contextSize = cs;
                total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
	    }
	    CHECK_ERROR0;
            if (op->value == 0)
                xmlXPathSubValues(ctxt);
            else if (op->value == 1)
                xmlXPathAddValues(ctxt);
            else if (op->value == 2)
                xmlXPathValueFlipSign(ctxt);
            else if (op->value == 3) {
                CAST_TO_NUMBER;
                CHECK_TYPE0(XPATH_NUMBER);
            }
            return (total);
        case XPATH_OP_MULT:
	    bakd = ctxt->context->doc;
	    bak = ctxt->context->node;
	    pp = ctxt->context->proximityPosition;
	    cs = ctxt->context->contextSize;
            total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
	    CHECK_ERROR0;
	    ctxt->context->doc = bakd;
	    ctxt->context->node = bak;
	    ctxt->context->proximityPosition = pp;
	    ctxt->context->contextSize = cs;
            total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
	    CHECK_ERROR0;
            if (op->value == 0)
                xmlXPathMultValues(ctxt);
            else if (op->value == 1)
                xmlXPathDivValues(ctxt);
            else if (op->value == 2)
                xmlXPathModValues(ctxt);
            return (total);
        case XPATH_OP_UNION:
	    bakd = ctxt->context->doc;
	    bak = ctxt->context->node;
	    pp = ctxt->context->proximityPosition;
	    cs = ctxt->context->contextSize;
            total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
	    CHECK_ERROR0;
	    ctxt->context->doc = bakd;
	    ctxt->context->node = bak;
	    ctxt->context->proximityPosition = pp;
	    ctxt->context->contextSize = cs;
            total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
	    CHECK_ERROR0;
            CHECK_TYPE0(XPATH_NODESET);
            arg2 = valuePop(ctxt);

            CHECK_TYPE0(XPATH_NODESET);
            arg1 = valuePop(ctxt);

	    if ((arg1->nodesetval == NULL) ||
		((arg2->nodesetval != NULL) &&
		 (arg2->nodesetval->nodeNr != 0)))
	    {
		arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
							arg2->nodesetval);
	    }

            valuePush(ctxt, arg1);
	    xmlXPathReleaseObject(ctxt->context, arg2);
            return (total);
        case XPATH_OP_ROOT:
            xmlXPathRoot(ctxt);
            return (total);
        case XPATH_OP_NODE:
            if (op->ch1 != -1)
                total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
	    CHECK_ERROR0;
            if (op->ch2 != -1)
                total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
	    CHECK_ERROR0;
	    valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
		ctxt->context->node));
            return (total);
        case XPATH_OP_RESET:
            if (op->ch1 != -1)
                total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
	    CHECK_ERROR0;
            if (op->ch2 != -1)
                total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
	    CHECK_ERROR0;
            ctxt->context->node = NULL;
            return (total);
        case XPATH_OP_COLLECT:{
                if (op->ch1 == -1)
                    return (total);

                total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
		CHECK_ERROR0;

                total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL, 0);
                return (total);
            }
        case XPATH_OP_VALUE:
            valuePush(ctxt,
                      xmlXPathCacheObjectCopy(ctxt->context,
			(xmlXPathObjectPtr) op->value4));
            return (total);
        case XPATH_OP_VARIABLE:{
		xmlXPathObjectPtr val;

                if (op->ch1 != -1)
                    total +=
                        xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
                if (op->value5 == NULL) {
		    val = xmlXPathVariableLookup(ctxt->context, op->value4);
		    if (val == NULL) {
			ctxt->error = XPATH_UNDEF_VARIABLE_ERROR;
			return(0);
		    }
                    valuePush(ctxt, val);
		} else {
                    const xmlChar *URI;

                    URI = xmlXPathNsLookup(ctxt->context, op->value5);
                    if (URI == NULL) {
                        xmlGenericError(xmlGenericErrorContext,
            "xmlXPathCompOpEval: variable %s bound to undefined prefix %s\n",
                                    (char *) op->value4, (char *)op->value5);
                        ctxt->error = XPATH_UNDEF_PREFIX_ERROR;
                        return (total);
                    }
		    val = xmlXPathVariableLookupNS(ctxt->context,
                                                       op->value4, URI);
		    if (val == NULL) {
			ctxt->error = XPATH_UNDEF_VARIABLE_ERROR;
			return(0);
		    }
                    valuePush(ctxt, val);
                }
                return (total);
            }
        case XPATH_OP_FUNCTION:{
                xmlXPathFunction func;
                const xmlChar *oldFunc, *oldFuncURI;
		int i;
                int frame;

                frame = xmlXPathSetFrame(ctxt);
                if (op->ch1 != -1) {
                    total +=
                        xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
                    if (ctxt->error != XPATH_EXPRESSION_OK) {
                        xmlXPathPopFrame(ctxt, frame);
                        return (total);
                    }
                }
		if (ctxt->valueNr < ctxt->valueFrame + op->value) {
		    xmlGenericError(xmlGenericErrorContext,
			    "xmlXPathCompOpEval: parameter error\n");
		    ctxt->error = XPATH_INVALID_OPERAND;
                    xmlXPathPopFrame(ctxt, frame);
		    return (total);
		}
		for (i = 0; i < op->value; i++) {
		    if (ctxt->valueTab[(ctxt->valueNr - 1) - i] == NULL) {
			xmlGenericError(xmlGenericErrorContext,
				"xmlXPathCompOpEval: parameter error\n");
			ctxt->error = XPATH_INVALID_OPERAND;
                        xmlXPathPopFrame(ctxt, frame);
			return (total);
		    }
                }
                if (op->cache != NULL)
                    XML_CAST_FPTR(func) = op->cache;
                else {
                    const xmlChar *URI = NULL;

                    if (op->value5 == NULL)
                        func =
                            xmlXPathFunctionLookup(ctxt->context,
                                                   op->value4);
                    else {
                        URI = xmlXPathNsLookup(ctxt->context, op->value5);
                        if (URI == NULL) {
                            xmlGenericError(xmlGenericErrorContext,
            "xmlXPathCompOpEval: function %s bound to undefined prefix %s\n",
                                    (char *)op->value4, (char *)op->value5);
                            xmlXPathPopFrame(ctxt, frame);
                            ctxt->error = XPATH_UNDEF_PREFIX_ERROR;
                            return (total);
                        }
                        func = xmlXPathFunctionLookupNS(ctxt->context,
                                                        op->value4, URI);
                    }
                    if (func == NULL) {
                        xmlGenericError(xmlGenericErrorContext,
                                "xmlXPathCompOpEval: function %s not found\n",
                                        (char *)op->value4);
                        XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR);
                    }
                    op->cache = XML_CAST_FPTR(func);
                    op->cacheURI = (void *) URI;
                }
                oldFunc = ctxt->context->function;
                oldFuncURI = ctxt->context->functionURI;
                ctxt->context->function = op->value4;
                ctxt->context->functionURI = op->cacheURI;
                func(ctxt, op->value);
                ctxt->context->function = oldFunc;
                ctxt->context->functionURI = oldFuncURI;
                xmlXPathPopFrame(ctxt, frame);
                return (total);
            }
        case XPATH_OP_ARG:
	    bakd = ctxt->context->doc;
	    bak = ctxt->context->node;
	    pp = ctxt->context->proximityPosition;
	    cs = ctxt->context->contextSize;
            if (op->ch1 != -1) {
                total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
                ctxt->context->contextSize = cs;
                ctxt->context->proximityPosition = pp;
                ctxt->context->node = bak;
                ctxt->context->doc = bakd;
	        CHECK_ERROR0;
            }
            if (op->ch2 != -1) {
                total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
                ctxt->context->contextSize = cs;
                ctxt->context->proximityPosition = pp;
                ctxt->context->node = bak;
                ctxt->context->doc = bakd;
	        CHECK_ERROR0;
	    }
            return (total);
        case XPATH_OP_PREDICATE:
        case XPATH_OP_FILTER:{
                xmlXPathObjectPtr res;
                xmlXPathObjectPtr obj, tmp;
                xmlNodeSetPtr newset = NULL;
                xmlNodeSetPtr oldset;
                xmlNodePtr oldnode;
		xmlDocPtr oldDoc;
                int i;

                /*
                 * Optimization for ()[1] selection i.e. the first elem
                 */
                if ((op->ch1 != -1) && (op->ch2 != -1) &&
#ifdef XP_OPTIMIZED_FILTER_FIRST
		    /*
		    * FILTER TODO: Can we assume that the inner processing
		    *  will result in an ordered list if we have an
		    *  XPATH_OP_FILTER?
		    *  What about an additional field or flag on
		    *  xmlXPathObject like @sorted ? This way we wouln'd need
		    *  to assume anything, so it would be more robust and
		    *  easier to optimize.
		    */
                    ((comp->steps[op->ch1].op == XPATH_OP_SORT) || /* 18 */
		     (comp->steps[op->ch1].op == XPATH_OP_FILTER)) && /* 17 */
#else
		    (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
#endif
                    (comp->steps[op->ch2].op == XPATH_OP_VALUE)) { /* 12 */
                    xmlXPathObjectPtr val;

                    val = comp->steps[op->ch2].value4;
                    if ((val != NULL) && (val->type == XPATH_NUMBER) &&
                        (val->floatval == 1.0)) {
                        xmlNodePtr first = NULL;

                        total +=
                            xmlXPathCompOpEvalFirst(ctxt,
                                                    &comp->steps[op->ch1],
                                                    &first);
			CHECK_ERROR0;
                        /*
                         * The nodeset should be in document order,
                         * Keep only the first value
                         */
                        if ((ctxt->value != NULL) &&
                            (ctxt->value->type == XPATH_NODESET) &&
                            (ctxt->value->nodesetval != NULL) &&
                            (ctxt->value->nodesetval->nodeNr > 1))
                            ctxt->value->nodesetval->nodeNr = 1;
                        return (total);
                    }
                }
                /*
                 * Optimization for ()[last()] selection i.e. the last elem
                 */
                if ((op->ch1 != -1) && (op->ch2 != -1) &&
                    (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
                    (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
                    int f = comp->steps[op->ch2].ch1;

                    if ((f != -1) &&
                        (comp->steps[f].op == XPATH_OP_FUNCTION) &&
                        (comp->steps[f].value5 == NULL) &&
                        (comp->steps[f].value == 0) &&
                        (comp->steps[f].value4 != NULL) &&
                        (xmlStrEqual
                         (comp->steps[f].value4, BAD_CAST "last"))) {
                        xmlNodePtr last = NULL;

                        total +=
                            xmlXPathCompOpEvalLast(ctxt,
                                                   &comp->steps[op->ch1],
                                                   &last);
			CHECK_ERROR0;
                        /*
                         * The nodeset should be in document order,
                         * Keep only the last value
                         */
                        if ((ctxt->value != NULL) &&
                            (ctxt->value->type == XPATH_NODESET) &&
                            (ctxt->value->nodesetval != NULL) &&
                            (ctxt->value->nodesetval->nodeTab != NULL) &&
                            (ctxt->value->nodesetval->nodeNr > 1)) {
                            ctxt->value->nodesetval->nodeTab[0] =
                                ctxt->value->nodesetval->nodeTab[ctxt->
                                                                 value->
                                                                 nodesetval->
                                                                 nodeNr -
                                                                 1];
                            ctxt->value->nodesetval->nodeNr = 1;
                        }
                        return (total);
                    }
                }
		/*
		* Process inner predicates first.
		* Example "index[parent::book][1]":
		* ...
		*   PREDICATE   <-- we are here "[1]"
		*     PREDICATE <-- process "[parent::book]" first
		*       SORT
		*         COLLECT  'parent' 'name' 'node' book
		*           NODE
		*     ELEM Object is a number : 1
		*/
                if (op->ch1 != -1)
                    total +=
                        xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
		CHECK_ERROR0;
                if (op->ch2 == -1)
                    return (total);
                if (ctxt->value == NULL)
                    return (total);

                oldnode = ctxt->context->node;

#ifdef LIBXML_XPTR_ENABLED
                /*
                 * Hum are we filtering the result of an XPointer expression
                 */
                if (ctxt->value->type == XPATH_LOCATIONSET) {
                    xmlLocationSetPtr newlocset = NULL;
                    xmlLocationSetPtr oldlocset;

                    /*
                     * Extract the old locset, and then evaluate the result of the
                     * expression for all the element in the locset. use it to grow
                     * up a new locset.
                     */
                    CHECK_TYPE0(XPATH_LOCATIONSET);
                    obj = valuePop(ctxt);
                    oldlocset = obj->user;
                    ctxt->context->node = NULL;

                    if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
                        ctxt->context->contextSize = 0;
                        ctxt->context->proximityPosition = 0;
                        if (op->ch2 != -1)
                            total +=
                                xmlXPathCompOpEval(ctxt,
                                                   &comp->steps[op->ch2]);
                        res = valuePop(ctxt);
                        if (res != NULL) {
			    xmlXPathReleaseObject(ctxt->context, res);
			}
                        valuePush(ctxt, obj);
                        CHECK_ERROR0;
                        return (total);
                    }
                    newlocset = xmlXPtrLocationSetCreate(NULL);

                    for (i = 0; i < oldlocset->locNr; i++) {
                        /*
                         * Run the evaluation with a node list made of a
                         * single item in the nodelocset.
                         */
                        ctxt->context->node = oldlocset->locTab[i]->user;
                        ctxt->context->contextSize = oldlocset->locNr;
                        ctxt->context->proximityPosition = i + 1;
			tmp = xmlXPathCacheNewNodeSet(ctxt->context,
			    ctxt->context->node);
                        valuePush(ctxt, tmp);

                        if (op->ch2 != -1)
                            total +=
                                xmlXPathCompOpEval(ctxt,
                                                   &comp->steps[op->ch2]);
			if (ctxt->error != XPATH_EXPRESSION_OK) {
			    xmlXPathFreeObject(obj);
			    return(0);
			}

                        /*
                         * The result of the evaluation need to be tested to
                         * decided whether the filter succeeded or not
                         */
                        res = valuePop(ctxt);
                        if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
                            xmlXPtrLocationSetAdd(newlocset,
                                                  xmlXPathObjectCopy
                                                  (oldlocset->locTab[i]));
                        }

                        /*
                         * Cleanup
                         */
                        if (res != NULL) {
			    xmlXPathReleaseObject(ctxt->context, res);
			}
                        if (ctxt->value == tmp) {
                            res = valuePop(ctxt);
			    xmlXPathReleaseObject(ctxt->context, res);
                        }

                        ctxt->context->node = NULL;
                    }

                    /*
                     * The result is used as the new evaluation locset.
                     */
		    xmlXPathReleaseObject(ctxt->context, obj);
                    ctxt->context->node = NULL;
                    ctxt->context->contextSize = -1;
                    ctxt->context->proximityPosition = -1;
                    valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
                    ctxt->context->node = oldnode;
                    return (total);
                }
#endif /* LIBXML_XPTR_ENABLED */

                /*
                 * Extract the old set, and then evaluate the result of the
                 * expression for all the element in the set. use it to grow
                 * up a new set.
                 */
                CHECK_TYPE0(XPATH_NODESET);
                obj = valuePop(ctxt);
                oldset = obj->nodesetval;

                oldnode = ctxt->context->node;
		oldDoc = ctxt->context->doc;
                ctxt->context->node = NULL;

                if ((oldset == NULL) || (oldset->nodeNr == 0)) {
                    ctxt->context->contextSize = 0;
                    ctxt->context->proximityPosition = 0;
/*
                    if (op->ch2 != -1)
                        total +=
                            xmlXPathCompOpEval(ctxt,
                                               &comp->steps[op->ch2]);
		    CHECK_ERROR0;
                    res = valuePop(ctxt);
                    if (res != NULL)
                        xmlXPathFreeObject(res);
*/
                    valuePush(ctxt, obj);
                    ctxt->context->node = oldnode;
                    CHECK_ERROR0;
                } else {
		    tmp = NULL;
                    /*
                     * Initialize the new set.
		     * Also set the xpath document in case things like
		     * key() evaluation are attempted on the predicate
                     */
                    newset = xmlXPathNodeSetCreate(NULL);
		    /*
		    * SPEC XPath 1.0:
		    *  "For each node in the node-set to be filtered, the
		    *  PredicateExpr is evaluated with that node as the
		    *  context node, with the number of nodes in the
		    *  node-set as the context size, and with the proximity
		    *  position of the node in the node-set with respect to
		    *  the axis as the context position;"
		    * @oldset is the node-set" to be filtered.
		    *
		    * SPEC XPath 1.0:
		    *  "only predicates change the context position and
		    *  context size (see [2.4 Predicates])."
		    * Example:
		    *   node-set  context pos
		    *    nA         1
		    *    nB         2
		    *    nC         3
		    *   After applying predicate [position() > 1] :
		    *   node-set  context pos
		    *    nB         1
		    *    nC         2
		    *
		    * removed the first node in the node-set, then
		    * the context position of the
		    */
                    for (i = 0; i < oldset->nodeNr; i++) {
                        /*
                         * Run the evaluation with a node list made of
                         * a single item in the nodeset.
                         */
                        ctxt->context->node = oldset->nodeTab[i];
			if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) &&
			    (oldset->nodeTab[i]->doc != NULL))
		            ctxt->context->doc = oldset->nodeTab[i]->doc;
			if (tmp == NULL) {
			    tmp = xmlXPathCacheNewNodeSet(ctxt->context,
				ctxt->context->node);
			} else {
			    if (xmlXPathNodeSetAddUnique(tmp->nodesetval,
				               ctxt->context->node) < 0) {
				ctxt->error = XPATH_MEMORY_ERROR;
			    }
			}
                        valuePush(ctxt, tmp);
                        ctxt->context->contextSize = oldset->nodeNr;
                        ctxt->context->proximityPosition = i + 1;
			/*
			* Evaluate the predicate against the context node.
			* Can/should we optimize position() predicates
			* here (e.g. "[1]")?
			*/
                        if (op->ch2 != -1)
                            total +=
                                xmlXPathCompOpEval(ctxt,
                                                   &comp->steps[op->ch2]);
			if (ctxt->error != XPATH_EXPRESSION_OK) {
			    xmlXPathFreeNodeSet(newset);
			    xmlXPathFreeObject(obj);
			    return(0);
			}

                        /*
                         * The result of the evaluation needs to be tested to
                         * decide whether the filter succeeded or not
                         */
			/*
			* OPTIMIZE TODO: Can we use
			* xmlXPathNodeSetAdd*Unique()* instead?
			*/
                        res = valuePop(ctxt);
                        if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
                            if (xmlXPathNodeSetAdd(newset, oldset->nodeTab[i])
			        < 0)
				ctxt->error = XPATH_MEMORY_ERROR;
                        }

                        /*
                         * Cleanup
                         */
                        if (res != NULL) {
			    xmlXPathReleaseObject(ctxt->context, res);
			}
                        if (ctxt->value == tmp) {
                            valuePop(ctxt);
			    xmlXPathNodeSetClear(tmp->nodesetval, 1);
			    /*
			    * Don't free the temporary nodeset
			    * in order to avoid massive recreation inside this
			    * loop.
			    */
                        } else
			    tmp = NULL;
                        ctxt->context->node = NULL;
                    }
		    if (tmp != NULL)
			xmlXPathReleaseObject(ctxt->context, tmp);
                    /*
                     * The result is used as the new evaluation set.
                     */
		    xmlXPathReleaseObject(ctxt->context, obj);
                    ctxt->context->node = NULL;
                    ctxt->context->contextSize = -1;
                    ctxt->context->proximityPosition = -1;
		    /* may want to move this past the '}' later */
		    ctxt->context->doc = oldDoc;
		    valuePush(ctxt,
			xmlXPathCacheWrapNodeSet(ctxt->context, newset));
                }
                ctxt->context->node = oldnode;
                return (total);
            }
        case XPATH_OP_SORT:
            if (op->ch1 != -1)
                total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
	    CHECK_ERROR0;
            if ((ctxt->value != NULL) &&
                (ctxt->value->type == XPATH_NODESET) &&
                (ctxt->value->nodesetval != NULL) &&
		(ctxt->value->nodesetval->nodeNr > 1))
	    {
                xmlXPathNodeSetSort(ctxt->value->nodesetval);
	    }
            return (total);
#ifdef LIBXML_XPTR_ENABLED
        case XPATH_OP_RANGETO:{
                xmlXPathObjectPtr range;
                xmlXPathObjectPtr res, obj;
                xmlXPathObjectPtr tmp;
                xmlLocationSetPtr newlocset = NULL;
		    xmlLocationSetPtr oldlocset;
                xmlNodeSetPtr oldset;
                int i, j;

                if (op->ch1 != -1)
                    total +=
                        xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
                if (op->ch2 == -1)
                    return (total);

                if (ctxt->value->type == XPATH_LOCATIONSET) {
                    /*
                     * Extract the old locset, and then evaluate the result of the
                     * expression for all the element in the locset. use it to grow
                     * up a new locset.
                     */
                    CHECK_TYPE0(XPATH_LOCATIONSET);
                    obj = valuePop(ctxt);
                    oldlocset = obj->user;

                    if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
		        ctxt->context->node = NULL;
                        ctxt->context->contextSize = 0;
                        ctxt->context->proximityPosition = 0;
                        total += xmlXPathCompOpEval(ctxt,&comp->steps[op->ch2]);
                        res = valuePop(ctxt);
                        if (res != NULL) {
			    xmlXPathReleaseObject(ctxt->context, res);
			}
                        valuePush(ctxt, obj);
                        CHECK_ERROR0;
                        return (total);
                    }
                    newlocset = xmlXPtrLocationSetCreate(NULL);

                    for (i = 0; i < oldlocset->locNr; i++) {
                        /*
                         * Run the evaluation with a node list made of a
                         * single item in the nodelocset.
                         */
                        ctxt->context->node = oldlocset->locTab[i]->user;
                        ctxt->context->contextSize = oldlocset->locNr;
                        ctxt->context->proximityPosition = i + 1;
			tmp = xmlXPathCacheNewNodeSet(ctxt->context,
			    ctxt->context->node);
                        valuePush(ctxt, tmp);

                        if (op->ch2 != -1)
                            total +=
                                xmlXPathCompOpEval(ctxt,
                                                   &comp->steps[op->ch2]);
			if (ctxt->error != XPATH_EXPRESSION_OK) {
			    xmlXPathFreeObject(obj);
			    return(0);
			}

                        res = valuePop(ctxt);
			if (res->type == XPATH_LOCATIONSET) {
			    xmlLocationSetPtr rloc =
			        (xmlLocationSetPtr)res->user;
			    for (j=0; j<rloc->locNr; j++) {
			        range = xmlXPtrNewRange(
				  oldlocset->locTab[i]->user,
				  oldlocset->locTab[i]->index,
				  rloc->locTab[j]->user2,
				  rloc->locTab[j]->index2);
				if (range != NULL) {
				    xmlXPtrLocationSetAdd(newlocset, range);
				}
			    }
			} else {
			    range = xmlXPtrNewRangeNodeObject(
				(xmlNodePtr)oldlocset->locTab[i]->user, res);
                            if (range != NULL) {
                                xmlXPtrLocationSetAdd(newlocset,range);
			    }
                        }

                        /*
                         * Cleanup
                         */
                        if (res != NULL) {
			    xmlXPathReleaseObject(ctxt->context, res);
			}
                        if (ctxt->value == tmp) {
                            res = valuePop(ctxt);
			    xmlXPathReleaseObject(ctxt->context, res);
                        }

                        ctxt->context->node = NULL;
                    }
		} else {	/* Not a location set */
                    CHECK_TYPE0(XPATH_NODESET);
                    obj = valuePop(ctxt);
                    oldset = obj->nodesetval;
                    ctxt->context->node = NULL;

                    newlocset = xmlXPtrLocationSetCreate(NULL);

                    if (oldset != NULL) {
                        for (i = 0; i < oldset->nodeNr; i++) {
                            /*
                             * Run the evaluation with a node list made of a single item
                             * in the nodeset.
                             */
                            ctxt->context->node = oldset->nodeTab[i];
			    /*
			    * OPTIMIZE TODO: Avoid recreation for every iteration.
			    */
			    tmp = xmlXPathCacheNewNodeSet(ctxt->context,
				ctxt->context->node);
                            valuePush(ctxt, tmp);

                            if (op->ch2 != -1)
                                total +=
                                    xmlXPathCompOpEval(ctxt,
                                                   &comp->steps[op->ch2]);
			    if (ctxt->error != XPATH_EXPRESSION_OK) {
				xmlXPathFreeObject(obj);
				return(0);
			    }

                            res = valuePop(ctxt);
                            range =
                                xmlXPtrNewRangeNodeObject(oldset->nodeTab[i],
                                                      res);
                            if (range != NULL) {
                                xmlXPtrLocationSetAdd(newlocset, range);
                            }

                            /*
                             * Cleanup
                             */
                            if (res != NULL) {
				xmlXPathReleaseObject(ctxt->context, res);
			    }
                            if (ctxt->value == tmp) {
                                res = valuePop(ctxt);
				xmlXPathReleaseObject(ctxt->context, res);
                            }

                            ctxt->context->node = NULL;
                        }
                    }
                }

                /*
                 * The result is used as the new evaluation set.
                 */
		xmlXPathReleaseObject(ctxt->context, obj);
                ctxt->context->node = NULL;
                ctxt->context->contextSize = -1;
                ctxt->context->proximityPosition = -1;
                valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
                return (total);
            }
#endif /* LIBXML_XPTR_ENABLED */
    }
    xmlGenericError(xmlGenericErrorContext,
                    "XPath: unknown precompiled operation %d\n", op->op);
    ctxt->error = XPATH_INVALID_OPERAND;


xmlCleanupOutputCallbacks(void)
{
    int i;

    if (!xmlOutputCallbackInitialized)
        return;

    for (i = xmlOutputCallbackNr - 1; i >= 0; i--) {
        xmlOutputCallbackTable[i].matchcallback = NULL;
        xmlOutputCallbackTable[i].opencallback = NULL;
        xmlOutputCallbackTable[i].writecallback = NULL;
        xmlOutputCallbackTable[i].closecallback = NULL;
    }

    xmlOutputCallbackNr = 0;
    xmlOutputCallbackInitialized = 0;
}


xmlSchemaGetParserErrors(xmlSchemaParserCtxtPtr ctxt,
			 xmlSchemaValidityErrorFunc * err,
			 xmlSchemaValidityWarningFunc * warn, void **ctx)
{
	if (ctxt == NULL)
		return(-1);
	if (err != NULL)
		*err = ctxt->error;
	if (warn != NULL)
		*warn = ctxt->warning;
	if (ctx != NULL)
		*ctx = ctxt->errCtxt;
	return(0);
}


xmlFdClose (void * context) {
    int ret;
    ret = close((int) (long) context);
    if (ret < 0) xmlIOErr(0, "close()");
    return(ret);
}


xmlSchemaValidateStreamLocator(void *ctx, const char **file,
                               unsigned long *line) {
    xmlParserCtxtPtr ctxt;

    if ((ctx == NULL) || ((file == NULL) && (line == NULL)))
        return(-1);

    if (file != NULL)
        *file = NULL;
    if (line != NULL)
        *line = 0;

    ctxt = (xmlParserCtxtPtr) ctx;
    if (ctxt->input != NULL) {
       if (file != NULL)
           *file = ctxt->input->filename;
       if (line != NULL)
           *line = ctxt->input->line;
       return(0);
    }
    return(-1);
}


xmlSchemaResolveAttrTypeReferences(xmlSchemaAttributePtr item,
				   xmlSchemaParserCtxtPtr ctxt)
{
    /*
    * The simple type definition corresponding to the <simpleType> element
    * information item in the [children], if present, otherwise the simple
    * type definition `resolved` to by the `actual value` of the type
    * [attribute], if present, otherwise the `simple ur-type definition`.
    */
    if (item->flags & XML_SCHEMAS_ATTR_INTERNAL_RESOLVED)
	return(0);
    item->flags |= XML_SCHEMAS_ATTR_INTERNAL_RESOLVED;
    if (item->subtypes != NULL)
        return(0);
    if (item->typeName != NULL) {
        xmlSchemaTypePtr type;

	type = xmlSchemaGetType(ctxt->schema, item->typeName,
	    item->typeNs);
	if ((type == NULL) || (! WXS_IS_SIMPLE(type))) {
	    xmlSchemaPResCompAttrErr(ctxt,
		XML_SCHEMAP_SRC_RESOLVE,
		WXS_BASIC_CAST item, item->node,
		"type", item->typeName, item->typeNs,
		XML_SCHEMA_TYPE_SIMPLE, NULL);
	    return(ctxt->err);
	} else
	    item->subtypes = type;

    } else {
	/*
	* The type defaults to the xs:anySimpleType.
	*/
	item->subtypes = xmlSchemaGetBuiltInType(XML_SCHEMAS_ANYSIMPLETYPE);
    }
    return(0);
}


xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
                          xmlRelaxNGValidStatePtr state1,
                          xmlRelaxNGValidStatePtr state2)
{
    int i;

    if ((state1 == NULL) || (state2 == NULL))
        return (0);
    if (state1 == state2)
        return (1);
    if (state1->node != state2->node)
        return (0);
    if (state1->seq != state2->seq)
        return (0);
    if (state1->nbAttrLeft != state2->nbAttrLeft)
        return (0);
    if (state1->nbAttrs != state2->nbAttrs)
        return (0);
    if (state1->endvalue != state2->endvalue)
        return (0);
    if ((state1->value != state2->value) &&
        (!xmlStrEqual(state1->value, state2->value)))
        return (0);
    for (i = 0; i < state1->nbAttrs; i++) {
        if (state1->attrs[i] != state2->attrs[i])
            return (0);
    }
    return (1);
}


xmlRngVErr(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node, int error,
           const char *msg, const xmlChar * str1, const xmlChar * str2)
{
    xmlStructuredErrorFunc schannel = NULL;
    xmlGenericErrorFunc channel = NULL;
    void *data = NULL;

    if (ctxt != NULL) {
        if (ctxt->serror != NULL)
	    schannel = ctxt->serror;
	else
	    channel = ctxt->error;
        data = ctxt->userData;
        ctxt->nbErrors++;
    }
    __xmlRaiseError(schannel, channel, data,
                    NULL, node, XML_FROM_RELAXNGV,
                    error, XML_ERR_ERROR, NULL, 0,
                    (const char *) str1, (const char *) str2, NULL, 0, 0,
                    msg, str1, str2);
}


xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
                         xmlRelaxNGValidityErrorFunc err,
                         xmlRelaxNGValidityWarningFunc warn, void *ctx)
{
    if (ctxt == NULL)
        return;
    ctxt->error = err;
    ctxt->warning = warn;
    ctxt->userData = ctx;
    ctxt->serror = NULL;
}


xmlRelaxNGElemPush(xmlRelaxNGValidCtxtPtr ctxt, xmlRegExecCtxtPtr exec)
{
    if (ctxt->elemTab == NULL) {
        ctxt->elemMax = 10;
        ctxt->elemTab = (xmlRegExecCtxtPtr *) xmlMalloc(ctxt->elemMax *
                                                        sizeof
                                                        (xmlRegExecCtxtPtr));
        if (ctxt->elemTab == NULL) {
            xmlRngVErrMemory(ctxt, "validating\n");
            return (-1);
        }
    }
    if (ctxt->elemNr >= ctxt->elemMax) {
        ctxt->elemMax *= 2;
        ctxt->elemTab = (xmlRegExecCtxtPtr *) xmlRealloc(ctxt->elemTab,
                                                         ctxt->elemMax *
                                                         sizeof
                                                         (xmlRegExecCtxtPtr));
        if (ctxt->elemTab == NULL) {
            xmlRngVErrMemory(ctxt, "validating\n");
            return (-1);
        }
    }
    ctxt->elemTab[ctxt->elemNr++] = exec;
    ctxt->elem = exec;
    return (0);
}


xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
{
    xmlRelaxNGValidStatePtr ret;
    xmlAttrPtr attr;
    xmlAttrPtr attrs[MAX_ATTR];
    int nbAttrs = 0;
    xmlNodePtr root = NULL;

    if (node == NULL) {
        root = xmlDocGetRootElement(ctxt->doc);
        if (root == NULL)
            return (NULL);
    } else {
        attr = node->properties;
        while (attr != NULL) {
            if (nbAttrs < MAX_ATTR)
                attrs[nbAttrs++] = attr;
            else
                nbAttrs++;
            attr = attr->next;
        }
    }
    if ((ctxt->freeState != NULL) && (ctxt->freeState->nbState > 0)) {
        ctxt->freeState->nbState--;
        ret = ctxt->freeState->tabState[ctxt->freeState->nbState];
    } else {
        ret =
            (xmlRelaxNGValidStatePtr)
            xmlMalloc(sizeof(xmlRelaxNGValidState));
        if (ret == NULL) {
            xmlRngVErrMemory(ctxt, "allocating states\n");
            return (NULL);
        }
        memset(ret, 0, sizeof(xmlRelaxNGValidState));
    }
    ret->value = NULL;
    ret->endvalue = NULL;
    if (node == NULL) {
        ret->node = (xmlNodePtr) ctxt->doc;
        ret->seq = root;
    } else {
        ret->node = node;
        ret->seq = node->children;
    }
    ret->nbAttrs = 0;
    if (nbAttrs > 0) {
        if (ret->attrs == NULL) {
            if (nbAttrs < 4)
                ret->maxAttrs = 4;
            else
                ret->maxAttrs = nbAttrs;
            ret->attrs = (xmlAttrPtr *) xmlMalloc(ret->maxAttrs *
                                                  sizeof(xmlAttrPtr));
            if (ret->attrs == NULL) {
                xmlRngVErrMemory(ctxt, "allocating states\n");
                return (ret);
            }
        } else if (ret->maxAttrs < nbAttrs) {
            xmlAttrPtr *tmp;

            tmp = (xmlAttrPtr *) xmlRealloc(ret->attrs, nbAttrs *
                                            sizeof(xmlAttrPtr));
            if (tmp == NULL) {
                xmlRngVErrMemory(ctxt, "allocating states\n");
                return (ret);
            }
            ret->attrs = tmp;
            ret->maxAttrs = nbAttrs;
        }
        ret->nbAttrs = nbAttrs;
        if (nbAttrs < MAX_ATTR) {
            memcpy(ret->attrs, attrs, sizeof(xmlAttrPtr) * nbAttrs);
        } else {
            attr = node->properties;
            nbAttrs = 0;
            while (attr != NULL) {
                ret->attrs[nbAttrs++] = attr;
                attr = attr->next;
            }
        }
    }
    ret->nbAttrLeft = ret->nbAttrs;
    return (ret);
}


xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
{
    xmlRelaxNGDefinePtr def = NULL;

    if (node == NULL) {
        return (NULL);
    }
    if (IS_RELAXNG(node, "element")) {
        def = xmlRelaxNGParseElement(ctxt, node);
    } else if (IS_RELAXNG(node, "attribute")) {
        def = xmlRelaxNGParseAttribute(ctxt, node);
    } else if (IS_RELAXNG(node, "empty")) {
        def = xmlRelaxNGNewDefine(ctxt, node);
        if (def == NULL)
            return (NULL);
        def->type = XML_RELAXNG_EMPTY;
        if (node->children != NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_NOT_EMPTY,
                       "empty: had a child node\n", NULL, NULL);
        }
    } else if (IS_RELAXNG(node, "text")) {
        def = xmlRelaxNGNewDefine(ctxt, node);
        if (def == NULL)
            return (NULL);
        def->type = XML_RELAXNG_TEXT;
        if (node->children != NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_TEXT_HAS_CHILD,
                       "text: had a child node\n", NULL, NULL);
        }
    } else if (IS_RELAXNG(node, "zeroOrMore")) {
        def = xmlRelaxNGNewDefine(ctxt, node);
        if (def == NULL)
            return (NULL);
        def->type = XML_RELAXNG_ZEROORMORE;
        if (node->children == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT,
                       "Element %s is empty\n", node->name, NULL);
        } else {
            def->content =
                xmlRelaxNGParsePatterns(ctxt, node->children, 1);
        }
    } else if (IS_RELAXNG(node, "oneOrMore")) {
        def = xmlRelaxNGNewDefine(ctxt, node);
        if (def == NULL)
            return (NULL);
        def->type = XML_RELAXNG_ONEORMORE;
        if (node->children == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT,
                       "Element %s is empty\n", node->name, NULL);
        } else {
            def->content =
                xmlRelaxNGParsePatterns(ctxt, node->children, 1);
        }
    } else if (IS_RELAXNG(node, "optional")) {
        def = xmlRelaxNGNewDefine(ctxt, node);
        if (def == NULL)
            return (NULL);
        def->type = XML_RELAXNG_OPTIONAL;
        if (node->children == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT,
                       "Element %s is empty\n", node->name, NULL);
        } else {
            def->content =
                xmlRelaxNGParsePatterns(ctxt, node->children, 1);
        }
    } else if (IS_RELAXNG(node, "choice")) {
        def = xmlRelaxNGNewDefine(ctxt, node);
        if (def == NULL)
            return (NULL);
        def->type = XML_RELAXNG_CHOICE;
        if (node->children == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT,
                       "Element %s is empty\n", node->name, NULL);
        } else {
            def->content =
                xmlRelaxNGParsePatterns(ctxt, node->children, 0);
        }
    } else if (IS_RELAXNG(node, "group")) {
        def = xmlRelaxNGNewDefine(ctxt, node);
        if (def == NULL)
            return (NULL);
        def->type = XML_RELAXNG_GROUP;
        if (node->children == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT,
                       "Element %s is empty\n", node->name, NULL);
        } else {
            def->content =
                xmlRelaxNGParsePatterns(ctxt, node->children, 0);
        }
    } else if (IS_RELAXNG(node, "ref")) {
        def = xmlRelaxNGNewDefine(ctxt, node);
        if (def == NULL)
            return (NULL);
        def->type = XML_RELAXNG_REF;
        def->name = xmlGetProp(node, BAD_CAST "name");
        if (def->name == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_REF_NO_NAME, "ref has no name\n",
                       NULL, NULL);
        } else {
            xmlRelaxNGNormExtSpace(def->name);
            if (xmlValidateNCName(def->name, 0)) {
                xmlRngPErr(ctxt, node, XML_RNGP_REF_NAME_INVALID,
                           "ref name '%s' is not an NCName\n", def->name,
                           NULL);
            }
        }
        if (node->children != NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_REF_NOT_EMPTY, "ref is not empty\n",
                       NULL, NULL);
        }
        if (ctxt->grammar->refs == NULL)
            ctxt->grammar->refs = xmlHashCreate(10);
        if (ctxt->grammar->refs == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_REF_CREATE_FAILED,
                       "Could not create references hash\n", NULL, NULL);
            def = NULL;
        } else {
            int tmp;

            tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
            if (tmp < 0) {
                xmlRelaxNGDefinePtr prev;

                prev = (xmlRelaxNGDefinePtr)
                    xmlHashLookup(ctxt->grammar->refs, def->name);
                if (prev == NULL) {
                    if (def->name != NULL) {
		        xmlRngPErr(ctxt, node, XML_RNGP_REF_CREATE_FAILED,
				   "Error refs definitions '%s'\n",
				   def->name, NULL);
                    } else {
		        xmlRngPErr(ctxt, node, XML_RNGP_REF_CREATE_FAILED,
				   "Error refs definitions\n",
				   NULL, NULL);
                    }
                    def = NULL;
                } else {
                    def->nextHash = prev->nextHash;
                    prev->nextHash = def;
                }
            }
        }
    } else if (IS_RELAXNG(node, "data")) {
        def = xmlRelaxNGParseData(ctxt, node);
    } else if (IS_RELAXNG(node, "value")) {
        def = xmlRelaxNGParseValue(ctxt, node);
    } else if (IS_RELAXNG(node, "list")) {
        def = xmlRelaxNGNewDefine(ctxt, node);
        if (def == NULL)
            return (NULL);
        def->type = XML_RELAXNG_LIST;
        if (node->children == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT,
                       "Element %s is empty\n", node->name, NULL);
        } else {
            def->content =
                xmlRelaxNGParsePatterns(ctxt, node->children, 0);
        }
    } else if (IS_RELAXNG(node, "interleave")) {
        def = xmlRelaxNGParseInterleave(ctxt, node);
    } else if (IS_RELAXNG(node, "externalRef")) {
        def = xmlRelaxNGProcessExternalRef(ctxt, node);
    } else if (IS_RELAXNG(node, "notAllowed")) {
        def = xmlRelaxNGNewDefine(ctxt, node);
        if (def == NULL)
            return (NULL);
        def->type = XML_RELAXNG_NOT_ALLOWED;
        if (node->children != NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_NOTALLOWED_NOT_EMPTY,
                       "xmlRelaxNGParse: notAllowed element is not empty\n",
                       NULL, NULL);
        }
    } else if (IS_RELAXNG(node, "grammar")) {
        xmlRelaxNGGrammarPtr grammar, old;
        xmlRelaxNGGrammarPtr oldparent;

#ifdef DEBUG_GRAMMAR
        xmlGenericError(xmlGenericErrorContext,
                        "Found <grammar> pattern\n");
#endif

        oldparent = ctxt->parentgrammar;
        old = ctxt->grammar;
        ctxt->parentgrammar = old;
        grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
        if (old != NULL) {
            ctxt->grammar = old;
            ctxt->parentgrammar = oldparent;
#if 0
            if (grammar != NULL) {
                grammar->next = old->next;
                old->next = grammar;
            }
#endif
        }
        if (grammar != NULL)
            def = grammar->start;
        else
            def = NULL;
    } else if (IS_RELAXNG(node, "parentRef")) {
        if (ctxt->parentgrammar == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_NO_PARENT,
                       "Use of parentRef without a parent grammar\n", NULL,
                       NULL);
            return (NULL);
        }
        def = xmlRelaxNGNewDefine(ctxt, node);
        if (def == NULL)
            return (NULL);
        def->type = XML_RELAXNG_PARENTREF;
        def->name = xmlGetProp(node, BAD_CAST "name");
        if (def->name == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_NO_NAME,
                       "parentRef has no name\n", NULL, NULL);
        } else {
            xmlRelaxNGNormExtSpace(def->name);
            if (xmlValidateNCName(def->name, 0)) {
                xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_NAME_INVALID,
                           "parentRef name '%s' is not an NCName\n",
                           def->name, NULL);
            }
        }
        if (node->children != NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_NOT_EMPTY,
                       "parentRef is not empty\n", NULL, NULL);
        }
        if (ctxt->parentgrammar->refs == NULL)
            ctxt->parentgrammar->refs = xmlHashCreate(10);
        if (ctxt->parentgrammar->refs == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_CREATE_FAILED,
                       "Could not create references hash\n", NULL, NULL);
            def = NULL;
        } else if (def->name != NULL) {
            int tmp;

            tmp =
                xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
            if (tmp < 0) {
                xmlRelaxNGDefinePtr prev;

                prev = (xmlRelaxNGDefinePtr)
                    xmlHashLookup(ctxt->parentgrammar->refs, def->name);
                if (prev == NULL) {
                    xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_CREATE_FAILED,
                               "Internal error parentRef definitions '%s'\n",
                               def->name, NULL);
                    def = NULL;
                } else {
                    def->nextHash = prev->nextHash;
                    prev->nextHash = def;
                }
            }
        }
    } else if (IS_RELAXNG(node, "mixed")) {
        if (node->children == NULL) {
            xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT, "Mixed is empty\n",
                       NULL, NULL);
            def = NULL;
        } else {
            def = xmlRelaxNGParseInterleave(ctxt, node);
            if (def != NULL) {
                xmlRelaxNGDefinePtr tmp;

                if ((def->content != NULL) && (def->content->next != NULL)) {
                    tmp = xmlRelaxNGNewDefine(ctxt, node);
                    if (tmp != NULL) {
                        tmp->type = XML_RELAXNG_GROUP;
                        tmp->content = def->content;
                        def->content = tmp;
                    }
                }

                tmp = xmlRelaxNGNewDefine(ctxt, node);
                if (tmp == NULL)
                    return (def);
                tmp->type = XML_RELAXNG_TEXT;
                tmp->next = def->content;
                def->content = tmp;
            }
        }
    } else {
        xmlRngPErr(ctxt, node, XML_RNGP_UNKNOWN_CONSTRUCT,
                   "Unexpected node %s is not a pattern\n", node->name,
                   NULL);
        def = NULL;
    }
    return (def);
}


xmlRelaxNGIsNullable(xmlRelaxNGDefinePtr define)
{
    int ret;

    if (define == NULL)
        return (-1);

    if (define->dflags & IS_NULLABLE)
        return (1);
    if (define->dflags & IS_NOT_NULLABLE)
        return (0);
    switch (define->type) {
        case XML_RELAXNG_EMPTY:
        case XML_RELAXNG_TEXT:
            ret = 1;
            break;
        case XML_RELAXNG_NOOP:
        case XML_RELAXNG_DEF:
        case XML_RELAXNG_REF:
        case XML_RELAXNG_EXTERNALREF:
        case XML_RELAXNG_PARENTREF:
        case XML_RELAXNG_ONEORMORE:
            ret = xmlRelaxNGIsNullable(define->content);
            break;
        case XML_RELAXNG_EXCEPT:
        case XML_RELAXNG_NOT_ALLOWED:
        case XML_RELAXNG_ELEMENT:
        case XML_RELAXNG_DATATYPE:
        case XML_RELAXNG_PARAM:
        case XML_RELAXNG_VALUE:
        case XML_RELAXNG_LIST:
        case XML_RELAXNG_ATTRIBUTE:
            ret = 0;
            break;
        case XML_RELAXNG_CHOICE:{
                xmlRelaxNGDefinePtr list = define->content;

                while (list != NULL) {
                    ret = xmlRelaxNGIsNullable(list);
                    if (ret != 0)
                        goto done;
                    list = list->next;
                }
                ret = 0;
                break;
            }
        case XML_RELAXNG_START:
        case XML_RELAXNG_INTERLEAVE:
        case XML_RELAXNG_GROUP:{
                xmlRelaxNGDefinePtr list = define->content;

                while (list != NULL) {
                    ret = xmlRelaxNGIsNullable(list);
                    if (ret != 1)
                        goto done;
                    list = list->next;
                }
                return (1);
            }
        default:
            return (-1);
    }
  done:
    if (ret == 0)
        define->dflags |= IS_NOT_NULLABLE;
    if (ret == 1)
        define->dflags |= IS_NULLABLE;
    return (ret);
}


xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
{
    if (output == NULL)
        return;
    if (schema == NULL) {
        fprintf(output, "RelaxNG empty or failed to compile\n");
        return;
    }
    fprintf(output, "RelaxNG: ");
    if (schema->doc == NULL) {
        fprintf(output, "no document\n");
    } else if (schema->doc->URL != NULL) {
        fprintf(output, "%s\n", schema->doc->URL);
    } else {
        fprintf(output, "\n");
    }
    if (schema->topgrammar == NULL) {
        fprintf(output, "RelaxNG has no top grammar\n");
        return;
    }
    xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
}


xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED,
                          const xmlChar * type)
{
    if (type == NULL)
        return (-1);
    if (xmlStrEqual(type, BAD_CAST "string"))
        return (1);
    if (xmlStrEqual(type, BAD_CAST "token"))
        return (1);
    return (0);
}


xmlRelaxNGNormExtSpace(xmlChar * value)
{
    xmlChar *start = value;
    xmlChar *cur = value;

    if (value == NULL)
        return;

    while (IS_BLANK_CH(*cur))
        cur++;
    if (cur == start) {
        do {
            while ((*cur != 0) && (!IS_BLANK_CH(*cur)))
                cur++;
            if (*cur == 0)
                return;
            start = cur;
            while (IS_BLANK_CH(*cur))
                cur++;
            if (*cur == 0) {
                *start = 0;
                return;
            }
        } while (1);
    } else {
        do {
            while ((*cur != 0) && (!IS_BLANK_CH(*cur)))
                *start++ = *cur++;
            if (*cur == 0) {
                *start = 0;
                return;
            }
            /* don't try to normalize the inner spaces */
            while (IS_BLANK_CH(*cur))
                cur++;
            if (*cur == 0) {
                *start = 0;
                return;
            }
            *start++ = *cur++;
        } while (1);
    }
}


xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
{
    xmlRelaxNGPtr ret = NULL;
    xmlDocPtr doc;
    xmlNodePtr root;

    xmlRelaxNGInitTypes();

    if (ctxt == NULL)
        return (NULL);

    /*
     * First step is to parse the input document into an DOM/Infoset
     */
    if (ctxt->URL != NULL) {
        doc = xmlReadFile((const char *) ctxt->URL,NULL,0);
        if (doc == NULL) {
            xmlRngPErr(ctxt, NULL, XML_RNGP_PARSE_ERROR,
                       "xmlRelaxNGParse: could not load %s\n", ctxt->URL,
                       NULL);
            return (NULL);
        }
    } else if (ctxt->buffer != NULL) {
        doc = xmlReadMemory(ctxt->buffer, ctxt->size,NULL,NULL,0);
        if (doc == NULL) {
            xmlRngPErr(ctxt, NULL, XML_RNGP_PARSE_ERROR,
                       "xmlRelaxNGParse: could not parse schemas\n", NULL,
                       NULL);
            return (NULL);
        }
        doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
        ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
    } else if (ctxt->document != NULL) {
        doc = ctxt->document;
    } else {
        xmlRngPErr(ctxt, NULL, XML_RNGP_EMPTY,
                   "xmlRelaxNGParse: nothing to parse\n", NULL, NULL);
        return (NULL);
    }
    ctxt->document = doc;

    /*
     * Some preprocessing of the document content
     */
    doc = xmlRelaxNGCleanupDoc(ctxt, doc);
    if (doc == NULL) {
        xmlFreeDoc(ctxt->document);
        ctxt->document = NULL;
        return (NULL);
    }

    /*
     * Then do the parsing for good
     */
    root = xmlDocGetRootElement(doc);
    if (root == NULL) {
        xmlRngPErr(ctxt, (xmlNodePtr) doc,
	           XML_RNGP_EMPTY, "xmlRelaxNGParse: %s is empty\n",
                   (ctxt->URL ? ctxt->URL : BAD_CAST "schemas"), NULL);

        xmlFreeDoc(ctxt->document);
        ctxt->document = NULL;
        return (NULL);
    }
    ret = xmlRelaxNGParseDocument(ctxt, root);
    if (ret == NULL) {
        xmlFreeDoc(ctxt->document);
        ctxt->document = NULL;
        return (NULL);
    }

    /*
     * Check the ref/defines links
     */
    /*
     * try to preprocess interleaves
     */
    if (ctxt->interleaves != NULL) {
        xmlHashScan(ctxt->interleaves,
                    (xmlHashScanner) xmlRelaxNGComputeInterleaves, ctxt);
    }

    /*
     * if there was a parsing error return NULL
     */
    if (ctxt->nbErrors > 0) {
        xmlRelaxNGFree(ret);
        ctxt->document = NULL;
        xmlFreeDoc(doc);
        return (NULL);
    }

    /*
     * try to compile (parts of) the schemas
     */
    if ((ret->topgrammar != NULL) && (ret->topgrammar->start != NULL)) {
        if (ret->topgrammar->start->type != XML_RELAXNG_START) {
            xmlRelaxNGDefinePtr def;

            def = xmlRelaxNGNewDefine(ctxt, NULL);
            if (def != NULL) {
                def->type = XML_RELAXNG_START;
                def->content = ret->topgrammar->start;
                ret->topgrammar->start = def;
            }
        }
        xmlRelaxNGTryCompile(ctxt, ret->topgrammar->start);
    }

    /*
     * Transfer the pointer for cleanup at the schema level.
     */
    ret->doc = doc;
    ctxt->document = NULL;
    ret->documents = ctxt->documents;
    ctxt->documents = NULL;

    ret->includes = ctxt->includes;
    ctxt->includes = NULL;
    ret->defNr = ctxt->defNr;
    ret->defTab = ctxt->defTab;
    ctxt->defTab = NULL;
    if (ctxt->idref == 1)
        ret->idref = 1;

    return (ret);
}


xmlSchemaSimpleTypeErr(xmlSchemaAbstractCtxtPtr actxt,
		       xmlParserErrors error,
		       xmlNodePtr node,
		       const xmlChar *value,
		       xmlSchemaTypePtr type,
		       int displayValue)
{
    xmlChar *msg = NULL;

    xmlSchemaFormatNodeForError(&msg, actxt, node);

    if (displayValue || (xmlSchemaEvalErrorNodeType(actxt, node) ==
	    XML_ATTRIBUTE_NODE))
	msg = xmlStrcat(msg, BAD_CAST "'%s' is not a valid value of ");
    else
	msg = xmlStrcat(msg, BAD_CAST "The character content is not a valid "
	    "value of ");

    if (! xmlSchemaIsGlobalItem(type))
	msg = xmlStrcat(msg, BAD_CAST "the local ");
    else
	msg = xmlStrcat(msg, BAD_CAST "the ");

    if (WXS_IS_ATOMIC(type))
	msg = xmlStrcat(msg, BAD_CAST "atomic type");
    else if (WXS_IS_LIST(type))
	msg = xmlStrcat(msg, BAD_CAST "list type");
    else if (WXS_IS_UNION(type))
	msg = xmlStrcat(msg, BAD_CAST "union type");

    if (xmlSchemaIsGlobalItem(type)) {
	xmlChar *str = NULL;
	msg = xmlStrcat(msg, BAD_CAST " '");
	if (type->builtInType != 0) {
	    msg = xmlStrcat(msg, BAD_CAST "xs:");
	    str = xmlStrdup(type->name);
	} else {
	    const xmlChar *qName = xmlSchemaFormatQName(&str, type->targetNamespace, type->name);
	    if (!str)
		str = xmlStrdup(qName);
	}
	msg = xmlStrcat(msg, xmlEscapeFormatString(&str));
	msg = xmlStrcat(msg, BAD_CAST "'");
	FREE_AND_NULL(str);
    }
    msg = xmlStrcat(msg, BAD_CAST ".\n");
    if (displayValue || (xmlSchemaEvalErrorNodeType(actxt, node) ==
	    XML_ATTRIBUTE_NODE))
	xmlSchemaErr(actxt, error, node, (const char *) msg, value, NULL);
    else
	xmlSchemaErr(actxt, error, node, (const char *) msg, NULL, NULL);
    FREE_AND_NULL(msg)
}


xmlRelaxNGValidErrorPop(xmlRelaxNGValidCtxtPtr ctxt)
{
    xmlRelaxNGValidErrorPtr cur;

    if (ctxt->errNr <= 0) {
        ctxt->err = NULL;
        return;
    }
    ctxt->errNr--;
    if (ctxt->errNr > 0)
        ctxt->err = &ctxt->errTab[ctxt->errNr - 1];
    else
        ctxt->err = NULL;
    cur = &ctxt->errTab[ctxt->errNr];
    if (cur->flags & ERROR_IS_DUP) {
        if (cur->arg1 != NULL)
            xmlFree((xmlChar *) cur->arg1);
        cur->arg1 = NULL;
        if (cur->arg2 != NULL)
            xmlFree((xmlChar *) cur->arg2);
        cur->arg2 = NULL;
        cur->flags = 0;
    }
}


xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
                               xmlRelaxNGDefinePtr defines)
{
    int ret = 0;

    while (defines != NULL) {
        ret = xmlRelaxNGValidateValue(ctxt, defines);
        if (ret != 0)
            break;
        defines = defines->next;
    }
    return (ret);
}


xmlRelaxNGValidateCompiledCallback(xmlRegExecCtxtPtr exec ATTRIBUTE_UNUSED,
                                   const xmlChar * token,
                                   void *transdata, void *inputdata)
{
    xmlRelaxNGValidCtxtPtr ctxt = (xmlRelaxNGValidCtxtPtr) inputdata;
    xmlRelaxNGDefinePtr define = (xmlRelaxNGDefinePtr) transdata;
    int ret;

#ifdef DEBUG_COMPILE
    xmlGenericError(xmlGenericErrorContext,
                    "Compiled callback for: '%s'\n", token);
#endif
    if (ctxt == NULL) {
        fprintf(stderr, "callback on %s missing context\n", token);
        return;
    }
    if (define == NULL) {
        if (token[0] == '#')
            return;
        fprintf(stderr, "callback on %s missing define\n", token);
        if ((ctxt != NULL) && (ctxt->errNo == XML_RELAXNG_OK))
            ctxt->errNo = XML_RELAXNG_ERR_INTERNAL;
        return;
    }
    if ((ctxt == NULL) || (define == NULL)) {
        fprintf(stderr, "callback on %s missing info\n", token);
        if ((ctxt != NULL) && (ctxt->errNo == XML_RELAXNG_OK))
            ctxt->errNo = XML_RELAXNG_ERR_INTERNAL;
        return;
    } else if (define->type != XML_RELAXNG_ELEMENT) {
        fprintf(stderr, "callback on %s define is not element\n", token);
        if (ctxt->errNo == XML_RELAXNG_OK)
            ctxt->errNo = XML_RELAXNG_ERR_INTERNAL;
        return;
    }
    ret = xmlRelaxNGValidateDefinition(ctxt, define);
    if (ret != 0)
        ctxt->perr = ret;
}


xmlRelaxNGNewMemParserCtxt(const char *buffer, int size)
{
    xmlRelaxNGParserCtxtPtr ret;

    if ((buffer == NULL) || (size <= 0))
        return (NULL);

    ret =
        (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
    if (ret == NULL) {
        xmlRngPErrMemory(NULL, "building parser\n");
        return (NULL);
    }
    memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
    ret->buffer = buffer;
    ret->size = size;
    ret->error = xmlGenericError;
    ret->userData = xmlGenericErrorContext;
    return (ret);
}


xmlRelaxNGValidateFullElement(xmlRelaxNGValidCtxtPtr ctxt,
                              xmlDocPtr doc ATTRIBUTE_UNUSED,
                              xmlNodePtr elem)
{
    int ret;
    xmlRelaxNGValidStatePtr state;

    if ((ctxt == NULL) || (ctxt->pdef == NULL) || (elem == NULL))
        return (-1);
#ifdef DEBUG_PROGRESSIVE
    xmlGenericError(xmlGenericErrorContext, "FullElem %s\n", elem->name);
#endif
    state = xmlRelaxNGNewValidState(ctxt, elem->parent);
    if (state == NULL) {
        return (-1);
    }
    state->seq = elem;
    ctxt->state = state;
    ctxt->errNo = XML_RELAXNG_OK;
    ret = xmlRelaxNGValidateDefinition(ctxt, ctxt->pdef);
    if ((ret != 0) || (ctxt->errNo != XML_RELAXNG_OK))
        ret = -1;
    else
        ret = 1;
    xmlRelaxNGFreeValidState(ctxt, ctxt->state);
    ctxt->state = NULL;
#ifdef DEBUG_PROGRESSIVE
    if (ret < 0)
        xmlGenericError(xmlGenericErrorContext, "FullElem %s failed\n",
                        elem->name);
#endif
    return (ret);
}


static void processNode(FILE *out, xmlTextReaderPtr reader) {
    const xmlChar *name, *value;
    int type, empty;

    type = xmlTextReaderNodeType(reader);
    empty = xmlTextReaderIsEmptyElement(reader);

    name = xmlTextReaderConstName(reader);
    if (name == NULL)
	name = BAD_CAST "--";

    value = xmlTextReaderConstValue(reader);

    fprintf(out, "%d %d %s %d %d",
	    xmlTextReaderDepth(reader),
	    type,
	    name,
	    empty,
	    xmlTextReaderHasValue(reader));
    if (value == NULL)
	fprintf(out, "\n");
    else {
	fprintf(out, " %s\n", value);
    }
}


xmlParserPrintFileContextInternal(xmlParserInputPtr input ,
		xmlGenericErrorFunc chanl, void *data ) {
    const xmlChar *cur, *base;
    unsigned int n, col;	/* GCC warns if signed, because compared with sizeof() */
    xmlChar  content[81]; /* space for 80 chars + line terminator */
    xmlChar *ctnt;

    if (input == NULL) return;
    cur = input->cur;
    base = input->base;
    /* skip backwards over any end-of-lines */
    while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) {
	cur--;
    }
    n = 0;
    /* search backwards for beginning-of-line (to max buff size) */
    while ((n++ < (sizeof(content)-1)) && (cur > base) &&
   (*(cur) != '\n') && (*(cur) != '\r'))
        cur--;
    if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;
    /* calculate the error position in terms of the current position */
    col = input->cur - cur;
    /* search forward for end-of-line (to max buff size) */
    n = 0;
    ctnt = content;
    /* copy selected text to our buffer */
    while ((*cur != 0) && (*(cur) != '\n') &&
   (*(cur) != '\r') && (n < sizeof(content)-1)) {
		*ctnt++ = *cur++;
	n++;
    }
    *ctnt = 0;
    /* print out the selected text */
    chanl(data ,"%s\n", content);
    /* create blank line with problem pointer */
    n = 0;
    ctnt = content;
    /* (leave buffer space for pointer + line terminator) */
    while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) {
	if (*(ctnt) != '\t')
	    *(ctnt) = ' ';
	ctnt++;
    }
    *ctnt++ = '^';
    *ctnt = 0;
    chanl(data ,"%s\n", content);
}


unparsedEntityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
		   const xmlChar *publicId, const xmlChar *systemId,
		   const xmlChar *notationName)
{
const xmlChar *nullstr = BAD_CAST "(null)";

    if (publicId == NULL)
        publicId = nullstr;
    if (systemId == NULL)
        systemId = nullstr;
    if (notationName == NULL)
        notationName = nullstr;
    callbacks++;
    if (quiet)
	return;
    fprintf(SAXdebug, "SAX.unparsedEntityDecl(%s, %s, %s, %s)\n",
            (char *) name, (char *) publicId, (char *) systemId,
	    (char *) notationName);
}


c14nRunTest(const char* xml_filename, int with_comments, int mode,
	    const char* xpath_filename, const char *ns_filename,
	    const char* result_file) {
    xmlDocPtr doc;
    xmlXPathObjectPtr xpath = NULL;
    xmlChar *result = NULL;
    int ret;
    xmlChar **inclusive_namespaces = NULL;
    const char *nslist = NULL;
    int nssize;

    /*
     * build an XML tree from a the file; we need to add default
     * attributes and resolve all character and entities references
     */
    xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
    xmlSubstituteEntitiesDefault(1);

    doc = xmlReadFile(xml_filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT);
    if (doc == NULL) {
	fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_filename);
	return(-1);
    }

    /*
     * Check the document is of the right kind
     */
    if(xmlDocGetRootElement(doc) == NULL) {
        fprintf(stderr,"Error: empty document for file \"%s\"\n", xml_filename);
	xmlFreeDoc(doc);
	return(-1);
    }

    /*
     * load xpath file if specified
     */
    if(xpath_filename) {
	xpath = load_xpath_expr(doc, xpath_filename);
	if(xpath == NULL) {
	    fprintf(stderr,"Error: unable to evaluate xpath expression\n");
	    xmlFreeDoc(doc);
	    return(-1);
	}
    }

    if (ns_filename != NULL) {
        if (loadMem(ns_filename, &nslist, &nssize)) {
	    fprintf(stderr,"Error: unable to evaluate xpath expression\n");
	    if(xpath != NULL) xmlXPathFreeObject(xpath);
	    xmlFreeDoc(doc);
	    return(-1);
	}
        inclusive_namespaces = parse_list((xmlChar *) nslist);
    }

    /*
     * Canonical form
     */
    /* fprintf(stderr,"File \"%s\" loaded: start canonization\n", xml_filename); */
    ret = xmlC14NDocDumpMemory(doc,
	    (xpath) ? xpath->nodesetval : NULL,
	    mode, inclusive_namespaces,
	    with_comments, &result);
    if (ret >= 0) {
	if(result != NULL) {
	    if (compareFileMem(result_file, (const char *) result, ret)) {
		fprintf(stderr, "Result mismatch for %s\n", xml_filename);
		fprintf(stderr, "RESULT:\n%s\n", (const char*)result);
	        ret = -1;
	    }
	}
    } else {
	fprintf(stderr,"Error: failed to canonicalize XML file \"%s\" (ret=%d)\n", xml_filename, ret);
	ret = -1;
    }

    /*
     * Cleanup
     */
    if (result != NULL) xmlFree(result);
    if(xpath != NULL) xmlXPathFreeObject(xpath);
    if (inclusive_namespaces != NULL) xmlFree(inclusive_namespaces);
    if (nslist != NULL) free((char *) nslist);
    xmlFreeDoc(doc);

    return(ret);
}


notationDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
	     const xmlChar *publicId, const xmlChar *systemId)
{
    callbacks++;
    if (quiet)
	return;
    fprintf(SAXdebug, "SAX.notationDecl(%s, %s, %s)\n",
            (char *) name, (char *) publicId, (char *) systemId);
}


charactersDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
{
    char output[40];
    int i;

    callbacks++;
    if (quiet)
	return;
    for (i = 0;(i<len) && (i < 30);i++)
	output[i] = ch[i];
    output[i] = 0;

    fprintf(SAXdebug, "SAX.characters(%s, %d)\n", output, len);
}


xmlExpStringDerive(xmlExpCtxtPtr ctxt, xmlExpNodePtr exp,
                   const xmlChar *str, int len) {
    const xmlChar *input;

    if ((exp == NULL) || (ctxt == NULL) || (str == NULL)) {
        return(NULL);
    }
    /*
     * check the string is in the dictionary, if yes use an interned
     * copy, otherwise we know it's not an acceptable input
     */
    input = xmlDictExists(ctxt->dict, str, len);
    if (input == NULL) {
        return(forbiddenExp);
    }
    return(xmlExpStringDeriveInt(ctxt, exp, input));
}


xmlExpSubsume(xmlExpCtxtPtr ctxt, xmlExpNodePtr exp, xmlExpNodePtr sub) {
    xmlExpNodePtr tmp;

    if ((exp == NULL) || (ctxt == NULL) || (sub == NULL))
        return(-1);

    /*
     * TODO: speedup by checking the language of sub is a subset of the
     *       language of exp
     */
    /*
     * O(1) speedups
     */
    if (IS_NILLABLE(sub) && (!IS_NILLABLE(exp))) {
#ifdef DEBUG_DERIV
	printf("Sub nillable and not exp : can't subsume\n");
#endif
        return(0);
    }
    if (xmlExpCheckCard(exp, sub) == 0) {
#ifdef DEBUG_DERIV
	printf("sub generate longuer sequances than exp : can't subsume\n");
#endif
        return(0);
    }
    tmp = xmlExpExpDeriveInt(ctxt, exp, sub);
#ifdef DEBUG_DERIV
    printf("Result derivation :\n");
    PRINT_EXP(tmp);
#endif
    if (tmp == NULL)
        return(-1);
    if (tmp == forbiddenExp)
	return(0);
    if (tmp == emptyExp)
	return(1);
    if ((tmp != NULL) && (IS_NILLABLE(tmp))) {
        xmlExpFree(ctxt, tmp);
        return(1);
    }
    xmlExpFree(ctxt, tmp);
    return(0);
}


xmlRegAtomAddRange(xmlRegParserCtxtPtr ctxt, xmlRegAtomPtr atom,
	           int neg, xmlRegAtomType type, int start, int end,
		   xmlChar *blockName) {
    xmlRegRangePtr range;

    if (atom == NULL) {
	ERROR("add range: atom is NULL");
	return;
    }
    if (atom->type != XML_REGEXP_RANGES) {
	ERROR("add range: atom is not ranges");
	return;
    }
    if (atom->maxRanges == 0) {
	atom->maxRanges = 4;
	atom->ranges = (xmlRegRangePtr *) xmlMalloc(atom->maxRanges *
		                             sizeof(xmlRegRangePtr));
	if (atom->ranges == NULL) {
	    xmlRegexpErrMemory(ctxt, "adding ranges");
	    atom->maxRanges = 0;
	    return;
	}
    } else if (atom->nbRanges >= atom->maxRanges) {
	xmlRegRangePtr *tmp;
	atom->maxRanges *= 2;
	tmp = (xmlRegRangePtr *) xmlRealloc(atom->ranges, atom->maxRanges *
		                             sizeof(xmlRegRangePtr));
	if (tmp == NULL) {
	    xmlRegexpErrMemory(ctxt, "adding ranges");
	    atom->maxRanges /= 2;
	    return;
	}
	atom->ranges = tmp;
    }
    range = xmlRegNewRange(ctxt, neg, type, start, end);
    if (range == NULL)
	return;
    range->blockName = blockName;
    atom->ranges[atom->nbRanges++] = range;

}


xmlExpCtxtNbNodes(xmlExpCtxtPtr ctxt) {
    if (ctxt == NULL)
        return(-1);
    return(ctxt->nb_nodes);
}


xmlExpMaxToken(xmlExpNodePtr expr) {
    if (expr == NULL)
        return(-1);
    return(expr->c_max);
}


xmlAutomataNewNegTrans(xmlAutomataPtr am, xmlAutomataStatePtr from,
		       xmlAutomataStatePtr to, const xmlChar *token,
		       const xmlChar *token2, void *data) {
    xmlRegAtomPtr atom;
    xmlChar err_msg[200];

    if ((am == NULL) || (from == NULL) || (token == NULL))
	return(NULL);
    atom = xmlRegNewAtom(am, XML_REGEXP_STRING);
    if (atom == NULL)
	return(NULL);
    atom->data = data;
    atom->neg = 1;
    if ((token2 == NULL) || (*token2 == 0)) {
	atom->valuep = xmlStrdup(token);
    } else {
	int lenn, lenp;
	xmlChar *str;

	lenn = strlen((char *) token2);
	lenp = strlen((char *) token);

	str = (xmlChar *) xmlMallocAtomic(lenn + lenp + 2);
	if (str == NULL) {
	    xmlRegFreeAtom(atom);
	    return(NULL);
	}
	memcpy(&str[0], token, lenp);
	str[lenp] = '|';
	memcpy(&str[lenp + 1], token2, lenn);
	str[lenn + lenp + 1] = 0;

	atom->valuep = str;
    }
    snprintf((char *) err_msg, 199, "not %s", (const char *) atom->valuep);
    err_msg[199] = 0;
    atom->valuep2 = xmlStrdup(err_msg);

    if (xmlFAGenerateTransitions(am, from, to, atom) < 0) {
        xmlRegFreeAtom(atom);
	return(NULL);
    }
    am->negs++;
    if (to == NULL)
	return(am->state);
    return(to);
}


xmlAutomataNewOnceTrans2(xmlAutomataPtr am, xmlAutomataStatePtr from,
			 xmlAutomataStatePtr to, const xmlChar *token,
			 const xmlChar *token2,
			 int min, int max, void *data) {
    xmlRegAtomPtr atom;
    int counter;

    if ((am == NULL) || (from == NULL) || (token == NULL))
	return(NULL);
    if (min < 1)
	return(NULL);
    if ((max < min) || (max < 1))
	return(NULL);
    atom = xmlRegNewAtom(am, XML_REGEXP_STRING);
    if (atom == NULL)
	return(NULL);
    if ((token2 == NULL) || (*token2 == 0)) {
	atom->valuep = xmlStrdup(token);
    } else {
	int lenn, lenp;
	xmlChar *str;

	lenn = strlen((char *) token2);
	lenp = strlen((char *) token);

	str = (xmlChar *) xmlMallocAtomic(lenn + lenp + 2);
	if (str == NULL) {
	    xmlRegFreeAtom(atom);
	    return(NULL);
	}
	memcpy(&str[0], token, lenp);
	str[lenp] = '|';
	memcpy(&str[lenp + 1], token2, lenn);
	str[lenn + lenp + 1] = 0;

	atom->valuep = str;
    }
    atom->data = data;
    atom->quant = XML_REGEXP_QUANT_ONCEONLY;
    atom->min = min;
    atom->max = max;
    /*
     * associate a counter to the transition.
     */
    counter = xmlRegGetCounter(am);
    am->counters[counter].min = 1;
    am->counters[counter].max = 1;

    /* xmlFAGenerateTransitions(am, from, to, atom); */
    if (to == NULL) {
	to = xmlRegNewState(am);
	xmlRegStatePush(am, to);
    }
    xmlRegStateAddTrans(am, from, atom, to, counter, -1);
    xmlRegAtomPush(am, atom);
    am->state = to;
    return(to);
}


xmlRegPrintRange(FILE *output, xmlRegRangePtr range) {
    fprintf(output, "  range: ");
    if (range->neg)
	fprintf(output, "negative ");
    xmlRegPrintAtomType(output, range->type);
    fprintf(output, "%c - %c\n", range->start, range->end);
}


xmlRegPrintTrans(FILE *output, xmlRegTransPtr trans) {
    fprintf(output, "  trans: ");
    if (trans == NULL) {
	fprintf(output, "NULL\n");
	return;
    }
    if (trans->to < 0) {
	fprintf(output, "removed\n");
	return;
    }
    if (trans->nd != 0) {
	if (trans->nd == 2)
	    fprintf(output, "last not determinist, ");
	else
	    fprintf(output, "not determinist, ");
    }
    if (trans->counter >= 0) {
	fprintf(output, "counted %d, ", trans->counter);
    }
    if (trans->count == REGEXP_ALL_COUNTER) {
	fprintf(output, "all transition, ");
    } else if (trans->count >= 0) {
	fprintf(output, "count based %d, ", trans->count);
    }
    if (trans->atom == NULL) {
	fprintf(output, "epsilon to %d\n", trans->to);
	return;
    }
    if (trans->atom->type == XML_REGEXP_CHARVAL)
	fprintf(output, "char %c ", trans->atom->codepoint);
    fprintf(output, "atom %d, to %d\n", trans->atom->no, trans->to);
}


xmlRegFreeExecCtxt(xmlRegExecCtxtPtr exec) {
    if (exec == NULL)
	return;

    if (exec->rollbacks != NULL) {
	if (exec->counts != NULL) {
	    int i;

	    for (i = 0;i < exec->maxRollbacks;i++)
		if (exec->rollbacks[i].counts != NULL)
		    xmlFree(exec->rollbacks[i].counts);
	}
	xmlFree(exec->rollbacks);
    }
    if (exec->counts != NULL)
	xmlFree(exec->counts);
    if (exec->inputStack != NULL) {
	int i;

	for (i = 0;i < exec->inputStackNr;i++) {
	    if (exec->inputStack[i].value != NULL)
		xmlFree(exec->inputStack[i].value);
	}
	xmlFree(exec->inputStack);
    }
    if (exec->errString != NULL)
        xmlFree(exec->errString);
    xmlFree(exec);
}


xmlFARegDebugExec(xmlRegExecCtxtPtr exec) {
    printf("state: %d:%d:idx %d", exec->state->no, exec->transno, exec->index);
    if (exec->inputStack != NULL) {
	int i;
	printf(": ");
	for (i = 0;(i < 3) && (i < exec->inputStackNr);i++)
	    printf("%s ", (const char *)
	           exec->inputStack[exec->inputStackNr - (i + 1)].value);
    } else {
	printf(": %s", &(exec->inputString[exec->index]));
    }
    printf("\n");
}


xmlFAIsChar(xmlRegParserCtxtPtr ctxt) {
    int cur;
    int len;

    cur = CUR_SCHAR(ctxt->cur, len);
    if ((cur == '.') || (cur == '\\') || (cur == '?') ||
	(cur == '*') || (cur == '+') || (cur == '(') ||
	(cur == ')') || (cur == '|') || (cur == 0x5B) ||
	(cur == 0x5D) || (cur == 0))
	return(-1);
    return(cur);
}


xmlAutomataNewTransition(xmlAutomataPtr am, xmlAutomataStatePtr from,
			 xmlAutomataStatePtr to, const xmlChar *token,
			 void *data) {
    xmlRegAtomPtr atom;

    if ((am == NULL) || (from == NULL) || (token == NULL))
	return(NULL);
    atom = xmlRegNewAtom(am, XML_REGEXP_STRING);
    if (atom == NULL)
        return(NULL);
    atom->data = data;
    atom->valuep = xmlStrdup(token);

    if (xmlFAGenerateTransitions(am, from, to, atom) < 0) {
        xmlRegFreeAtom(atom);
	return(NULL);
    }
    if (to == NULL)
	return(am->state);
    return(to);
}


static double filter_bicubic(const double t)
{
	const double abs_t = (double)fabs(t);
	const double abs_t_sq = abs_t * abs_t;
	if (abs_t<1) return 1-2*abs_t_sq+abs_t_sq*abs_t;
	if (abs_t<2) return 4 - 8*abs_t +5*abs_t_sq - abs_t_sq*abs_t;
	return 0;
}


static inline LineContribType *_gdContributionsCalc(unsigned int line_size, unsigned int src_size, double scale_d,  const interpolation_method pFilter)
{
	double width_d;
	double scale_f_d = 1.0;
	const double filter_width_d = DEFAULT_BOX_RADIUS;
	int windows_size;
	unsigned int u;
	LineContribType *res;

	if (scale_d < 1.0) {
		width_d = filter_width_d / scale_d;
		scale_f_d = scale_d;
	}  else {
		width_d= filter_width_d;
	}

	windows_size = 2 * (int)ceil(width_d) + 1;
	res = _gdContributionsAlloc(line_size, windows_size);

	for (u = 0; u < line_size; u++) {
		const double dCenter = (double)u / scale_d;
		/* get the significant edge points affecting the pixel */
		register int iLeft = MAX(0, (int)floor (dCenter - width_d));
		int iRight = MIN((int)ceil(dCenter + width_d), (int)src_size - 1);
		double dTotalWeight = 0.0;
		int iSrc;

		/* Cut edge points to fit in filter window in case of spill-off */
		if (iRight - iLeft + 1 > windows_size)  {
			if (iLeft < ((int)src_size - 1 / 2))  {
				iLeft++;
			} else {
				iRight--;
			}
		}

		res->ContribRow[u].Left = iLeft;
		res->ContribRow[u].Right = iRight;

		for (iSrc = iLeft; iSrc <= iRight; iSrc++) {
			dTotalWeight += (res->ContribRow[u].Weights[iSrc-iLeft] =  scale_f_d * (*pFilter)(scale_f_d * (dCenter - (double)iSrc)));
		}

		if (dTotalWeight < 0.0) {
			_gdContributionsFree(res);
			return NULL;
		}

		if (dTotalWeight > 0.0) {
			for (iSrc = iLeft; iSrc <= iRight; iSrc++) {
				res->ContribRow[u].Weights[iSrc-iLeft] /= dTotalWeight;
			}
		}
	}
	return res;
}


BGD_DECLARE(int) gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox)
{
	gdPointF extent[4], min, max, point;
	int i;

	extent[0].x=0.0;
	extent[0].y=0.0;
	extent[1].x=(double) src->width;
	extent[1].y=0.0;
	extent[2].x=(double) src->width;
	extent[2].y=(double) src->height;
	extent[3].x=0.0;
	extent[3].y=(double) src->height;

	for (i=0; i < 4; i++) {
		point=extent[i];
		if (gdAffineApplyToPointF(&extent[i], &point, affine) != GD_TRUE) {
			return GD_FALSE;
		}
	}
	min=extent[0];
	max=extent[0];

	for (i=1; i < 4; i++) {
		if (min.x > extent[i].x)
			min.x=extent[i].x;
		if (min.y > extent[i].y)
			min.y=extent[i].y;
		if (max.x < extent[i].x)
			max.x=extent[i].x;
		if (max.y < extent[i].y)
			max.y=extent[i].y;
	}
	bbox->x = (int) min.x;
	bbox->y = (int) min.y;
	bbox->width  = (int) floor(max.x - min.x) - 1;
	bbox->height = (int) floor(max.y - min.y);
	return GD_TRUE;
}


PHP_FUNCTION(odbc_tableprivileges)
{
	zval *pv_conn;
	odbc_result   *result = NULL;
	odbc_connection *conn;
	char *cat = NULL, *schema = NULL, *table = NULL;
	int cat_len = 0, schema_len, table_len;
	RETCODE rc;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs!ss", &pv_conn, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) {
		return;
	}

	ZEND_FETCH_RESOURCE2(conn, odbc_connection *, &pv_conn, -1, "ODBC-Link", le_conn, le_pconn);

	result = (odbc_result *)ecalloc(1, sizeof(odbc_result));

	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
	if (rc == SQL_INVALID_HANDLE) {
		efree(result);
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
		RETURN_FALSE;
	}

	if (rc == SQL_ERROR) {
		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
		efree(result);
		RETURN_FALSE;
	}

	rc = SQLTablePrivileges(result->stmt,
			cat, SAFE_SQL_NTS(cat),
			schema, SAFE_SQL_NTS(schema),
			table, SAFE_SQL_NTS(table));

	if (rc == SQL_ERROR) {
		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLTablePrivileges");
		efree(result);
		RETURN_FALSE;
	}

	result->numparams = 0;
	SQLNumResultCols(result->stmt, &(result->numcols));

	if (result->numcols > 0) {
		if (!odbc_bindcols(result TSRMLS_CC)) {
			efree(result);
			RETURN_FALSE;
		}
	} else {
		result->values = NULL;
	}
	result->conn_ptr = conn;
	result->fetched = 0;
	ZEND_REGISTER_RESOURCE(return_value, result, le_result);
}


PHP_FUNCTION(odbc_columns)
{
	zval *pv_conn;
	odbc_result *result = NULL;
	odbc_connection *conn;
	char *cat = NULL, *schema = NULL, *table = NULL, *column = NULL;
	int cat_len = 0, schema_len = 0, table_len = 0, column_len = 0;
	RETCODE rc;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|s!sss", &pv_conn, &cat, &cat_len, &schema, &schema_len,
		&table, &table_len, &column, &column_len) == FAILURE) {
		return;
	}

	ZEND_FETCH_RESOURCE2(conn, odbc_connection *, &pv_conn, -1, "ODBC-Link", le_conn, le_pconn);

	result = (odbc_result *)ecalloc(1, sizeof(odbc_result));

	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
	if (rc == SQL_INVALID_HANDLE) {
		efree(result);
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
		RETURN_FALSE;
	}

	if (rc == SQL_ERROR) {
		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
		efree(result);
		RETURN_FALSE;
	}

	/*
	 * Needed to make MS Access happy
	 */
	if (table && table_len && schema && schema_len == 0) {
		schema = NULL;
	}

	rc = SQLColumns(result->stmt,
			cat, (SQLSMALLINT) cat_len,
			schema, (SQLSMALLINT) schema_len,
			table, (SQLSMALLINT) table_len,
			column, (SQLSMALLINT) column_len);

	if (rc == SQL_ERROR) {
		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLColumns");
		efree(result);
		RETURN_FALSE;
	}

	result->numparams = 0;
	SQLNumResultCols(result->stmt, &(result->numcols));

	if (result->numcols > 0) {
		if (!odbc_bindcols(result TSRMLS_CC)) {
			efree(result);
			RETURN_FALSE;
		}
	} else {
		result->values = NULL;
	}
	result->conn_ptr = conn;
	result->fetched = 0;
	ZEND_REGISTER_RESOURCE(return_value, result, le_result);
}


PHP_FUNCTION(odbc_primarykeys)
{
	zval *pv_conn;
	odbc_result   *result = NULL;
	odbc_connection *conn;
	char *cat = NULL, *schema = NULL, *table = NULL;
	int cat_len = 0, schema_len, table_len;
	RETCODE rc;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs!ss", &pv_conn, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) {
		return;
	}

	ZEND_FETCH_RESOURCE2(conn, odbc_connection *, &pv_conn, -1, "ODBC-Link", le_conn, le_pconn);

	result = (odbc_result *)ecalloc(1, sizeof(odbc_result));

	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
	if (rc == SQL_INVALID_HANDLE) {
		efree(result);
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
		RETURN_FALSE;
	}

	if (rc == SQL_ERROR) {
		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
		efree(result);
		RETURN_FALSE;
	}

	rc = SQLPrimaryKeys(result->stmt,
			cat, SAFE_SQL_NTS(cat),
			schema, SAFE_SQL_NTS(schema),
			table, SAFE_SQL_NTS(table) );

	if (rc == SQL_ERROR) {
		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLPrimaryKeys");
		efree(result);
		RETURN_FALSE;
	}

	result->numparams = 0;
	SQLNumResultCols(result->stmt, &(result->numcols));

	if (result->numcols > 0) {
		if (!odbc_bindcols(result TSRMLS_CC)) {
			efree(result);
			RETURN_FALSE;
		}
	} else {
		result->values = NULL;
	}
	result->conn_ptr = conn;
	result->fetched = 0;
	ZEND_REGISTER_RESOURCE(return_value, result, le_result);
}


PHP_FUNCTION(odbc_gettypeinfo)
{
	zval *pv_conn;
	long pv_data_type = SQL_ALL_TYPES;
	odbc_result *result = NULL;
	odbc_connection *conn;
	RETCODE rc;
	SQLSMALLINT data_type;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &pv_conn, &pv_data_type) == FAILURE) {
		return;
	}

	data_type = (SQLSMALLINT) pv_data_type;

	ZEND_FETCH_RESOURCE2(conn, odbc_connection *, &pv_conn, -1, "ODBC-Link", le_conn, le_pconn);

	result = (odbc_result *)ecalloc(1, sizeof(odbc_result));

	rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt));
	if (rc == SQL_INVALID_HANDLE) {
		efree(result);
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "SQLAllocStmt error 'Invalid Handle'");
		RETURN_FALSE;
	}

	if (rc == SQL_ERROR) {
		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt");
		efree(result);
		RETURN_FALSE;
	}

	rc = SQLGetTypeInfo(result->stmt, data_type );

	if (rc == SQL_ERROR) {
		odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLGetTypeInfo");
		efree(result);
		RETURN_FALSE;
	}

	result->numparams = 0;
	SQLNumResultCols(result->stmt, &(result->numcols));

	if (result->numcols > 0) {
		if (!odbc_bindcols(result TSRMLS_CC)) {
			efree(result);
			RETURN_FALSE;
		}
	} else {
		result->values = NULL;
	}
	result->conn_ptr = conn;
	result->fetched = 0;
	ZEND_REGISTER_RESOURCE(return_value, result, le_result);
}


static void add_prefix(smart_str* loc_name, char* key_name)
{
	if( strncmp(key_name , LOC_PRIVATE_TAG , 7) == 0 ){
		smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
		smart_str_appendl(loc_name, PRIVATE_PREFIX , sizeof(PRIVATE_PREFIX)-1);
	}
}


PHP_FUNCTION( locale_get_region )
{
	get_icu_value_src_php( LOC_REGION_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
}


static int append_multiple_key_values(smart_str* loc_name, HashTable* hash_arr, char* key_name TSRMLS_DC)
{
	zval**	ele_value    	= NULL;
	int 	i 		= 0;
	int 	isFirstSubtag 	= 0;
	int 	max_value 	= 0;

	/* Variant/ Extlang/Private etc. */
	if( zend_hash_find( hash_arr , key_name , strlen(key_name) + 1 ,(void **)&ele_value ) == SUCCESS ) {
		if( Z_TYPE_PP(ele_value) == IS_STRING ){
			add_prefix( loc_name , key_name);

			smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
			smart_str_appendl(loc_name, Z_STRVAL_PP(ele_value) , Z_STRLEN_PP(ele_value));
			return SUCCESS;
		} else if(Z_TYPE_PP(ele_value) == IS_ARRAY ) {
			HashPosition pos;
			HashTable *arr = HASH_OF(*ele_value);
			zval **data = NULL;

			zend_hash_internal_pointer_reset_ex(arr, &pos);
			while(zend_hash_get_current_data_ex(arr, (void **)&data, &pos) != FAILURE) {
				if(Z_TYPE_PP(data) != IS_STRING) {
					return FAILURE;
				}
				if (isFirstSubtag++ == 0){
					add_prefix(loc_name , key_name);
				}
				smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
				smart_str_appendl(loc_name, Z_STRVAL_PP(data) , Z_STRLEN_PP(data));
				zend_hash_move_forward_ex(arr, &pos);
			}
			return SUCCESS;
		} else {
			return FAILURE;
		}
	} else {
		char cur_key_name[31];
		/* Decide the max_value: the max. no. of elements allowed */
		if( strcmp(key_name , LOC_VARIANT_TAG) ==0 ){
			max_value  = MAX_NO_VARIANT;
		}
		if( strcmp(key_name , LOC_EXTLANG_TAG) ==0 ){
			max_value  = MAX_NO_EXTLANG;
		}
		if( strcmp(key_name , LOC_PRIVATE_TAG) ==0 ){
			max_value  = MAX_NO_PRIVATE;
		}

		/* Multiple variant values as variant0, variant1 ,variant2 */
		isFirstSubtag = 0;
		for( i=0 ; i< max_value; i++ ){
			snprintf( cur_key_name , 30, "%s%d", key_name , i);
			if( zend_hash_find( hash_arr , cur_key_name , strlen(cur_key_name) + 1,(void **)&ele_value ) == SUCCESS ){
				if( Z_TYPE_PP(ele_value)!= IS_STRING ){
					/* variant is not a string */
					return FAILURE;
				}
				/* Add the contents */
				if (isFirstSubtag++ == 0){
					add_prefix(loc_name , cur_key_name);
				}
				smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
				smart_str_appendl(loc_name, Z_STRVAL_PP(ele_value) , Z_STRLEN_PP(ele_value));
			}
		} /* end of for */
	} /* end of else */

	return SUCCESS;
}


PHP_FUNCTION(pclose)
{
	zval *arg1;
	php_stream *stream;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg1) == FAILURE) {
		RETURN_FALSE;
	}

	PHP_STREAM_TO_ZVAL(stream, &arg1);

	FG(pclose_wait) = 1;
	zend_list_delete(stream->rsrc_id);
	FG(pclose_wait) = 0;
	RETURN_LONG(FG(pclose_ret));
}


BGD_DECLARE(gdImagePtr) gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor)
{
	/* round to two decimals and keep the 100x multiplication to use it in the common square angles
	   case later. Keep the two decimal precisions so smaller rotation steps can be done, useful for
	   slow animations, f.e. */
	const int angle_rounded = fmod((int) floorf(angle * 100), 360 * 100);

	if (bgcolor < 0) {
		return NULL;
	}

	/* 0 && 90 degrees multiple rotation, 0 rotation simply clones the return image and convert it
	   to truecolor, as we must return truecolor image. */
	switch (angle_rounded) {
		case    0: {
			gdImagePtr dst = gdImageClone(src);

			if (dst == NULL) {
				return NULL;
			}
			if (dst->trueColor == 0) {
				gdImagePaletteToTrueColor(dst);
			}
			return dst;
		}

		case -2700:
		case   9000:
			return gdImageRotate90(src, 0);

		case -18000:
		case  18000:
			return gdImageRotate180(src, 0);

		case  -9000:
		case  27000:
			return gdImageRotate270(src, 0);
	}

	if (src == NULL || src->interpolation_id < 1 || src->interpolation_id > GD_METHOD_COUNT) {
		return NULL;
	}

	switch (src->interpolation_id) {
		case GD_NEAREST_NEIGHBOUR:
			return gdImageRotateNearestNeighbour(src, angle, bgcolor);
			break;

		case GD_BILINEAR_FIXED:
			return gdImageRotateBilinear(src, angle, bgcolor);
			break;

		case GD_BICUBIC_FIXED:
			return gdImageRotateBicubicFixed(src, angle, bgcolor);
			break;

		default:
			return gdImageRotateGeneric(src, angle, bgcolor);
	}
	return NULL;
}


PHP_FUNCTION(html_entity_decode)
{
	char *str, *hint_charset = NULL;
	int str_len, hint_charset_len = 0;
	size_t new_len = 0;
	long quote_style = ENT_COMPAT;
	char *replaced;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls", &str, &str_len,
							  &quote_style, &hint_charset, &hint_charset_len) == FAILURE) {
		return;
	}

	replaced = php_unescape_html_entities(str, str_len, &new_len, 1 /*all*/, quote_style, hint_charset TSRMLS_CC);
	if (replaced) {
		RETURN_STRINGL(replaced, (int)new_len, 0);
	}
	RETURN_FALSE;
}


static inline void find_entity_for_char_basic(
	unsigned int k,
	const entity_stage3_row *table,
	const unsigned char **entity,
	size_t *entity_len)
{
	if (k >= 64U) {
		*entity     = NULL;
		*entity_len = 0;
		return;
	}

	*entity     = table[k].data.ent.entity;
	*entity_len = table[k].data.ent.entity_len;
}


static inline size_t php_mb2_int_to_char(unsigned char *buf, unsigned k)
{
	assert(k <= 0xFFFFU);
	/* one or two bytes */
	if (k <= 0xFFU) { /* 1 */
		buf[0] = k;
		return 1U;
	} else { /* 2 */
		buf[0] = k >> 8;
		buf[1] = k & 0xFFU;
		return 2U;
	}
}


PHP_FUNCTION(filter_var)
{
	long filter = FILTER_DEFAULT;
	zval **filter_args = NULL, *data;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/|lZ", &data, &filter, &filter_args) == FAILURE) {
		return;
	}

	if (!PHP_FILTER_ID_EXISTS(filter)) {
		RETURN_FALSE;
	}

	MAKE_COPY_ZVAL(&data, return_value);

	php_filter_call(&return_value, filter, filter_args, 1, FILTER_REQUIRE_SCALAR TSRMLS_CC);
}


PHP_RSHUTDOWN_FUNCTION(filter)
{
	VAR_ARRAY_COPY_DTOR(get_array)
	VAR_ARRAY_COPY_DTOR(post_array)
	VAR_ARRAY_COPY_DTOR(cookie_array)
	VAR_ARRAY_COPY_DTOR(server_array)
	VAR_ARRAY_COPY_DTOR(env_array)
	VAR_ARRAY_COPY_DTOR(session_array)
	return SUCCESS;
}


static void php_zval_filter_recursive(zval **value, long filter, long flags, zval *options, char *charset, zend_bool copy TSRMLS_DC) /* {{{ */
{
	if (Z_TYPE_PP(value) == IS_ARRAY) {
		zval **element;
		HashPosition pos;

		if (Z_ARRVAL_PP(value)->nApplyCount > 1) {
			return;
		}

		for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(value), &pos);
			 zend_hash_get_current_data_ex(Z_ARRVAL_PP(value), (void **) &element, &pos) == SUCCESS;
			 zend_hash_move_forward_ex(Z_ARRVAL_PP(value), &pos)
		) {
			SEPARATE_ZVAL_IF_NOT_REF(element);
			if (Z_TYPE_PP(element) == IS_ARRAY) {
				Z_ARRVAL_PP(element)->nApplyCount++;
				php_zval_filter_recursive(element, filter, flags, options, charset, copy TSRMLS_CC);
				Z_ARRVAL_PP(element)->nApplyCount--;
			} else {
				php_zval_filter(element, filter, flags, options, charset, copy TSRMLS_CC);
			}
		}
	} else {
		php_zval_filter(value, filter, flags, options, charset, copy TSRMLS_CC);
	}
}


PHP_FUNCTION(filter_input_array)
{
	long    fetch_from;
	zval   *array_input = NULL, **op = NULL;
	zend_bool add_empty = 1;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|Zb",  &fetch_from, &op, &add_empty) == FAILURE) {
		return;
	}

	if (op
		&& (Z_TYPE_PP(op) != IS_ARRAY)
		&& (Z_TYPE_PP(op) == IS_LONG && !PHP_FILTER_ID_EXISTS(Z_LVAL_PP(op)))
		) {
		RETURN_FALSE;
	}

	array_input = php_filter_get_storage(fetch_from TSRMLS_CC);

	if (!array_input || !HASH_OF(array_input)) {
		long filter_flags = 0;
		zval **option;
		if (op) {
			if (Z_TYPE_PP(op) == IS_LONG) {
				filter_flags = Z_LVAL_PP(op);
			} else if (Z_TYPE_PP(op) == IS_ARRAY && zend_hash_find(HASH_OF(*op), "flags", sizeof("flags"), (void **)&option) == SUCCESS) {
				PHP_FILTER_GET_LONG_OPT(option, filter_flags);
			}
		}

		/* The FILTER_NULL_ON_FAILURE flag inverts the usual return values of
		 * the function: normally when validation fails false is returned, and
		 * when the input value doesn't exist NULL is returned. With the flag
		 * set, NULL and false should be returned, respectively. Ergo, although
		 * the code below looks incorrect, it's actually right. */
		if (filter_flags & FILTER_NULL_ON_FAILURE) {
			RETURN_FALSE;
		} else {
			RETURN_NULL();
		}
	}

	php_filter_array_handler(array_input, op, return_value, add_empty TSRMLS_CC);
}


PHP_MINFO_FUNCTION(filter)
{
	php_info_print_table_start();
	php_info_print_table_row( 2, "Input Validation and Filtering", "enabled" );
	php_info_print_table_row( 2, "Revision", "$Id$");
	php_info_print_table_end();

	DISPLAY_INI_ENTRIES();
}