root/lib/common/xpath.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. freeXpathObject
  2. getXpathResult
  3. dedupXpathResults
  4. xpath_search
  5. crm_foreach_xpath_result
  6. get_xpath_object_relative
  7. get_xpath_object

   1 /*
   2  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
   3  *
   4  * This library is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2.1 of the License, or (at your option) any later version.
   8  *
   9  * This library is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with this library; if not, write to the Free Software
  16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17  */
  18 
  19 #include <crm_internal.h>
  20 #include <stdio.h>
  21 #include <string.h>
  22 
  23 /*
  24  * From xpath2.c
  25  *
  26  * All the elements returned by an XPath query are pointers to
  27  * elements from the tree *except* namespace nodes where the XPath
  28  * semantic is different from the implementation in libxml2 tree.
  29  * As a result when a returned node set is freed when
  30  * xmlXPathFreeObject() is called, that routine must check the
  31  * element type. But node from the returned set may have been removed
  32  * by xmlNodeSetContent() resulting in access to freed data.
  33  *
  34  * This can be exercised by running
  35  *       valgrind xpath2 test3.xml '//discarded' discarded
  36  *
  37  * There is 2 ways around it:
  38  *   - make a copy of the pointers to the nodes from the result set
  39  *     then call xmlXPathFreeObject() and then modify the nodes
  40  * or
  41  * - remove the references from the node set, if they are not
  42        namespace nodes, before calling xmlXPathFreeObject().
  43  */
  44 void
  45 freeXpathObject(xmlXPathObjectPtr xpathObj)
     /* [previous][next][first][last][top][bottom][index][help] */
  46 {
  47     int lpc, max = numXpathResults(xpathObj);
  48 
  49     if (xpathObj == NULL) {
  50         return;
  51     }
  52 
  53     for (lpc = 0; lpc < max; lpc++) {
  54         if (xpathObj->nodesetval->nodeTab[lpc] && xpathObj->nodesetval->nodeTab[lpc]->type != XML_NAMESPACE_DECL) {
  55             xpathObj->nodesetval->nodeTab[lpc] = NULL;
  56         }
  57     }
  58 
  59     /* _Now_ it's safe to free it */
  60     xmlXPathFreeObject(xpathObj);
  61 }
  62 
  63 xmlNode *
  64 getXpathResult(xmlXPathObjectPtr xpathObj, int index)
     /* [previous][next][first][last][top][bottom][index][help] */
  65 {
  66     xmlNode *match = NULL;
  67     int max = numXpathResults(xpathObj);
  68 
  69     CRM_CHECK(index >= 0, return NULL);
  70     CRM_CHECK(xpathObj != NULL, return NULL);
  71 
  72     if (index >= max) {
  73         crm_err("Requested index %d of only %d items", index, max);
  74         return NULL;
  75 
  76     } else if(xpathObj->nodesetval->nodeTab[index] == NULL) {
  77         /* Previously requested */
  78         return NULL;
  79     }
  80 
  81     match = xpathObj->nodesetval->nodeTab[index];
  82     CRM_CHECK(match != NULL, return NULL);
  83 
  84     if (xpathObj->nodesetval->nodeTab[index]->type != XML_NAMESPACE_DECL) {
  85         /* See the comment for freeXpathObject() */
  86         xpathObj->nodesetval->nodeTab[index] = NULL;
  87     }
  88 
  89     if (match->type == XML_DOCUMENT_NODE) {
  90         /* Will happen if section = '/' */
  91         match = match->children;
  92 
  93     } else if (match->type != XML_ELEMENT_NODE
  94                && match->parent && match->parent->type == XML_ELEMENT_NODE) {
  95         /* Return the parent instead */
  96         match = match->parent;
  97 
  98     } else if (match->type != XML_ELEMENT_NODE) {
  99         /* We only support searching nodes */
 100         crm_err("We only support %d not %d", XML_ELEMENT_NODE, match->type);
 101         match = NULL;
 102     }
 103     return match;
 104 }
 105 
 106 void
 107 dedupXpathResults(xmlXPathObjectPtr xpathObj)
     /* [previous][next][first][last][top][bottom][index][help] */
 108 {
 109     int lpc, max = numXpathResults(xpathObj);
 110 
 111     if (xpathObj == NULL) {
 112         return;
 113     }
 114 
 115     for (lpc = 0; lpc < max; lpc++) {
 116         xmlNode *xml = NULL;
 117         gboolean dedup = FALSE;
 118 
 119         if (xpathObj->nodesetval->nodeTab[lpc] == NULL) {
 120             continue;
 121         }
 122 
 123         xml = xpathObj->nodesetval->nodeTab[lpc]->parent;
 124 
 125         for (; xml; xml = xml->parent) {
 126             int lpc2 = 0;
 127 
 128             for (lpc2 = 0; lpc2 < max; lpc2++) {
 129                 if (xpathObj->nodesetval->nodeTab[lpc2] == xml) {
 130                     xpathObj->nodesetval->nodeTab[lpc] = NULL;
 131                     dedup = TRUE;
 132                     break;
 133                 }
 134             }
 135 
 136             if (dedup) {
 137                 break;
 138             }
 139         }
 140     }
 141 }
 142 
 143 /* the caller needs to check if the result contains a xmlDocPtr or xmlNodePtr */
 144 xmlXPathObjectPtr
 145 xpath_search(xmlNode * xml_top, const char *path)
     /* [previous][next][first][last][top][bottom][index][help] */
 146 {
 147     xmlDocPtr doc = NULL;
 148     xmlXPathObjectPtr xpathObj = NULL;
 149     xmlXPathContextPtr xpathCtx = NULL;
 150     const xmlChar *xpathExpr = (const xmlChar *)path;
 151 
 152     CRM_CHECK(path != NULL, return NULL);
 153     CRM_CHECK(xml_top != NULL, return NULL);
 154     CRM_CHECK(strlen(path) > 0, return NULL);
 155 
 156     doc = getDocPtr(xml_top);
 157 
 158     xpathCtx = xmlXPathNewContext(doc);
 159     CRM_ASSERT(xpathCtx != NULL);
 160 
 161     xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
 162     xmlXPathFreeContext(xpathCtx);
 163     return xpathObj;
 164 }
 165 
 166 /*!
 167  * \brief Run a supplied function for each result of an xpath search
 168  *
 169  * \param[in] xml            XML to search
 170  * \param[in] xpath          XPath search string
 171  * \param[in] helper         Function to call for each result
 172  * \param[in,out] user_data  Data to pass to supplied function
 173  *
 174  * \note The helper function will be passed the XML node of the result,
 175  *       and the supplied user_data. This function does not otherwise
 176  *       use user_data.
 177  */
 178 void
 179 crm_foreach_xpath_result(xmlNode *xml, const char *xpath,
     /* [previous][next][first][last][top][bottom][index][help] */
 180                          void (*helper)(xmlNode*, void*), void *user_data)
 181 {
 182     xmlXPathObjectPtr xpathObj = xpath_search(xml, xpath);
 183     int nresults = numXpathResults(xpathObj);
 184     int i;
 185 
 186     for (i = 0; i < nresults; i++) {
 187         xmlNode *result = getXpathResult(xpathObj, i);
 188 
 189         CRM_LOG_ASSERT(result != NULL);
 190         if (result) {
 191             (*helper)(result, user_data);
 192         }
 193     }
 194     freeXpathObject(xpathObj);
 195 }
 196 
 197 xmlNode *
 198 get_xpath_object_relative(const char *xpath, xmlNode * xml_obj, int error_level)
     /* [previous][next][first][last][top][bottom][index][help] */
 199 {
 200     int len = 0;
 201     xmlNode *result = NULL;
 202     char *xpath_full = NULL;
 203     char *xpath_prefix = NULL;
 204 
 205     if (xml_obj == NULL || xpath == NULL) {
 206         return NULL;
 207     }
 208 
 209     xpath_prefix = (char *)xmlGetNodePath(xml_obj);
 210 
 211     len = strlen(xpath_prefix) + strlen(xpath) + 1;
 212     xpath_full = malloc(len);
 213     strcpy(xpath_full, xpath_prefix);
 214     strcat(xpath_full, xpath);
 215 
 216     result = get_xpath_object(xpath_full, xml_obj, error_level);
 217 
 218     free(xpath_prefix);
 219     free(xpath_full);
 220     return result;
 221 }
 222 
 223 xmlNode *
 224 get_xpath_object(const char *xpath, xmlNode * xml_obj, int error_level)
     /* [previous][next][first][last][top][bottom][index][help] */
 225 {
 226     int max;
 227     xmlNode *result = NULL;
 228     xmlXPathObjectPtr xpathObj = NULL;
 229     char *nodePath = NULL;
 230     char *matchNodePath = NULL;
 231 
 232     if (xpath == NULL) {
 233         return xml_obj;         /* or return NULL? */
 234     }
 235 
 236     xpathObj = xpath_search(xml_obj, xpath);
 237     nodePath = (char *)xmlGetNodePath(xml_obj);
 238     max = numXpathResults(xpathObj);
 239 
 240     if (max < 1) {
 241         do_crm_log(error_level, "No match for %s in %s", xpath, crm_str(nodePath));
 242         crm_log_xml_explicit(xml_obj, "Unexpected Input");
 243 
 244     } else if (max > 1) {
 245         int lpc = 0;
 246 
 247         do_crm_log(error_level, "Too many matches for %s in %s", xpath, crm_str(nodePath));
 248 
 249         for (lpc = 0; lpc < max; lpc++) {
 250             xmlNode *match = getXpathResult(xpathObj, lpc);
 251 
 252             CRM_LOG_ASSERT(match != NULL);
 253             if(match != NULL) {
 254                 matchNodePath = (char *)xmlGetNodePath(match);
 255                 do_crm_log(error_level, "%s[%d] = %s", xpath, lpc, crm_str(matchNodePath));
 256                 free(matchNodePath);
 257             }
 258         }
 259         crm_log_xml_explicit(xml_obj, "Bad Input");
 260 
 261     } else {
 262         result = getXpathResult(xpathObj, 0);
 263     }
 264 
 265     freeXpathObject(xpathObj);
 266     free(nodePath);
 267 
 268     return result;
 269 }

/* [previous][next][first][last][top][bottom][index][help] */