libxml-libxml-perl/xpath.c

407 lines
12 KiB
C
Raw Normal View History

2022-09-27 15:00:17 +08:00
/* $Id$
*
* This is free software, you may use it and distribute it under the same terms as
* Perl itself.
*
* Copyright 2001-2003 AxKit.com Ltd., 2002-2006 Christian Glahn, 2006-2009 Petr Pajas
*/
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxml/uri.h>
#include "EXTERN.h"
#include "dom.h"
#include "xpath.h"
void
perlDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs){
xmlXPathObjectPtr obj = NULL, obj2 = NULL;
xmlChar *base = NULL, *URI = NULL;
if ((nargs < 1) || (nargs > 2)) {
ctxt->error = XPATH_INVALID_ARITY;
return;
}
if (ctxt->value == NULL) {
ctxt->error = XPATH_INVALID_TYPE;
return;
}
if (nargs == 2) {
if (ctxt->value->type != XPATH_NODESET) {
ctxt->error = XPATH_INVALID_TYPE;
return;
}
obj2 = valuePop(ctxt);
}
/* first assure the XML::LibXML error handler is deactivated
otherwise strange things might happen
*/
if (ctxt->value->type == XPATH_NODESET) {
int i;
xmlXPathObjectPtr newobj, ret;
obj = valuePop(ctxt);
ret = xmlXPathNewNodeSet(NULL);
if (obj->nodesetval) {
for (i = 0; i < obj->nodesetval->nodeNr; i++) {
valuePush(ctxt,
xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
xmlXPathStringFunction(ctxt, 1);
if (nargs == 2) {
valuePush(ctxt, xmlXPathObjectCopy(obj2));
} else {
valuePush(ctxt,
xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
}
perlDocumentFunction(ctxt, 2);
newobj = valuePop(ctxt);
ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
newobj->nodesetval);
xmlXPathFreeObject(newobj);
}
}
xmlXPathFreeObject(obj);
if (obj2 != NULL)
xmlXPathFreeObject(obj2);
valuePush(ctxt, ret);
/* reset the error old error handler before leaving
*/
return;
}
/*
* Make sure it's converted to a string
*/
xmlXPathStringFunction(ctxt, 1);
if (ctxt->value->type != XPATH_STRING) {
ctxt->error = XPATH_INVALID_TYPE;
if (obj2 != NULL)
xmlXPathFreeObject(obj2);
/* reset the error old error handler before leaving
*/
return;
}
obj = valuePop(ctxt);
if (obj->stringval == NULL) {
valuePush(ctxt, xmlXPathNewNodeSet(NULL));
} else {
if ((obj2 != NULL) && (obj2->nodesetval != NULL) &&
(obj2->nodesetval->nodeNr > 0)) {
xmlNodePtr target;
target = obj2->nodesetval->nodeTab[0];
if (target->type == XML_ATTRIBUTE_NODE) {
target = ((xmlAttrPtr) target)->parent;
}
base = xmlNodeGetBase(target->doc, target);
} else {
base = xmlNodeGetBase(ctxt->context->node->doc, ctxt->context->node);
}
URI = xmlBuildURI(obj->stringval, base);
if (base != NULL)
xmlFree(base);
if (URI == NULL) {
valuePush(ctxt, xmlXPathNewNodeSet(NULL));
} else {
if (xmlStrEqual(ctxt->context->node->doc->URL, URI)) {
valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr)ctxt->context->node->doc));
}
else {
xmlDocPtr doc;
doc = xmlParseFile((const char *)URI);
if (doc == NULL)
valuePush(ctxt, xmlXPathNewNodeSet(NULL));
else {
/* TODO: use XPointer of HTML location for fragment ID */
/* pbm #xxx can lead to location sets, not nodesets :-) */
valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc));
}
}
xmlFree(URI);
}
}
xmlXPathFreeObject(obj);
if (obj2 != NULL)
xmlXPathFreeObject(obj2);
/* reset the error old error handler before leaving
*/
}
/**
* Most of the code is stolen from testXPath.
* The almost only thing I added, is the storeing of the data, so
* we can access the data easily - or say more easiely than through
* libxml2.
**/
xmlXPathObjectPtr
domXPathFind( xmlNodePtr refNode, xmlChar * path, int to_bool ) {
xmlXPathObjectPtr res = NULL;
xmlXPathCompExprPtr comp;
comp = xmlXPathCompile( path );
if ( comp == NULL ) {
return NULL;
}
res = domXPathCompFind(refNode,comp,to_bool);
xmlXPathFreeCompExpr(comp);
return res;
}
xmlXPathObjectPtr
domXPathCompFind( xmlNodePtr refNode, xmlXPathCompExprPtr comp, int to_bool ) {
xmlXPathObjectPtr res = NULL;
if ( refNode != NULL && comp != NULL ) {
xmlXPathContextPtr ctxt;
xmlDocPtr tdoc = NULL;
xmlNodePtr froot = refNode;
if ( comp == NULL ) {
return NULL;
}
if ( refNode->doc == NULL ) {
/* if one XPaths a node from a fragment, libxml2 will
refuse the lookup. this is not very useful for XML
scripters. thus we need to create a temporary document
to make libxml2 do it's job correctly.
*/
tdoc = xmlNewDoc( NULL );
/* find refnode's root node */
while ( froot != NULL ) {
if ( froot->parent == NULL ) {
break;
}
froot = froot->parent;
}
xmlAddChild((xmlNodePtr)tdoc, froot);
xmlSetTreeDoc(froot, tdoc); /* probably no need to clean psvi */
froot->doc = tdoc;
/* refNode->doc = tdoc; */
}
/* prepare the xpath context */
ctxt = xmlXPathNewContext( refNode->doc );
ctxt->node = refNode;
/* get the namespace information */
if (refNode->type == XML_DOCUMENT_NODE) {
ctxt->namespaces = xmlGetNsList( refNode->doc,
xmlDocGetRootElement( refNode->doc ) );
}
else {
ctxt->namespaces = xmlGetNsList(refNode->doc, refNode);
}
ctxt->nsNr = 0;
if (ctxt->namespaces != NULL) {
while (ctxt->namespaces[ctxt->nsNr] != NULL)
ctxt->nsNr++;
}
xmlXPathRegisterFunc(ctxt,
(const xmlChar *) "document",
perlDocumentFunction);
if (to_bool) {
#if LIBXML_VERSION >= 20627
int val = xmlXPathCompiledEvalToBoolean(comp, ctxt);
res = xmlXPathNewBoolean(val);
#else
res = xmlXPathCompiledEval(comp, ctxt);
if (res!=NULL) {
int val = xmlXPathCastToBoolean(res);
xmlXPathFreeObject(res);
res = xmlXPathNewBoolean(val);
}
#endif
} else {
res = xmlXPathCompiledEval(comp, ctxt);
}
if (ctxt->namespaces != NULL) {
xmlFree( ctxt->namespaces );
}
xmlXPathFreeContext(ctxt);
if ( tdoc != NULL ) {
/* after looking through a fragment, we need to drop the
fake document again */
xmlSetTreeDoc(froot, NULL); /* probably no need to clean psvi */
froot->doc = NULL;
froot->parent = NULL;
tdoc->children = NULL;
tdoc->last = NULL;
/* next line is not required anymore */
/* refNode->doc = NULL; */
xmlFreeDoc( tdoc );
}
}
return res;
}
/* this function is not actually used: */
xmlNodeSetPtr
domXPathSelect( xmlNodePtr refNode, xmlChar * path ) {
xmlNodeSetPtr rv = NULL;
xmlXPathObjectPtr res = NULL;
res = domXPathFind( refNode, path, 0 );
if (res != NULL) {
/* here we have to transfer the result from the internal
structure to the return value */
/* get the result from the query */
/* we have to unbind the nodelist, so free object can
not kill it */
rv = res->nodesetval;
res->nodesetval = 0 ;
}
xmlXPathFreeObject(res);
return rv;
}
/* this function is not actually used: */
xmlNodeSetPtr
domXPathCompSelect( xmlNodePtr refNode, xmlXPathCompExprPtr comp ) {
xmlNodeSetPtr rv = NULL;
xmlXPathObjectPtr res = NULL;
res = domXPathCompFind( refNode, comp, 0 );
if (res != NULL) {
/* here we have to transfer the result from the internal
structure to the return value */
/* get the result from the query */
/* we have to unbind the nodelist, so free object can
not kill it */
rv = res->nodesetval;
res->nodesetval = 0 ;
}
xmlXPathFreeObject(res);
return rv;
}
/**
* Most of the code is stolen from testXPath.
* The almost only thing I added, is the storeing of the data, so
* we can access the data easily - or say more easiely than through
* libxml2.
**/
xmlXPathObjectPtr
domXPathFindCtxt( xmlXPathContextPtr ctxt, xmlChar * path, int to_bool ) {
xmlXPathObjectPtr res = NULL;
if ( ctxt->node != NULL && path != NULL ) {
xmlXPathCompExprPtr comp;
comp = xmlXPathCompile( path );
if ( comp == NULL ) {
return NULL;
}
res = domXPathCompFindCtxt(ctxt,comp,to_bool);
xmlXPathFreeCompExpr(comp);
}
return res;
}
xmlXPathObjectPtr
domXPathCompFindCtxt( xmlXPathContextPtr ctxt, xmlXPathCompExprPtr comp, int to_bool ) {
xmlXPathObjectPtr res = NULL;
if ( ctxt != NULL && ctxt->node != NULL && comp != NULL ) {
xmlDocPtr tdoc = NULL;
xmlNodePtr froot = ctxt->node;
if ( ctxt->node->doc == NULL ) {
/* if one XPaths a node from a fragment, libxml2 will
refuse the lookup. this is not very useful for XML
scripters. thus we need to create a temporary document
to make libxml2 do it's job correctly.
*/
tdoc = xmlNewDoc( NULL );
/* find refnode's root node */
while ( froot != NULL ) {
if ( froot->parent == NULL ) {
break;
}
froot = froot->parent;
}
xmlAddChild((xmlNodePtr)tdoc, froot);
xmlSetTreeDoc(froot,tdoc); /* probably no need to clean psvi */
froot->doc = tdoc;
/* ctxt->node->doc = tdoc; */
}
if (to_bool) {
#if LIBXML_VERSION >= 20627
int val = xmlXPathCompiledEvalToBoolean(comp, ctxt);
res = xmlXPathNewBoolean(val);
#else
res = xmlXPathCompiledEval(comp, ctxt);
if (res!=NULL) {
int val = xmlXPathCastToBoolean(res);
xmlXPathFreeObject(res);
res = xmlXPathNewBoolean(val);
}
#endif
} else {
res = xmlXPathCompiledEval(comp, ctxt);
}
if ( tdoc != NULL ) {
/* after looking through a fragment, we need to drop the
fake document again */
xmlSetTreeDoc(froot,NULL); /* probably no need to clean psvi */
froot->doc = NULL;
froot->parent = NULL;
tdoc->children = NULL;
tdoc->last = NULL;
if (ctxt->node) {
ctxt->node->doc = NULL;
}
xmlFreeDoc( tdoc );
}
}
return res;
}
xmlNodeSetPtr
domXPathSelectCtxt( xmlXPathContextPtr ctxt, xmlChar * path ) {
xmlNodeSetPtr rv = NULL;
xmlXPathObjectPtr res = NULL;
res = domXPathFindCtxt( ctxt, path, 0 );
if (res != NULL) {
/* here we have to transfer the result from the internal
structure to the return value */
/* get the result from the query */
/* we have to unbind the nodelist, so free object can
not kill it */
rv = res->nodesetval;
res->nodesetval = 0 ;
}
xmlXPathFreeObject(res);
return rv;
}