dhodovsk / source-git / pacemaker

Forked from source-git/pacemaker 3 years ago
Clone

Blame lib/common/xpath.c

rpm-build 3ee90c
/*
rpm-build 3ee90c
 * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
rpm-build 3ee90c
 *
rpm-build 3ee90c
 * This library is free software; you can redistribute it and/or
rpm-build 3ee90c
 * modify it under the terms of the GNU Lesser General Public
rpm-build 3ee90c
 * License as published by the Free Software Foundation; either
rpm-build 3ee90c
 * version 2.1 of the License, or (at your option) any later version.
rpm-build 3ee90c
 *
rpm-build 3ee90c
 * This library is distributed in the hope that it will be useful,
rpm-build 3ee90c
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
rpm-build 3ee90c
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
rpm-build 3ee90c
 * Lesser General Public License for more details.
rpm-build 3ee90c
 *
rpm-build 3ee90c
 * You should have received a copy of the GNU Lesser General Public
rpm-build 3ee90c
 * License along with this library; if not, write to the Free Software
rpm-build 3ee90c
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
rpm-build 3ee90c
 */
rpm-build 3ee90c
rpm-build 3ee90c
#include <crm_internal.h>
rpm-build 3ee90c
#include <stdio.h>
rpm-build 3ee90c
#include <string.h>
rpm-build 3ee90c
rpm-build 3ee90c
/*
rpm-build 3ee90c
 * From xpath2.c
rpm-build 3ee90c
 *
rpm-build 3ee90c
 * All the elements returned by an XPath query are pointers to
rpm-build 3ee90c
 * elements from the tree *except* namespace nodes where the XPath
rpm-build 3ee90c
 * semantic is different from the implementation in libxml2 tree.
rpm-build 3ee90c
 * As a result when a returned node set is freed when
rpm-build 3ee90c
 * xmlXPathFreeObject() is called, that routine must check the
rpm-build 3ee90c
 * element type. But node from the returned set may have been removed
rpm-build 3ee90c
 * by xmlNodeSetContent() resulting in access to freed data.
rpm-build 3ee90c
 *
rpm-build 3ee90c
 * This can be exercised by running
rpm-build 3ee90c
 *       valgrind xpath2 test3.xml '//discarded' discarded
rpm-build 3ee90c
 *
rpm-build 3ee90c
 * There is 2 ways around it:
rpm-build 3ee90c
 *   - make a copy of the pointers to the nodes from the result set
rpm-build 3ee90c
 *     then call xmlXPathFreeObject() and then modify the nodes
rpm-build 3ee90c
 * or
rpm-build 3ee90c
 * - remove the references from the node set, if they are not
rpm-build 3ee90c
       namespace nodes, before calling xmlXPathFreeObject().
rpm-build 3ee90c
 */
rpm-build 3ee90c
void
rpm-build 3ee90c
freeXpathObject(xmlXPathObjectPtr xpathObj)
rpm-build 3ee90c
{
rpm-build 3ee90c
    int lpc, max = numXpathResults(xpathObj);
rpm-build 3ee90c
rpm-build 3ee90c
    if (xpathObj == NULL) {
rpm-build 3ee90c
        return;
rpm-build 3ee90c
    }
rpm-build 3ee90c
rpm-build 3ee90c
    for (lpc = 0; lpc < max; lpc++) {
rpm-build 3ee90c
        if (xpathObj->nodesetval->nodeTab[lpc] && xpathObj->nodesetval->nodeTab[lpc]->type != XML_NAMESPACE_DECL) {
rpm-build 3ee90c
            xpathObj->nodesetval->nodeTab[lpc] = NULL;
rpm-build 3ee90c
        }
rpm-build 3ee90c
    }
rpm-build 3ee90c
rpm-build 3ee90c
    /* _Now_ it's safe to free it */
rpm-build 3ee90c
    xmlXPathFreeObject(xpathObj);
rpm-build 3ee90c
}
rpm-build 3ee90c
rpm-build 3ee90c
xmlNode *
rpm-build 3ee90c
getXpathResult(xmlXPathObjectPtr xpathObj, int index)
rpm-build 3ee90c
{
rpm-build 3ee90c
    xmlNode *match = NULL;
rpm-build 3ee90c
    int max = numXpathResults(xpathObj);
rpm-build 3ee90c
rpm-build 3ee90c
    CRM_CHECK(index >= 0, return NULL);
rpm-build 3ee90c
    CRM_CHECK(xpathObj != NULL, return NULL);
rpm-build 3ee90c
rpm-build 3ee90c
    if (index >= max) {
rpm-build 3ee90c
        crm_err("Requested index %d of only %d items", index, max);
rpm-build 3ee90c
        return NULL;
rpm-build 3ee90c
rpm-build 3ee90c
    } else if(xpathObj->nodesetval->nodeTab[index] == NULL) {
rpm-build 3ee90c
        /* Previously requested */
rpm-build 3ee90c
        return NULL;
rpm-build 3ee90c
    }
rpm-build 3ee90c
rpm-build 3ee90c
    match = xpathObj->nodesetval->nodeTab[index];
rpm-build 3ee90c
    CRM_CHECK(match != NULL, return NULL);
rpm-build 3ee90c
rpm-build 3ee90c
    if (xpathObj->nodesetval->nodeTab[index]->type != XML_NAMESPACE_DECL) {
rpm-build 3ee90c
        /* See the comment for freeXpathObject() */
rpm-build 3ee90c
        xpathObj->nodesetval->nodeTab[index] = NULL;
rpm-build 3ee90c
    }
rpm-build 3ee90c
rpm-build 3ee90c
    if (match->type == XML_DOCUMENT_NODE) {
rpm-build 3ee90c
        /* Will happen if section = '/' */
rpm-build 3ee90c
        match = match->children;
rpm-build 3ee90c
rpm-build 3ee90c
    } else if (match->type != XML_ELEMENT_NODE
rpm-build 3ee90c
               && match->parent && match->parent->type == XML_ELEMENT_NODE) {
rpm-build 3ee90c
        /* Return the parent instead */
rpm-build 3ee90c
        match = match->parent;
rpm-build 3ee90c
rpm-build 3ee90c
    } else if (match->type != XML_ELEMENT_NODE) {
rpm-build 3ee90c
        /* We only support searching nodes */
rpm-build 3ee90c
        crm_err("We only support %d not %d", XML_ELEMENT_NODE, match->type);
rpm-build 3ee90c
        match = NULL;
rpm-build 3ee90c
    }
rpm-build 3ee90c
    return match;
rpm-build 3ee90c
}
rpm-build 3ee90c
rpm-build 3ee90c
void
rpm-build 3ee90c
dedupXpathResults(xmlXPathObjectPtr xpathObj)
rpm-build 3ee90c
{
rpm-build 3ee90c
    int lpc, max = numXpathResults(xpathObj);
rpm-build 3ee90c
rpm-build 3ee90c
    if (xpathObj == NULL) {
rpm-build 3ee90c
        return;
rpm-build 3ee90c
    }
rpm-build 3ee90c
rpm-build 3ee90c
    for (lpc = 0; lpc < max; lpc++) {
rpm-build 3ee90c
        xmlNode *xml = NULL;
rpm-build 3ee90c
        gboolean dedup = FALSE;
rpm-build 3ee90c
rpm-build 3ee90c
        if (xpathObj->nodesetval->nodeTab[lpc] == NULL) {
rpm-build 3ee90c
            continue;
rpm-build 3ee90c
        }
rpm-build 3ee90c
rpm-build 3ee90c
        xml = xpathObj->nodesetval->nodeTab[lpc]->parent;
rpm-build 3ee90c
rpm-build 3ee90c
        for (; xml; xml = xml->parent) {
rpm-build 3ee90c
            int lpc2 = 0;
rpm-build 3ee90c
rpm-build 3ee90c
            for (lpc2 = 0; lpc2 < max; lpc2++) {
rpm-build 3ee90c
                if (xpathObj->nodesetval->nodeTab[lpc2] == xml) {
rpm-build 3ee90c
                    xpathObj->nodesetval->nodeTab[lpc] = NULL;
rpm-build 3ee90c
                    dedup = TRUE;
rpm-build 3ee90c
                    break;
rpm-build 3ee90c
                }
rpm-build 3ee90c
            }
rpm-build 3ee90c
rpm-build 3ee90c
            if (dedup) {
rpm-build 3ee90c
                break;
rpm-build 3ee90c
            }
rpm-build 3ee90c
        }
rpm-build 3ee90c
    }
rpm-build 3ee90c
}
rpm-build 3ee90c
rpm-build 3ee90c
/* the caller needs to check if the result contains a xmlDocPtr or xmlNodePtr */
rpm-build 3ee90c
xmlXPathObjectPtr
rpm-build 3ee90c
xpath_search(xmlNode * xml_top, const char *path)
rpm-build 3ee90c
{
rpm-build 3ee90c
    xmlDocPtr doc = NULL;
rpm-build 3ee90c
    xmlXPathObjectPtr xpathObj = NULL;
rpm-build 3ee90c
    xmlXPathContextPtr xpathCtx = NULL;
rpm-build 3ee90c
    const xmlChar *xpathExpr = (pcmkXmlStr) path;
rpm-build 3ee90c
rpm-build 3ee90c
    CRM_CHECK(path != NULL, return NULL);
rpm-build 3ee90c
    CRM_CHECK(xml_top != NULL, return NULL);
rpm-build 3ee90c
    CRM_CHECK(strlen(path) > 0, return NULL);
rpm-build 3ee90c
rpm-build 3ee90c
    doc = getDocPtr(xml_top);
rpm-build 3ee90c
rpm-build 3ee90c
    xpathCtx = xmlXPathNewContext(doc);
rpm-build 3ee90c
    CRM_ASSERT(xpathCtx != NULL);
rpm-build 3ee90c
rpm-build 3ee90c
    xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
rpm-build 3ee90c
    xmlXPathFreeContext(xpathCtx);
rpm-build 3ee90c
    return xpathObj;
rpm-build 3ee90c
}
rpm-build 3ee90c
rpm-build 3ee90c
/*!
rpm-build 3ee90c
 * \brief Run a supplied function for each result of an xpath search
rpm-build 3ee90c
 *
rpm-build 3ee90c
 * \param[in] xml            XML to search
rpm-build 3ee90c
 * \param[in] xpath          XPath search string
rpm-build 3ee90c
 * \param[in] helper         Function to call for each result
rpm-build 3ee90c
 * \param[in,out] user_data  Data to pass to supplied function
rpm-build 3ee90c
 *
rpm-build 3ee90c
 * \note The helper function will be passed the XML node of the result,
rpm-build 3ee90c
 *       and the supplied user_data. This function does not otherwise
rpm-build 3ee90c
 *       use user_data.
rpm-build 3ee90c
 */
rpm-build 3ee90c
void
rpm-build 3ee90c
crm_foreach_xpath_result(xmlNode *xml, const char *xpath,
rpm-build 3ee90c
                         void (*helper)(xmlNode*, void*), void *user_data)
rpm-build 3ee90c
{
rpm-build 3ee90c
    xmlXPathObjectPtr xpathObj = xpath_search(xml, xpath);
rpm-build 3ee90c
    int nresults = numXpathResults(xpathObj);
rpm-build 3ee90c
    int i;
rpm-build 3ee90c
rpm-build 3ee90c
    for (i = 0; i < nresults; i++) {
rpm-build 3ee90c
        xmlNode *result = getXpathResult(xpathObj, i);
rpm-build 3ee90c
rpm-build 3ee90c
        CRM_LOG_ASSERT(result != NULL);
rpm-build 3ee90c
        if (result) {
rpm-build 3ee90c
            (*helper)(result, user_data);
rpm-build 3ee90c
        }
rpm-build 3ee90c
    }
rpm-build 3ee90c
    freeXpathObject(xpathObj);
rpm-build 3ee90c
}
rpm-build 3ee90c
rpm-build 3ee90c
xmlNode *
rpm-build 3ee90c
get_xpath_object_relative(const char *xpath, xmlNode * xml_obj, int error_level)
rpm-build 3ee90c
{
rpm-build 3ee90c
    xmlNode *result = NULL;
rpm-build 3ee90c
    char *xpath_full = NULL;
rpm-build 3ee90c
    char *xpath_prefix = NULL;
rpm-build 3ee90c
rpm-build 3ee90c
    if (xml_obj == NULL || xpath == NULL) {
rpm-build 3ee90c
        return NULL;
rpm-build 3ee90c
    }
rpm-build 3ee90c
rpm-build 3ee90c
    xpath_prefix = (char *)xmlGetNodePath(xml_obj);
rpm-build 3ee90c
rpm-build 3ee90c
    xpath_full = crm_strdup_printf("%s%s", xpath_prefix, xpath);
rpm-build 3ee90c
rpm-build 3ee90c
    result = get_xpath_object(xpath_full, xml_obj, error_level);
rpm-build 3ee90c
rpm-build 3ee90c
    free(xpath_prefix);
rpm-build 3ee90c
    free(xpath_full);
rpm-build 3ee90c
    return result;
rpm-build 3ee90c
}
rpm-build 3ee90c
rpm-build 3ee90c
xmlNode *
rpm-build 3ee90c
get_xpath_object(const char *xpath, xmlNode * xml_obj, int error_level)
rpm-build 3ee90c
{
rpm-build 3ee90c
    int max;
rpm-build 3ee90c
    xmlNode *result = NULL;
rpm-build 3ee90c
    xmlXPathObjectPtr xpathObj = NULL;
rpm-build 3ee90c
    char *nodePath = NULL;
rpm-build 3ee90c
    char *matchNodePath = NULL;
rpm-build 3ee90c
rpm-build 3ee90c
    if (xpath == NULL) {
rpm-build 3ee90c
        return xml_obj;         /* or return NULL? */
rpm-build 3ee90c
    }
rpm-build 3ee90c
rpm-build 3ee90c
    xpathObj = xpath_search(xml_obj, xpath);
rpm-build 3ee90c
    nodePath = (char *)xmlGetNodePath(xml_obj);
rpm-build 3ee90c
    max = numXpathResults(xpathObj);
rpm-build 3ee90c
rpm-build 3ee90c
    if (max < 1) {
rpm-build 3ee90c
        do_crm_log(error_level, "No match for %s in %s", xpath, crm_str(nodePath));
rpm-build 3ee90c
        crm_log_xml_explicit(xml_obj, "Unexpected Input");
rpm-build 3ee90c
rpm-build 3ee90c
    } else if (max > 1) {
rpm-build 3ee90c
        int lpc = 0;
rpm-build 3ee90c
rpm-build 3ee90c
        do_crm_log(error_level, "Too many matches for %s in %s", xpath, crm_str(nodePath));
rpm-build 3ee90c
rpm-build 3ee90c
        for (lpc = 0; lpc < max; lpc++) {
rpm-build 3ee90c
            xmlNode *match = getXpathResult(xpathObj, lpc);
rpm-build 3ee90c
rpm-build 3ee90c
            CRM_LOG_ASSERT(match != NULL);
rpm-build 3ee90c
            if(match != NULL) {
rpm-build 3ee90c
                matchNodePath = (char *)xmlGetNodePath(match);
rpm-build 3ee90c
                do_crm_log(error_level, "%s[%d] = %s", xpath, lpc, crm_str(matchNodePath));
rpm-build 3ee90c
                free(matchNodePath);
rpm-build 3ee90c
            }
rpm-build 3ee90c
        }
rpm-build 3ee90c
        crm_log_xml_explicit(xml_obj, "Bad Input");
rpm-build 3ee90c
rpm-build 3ee90c
    } else {
rpm-build 3ee90c
        result = getXpathResult(xpathObj, 0);
rpm-build 3ee90c
    }
rpm-build 3ee90c
rpm-build 3ee90c
    freeXpathObject(xpathObj);
rpm-build 3ee90c
    free(nodePath);
rpm-build 3ee90c
rpm-build 3ee90c
    return result;
rpm-build 3ee90c
}