(root)/
libxml2-2.12.3/
doc/
examples/
xpath2.c
       1  /** 
       2   * section: 	XPath
       3   * synopsis: 	Load a document, locate subelements with XPath, modify
       4   *              said elements and save the resulting document.
       5   * purpose: 	Shows how to make a full round-trip from a load/edit/save
       6   * usage:	xpath2 <xml-file> <xpath-expr> <new-value>
       7   * test:	xpath2 test3.xml '//discarded' discarded > xpath2.tmp && diff xpath2.tmp $(srcdir)/xpath2.res
       8   * author: 	Aleksey Sanin and Daniel Veillard
       9   * copy: 	see Copyright for the status of this software.
      10   */
      11  #include <stdlib.h>
      12  #include <stdio.h>
      13  #include <string.h>
      14  #include <assert.h>
      15  
      16  #include <libxml/tree.h>
      17  #include <libxml/parser.h>
      18  #include <libxml/xpath.h>
      19  #include <libxml/xpathInternals.h>
      20  
      21  #if defined(LIBXML_XPATH_ENABLED) && defined(LIBXML_SAX1_ENABLED) && \
      22      defined(LIBXML_OUTPUT_ENABLED)
      23  
      24  
      25  static void usage(const char *name);
      26  static int example4(const char *filename, const xmlChar * xpathExpr,
      27                      const xmlChar * value);
      28  static void update_xpath_nodes(xmlNodeSetPtr nodes, const xmlChar * value);
      29  
      30  
      31  int 
      32  main(int argc, char **argv) {
      33      /* Parse command line and process file */
      34      if (argc != 4) {
      35  	fprintf(stderr, "Error: wrong number of arguments.\n");
      36  	usage(argv[0]);
      37  	return(-1);
      38      } 
      39      
      40      /* Init libxml */     
      41      xmlInitParser();
      42      LIBXML_TEST_VERSION
      43  
      44      /* Do the main job */
      45      if (example4(argv[1], BAD_CAST argv[2], BAD_CAST argv[3])) {
      46  	usage(argv[0]);
      47  	return(-1);
      48      }
      49  
      50      return 0;
      51  }
      52  
      53  /**
      54   * usage:
      55   * @name:		the program name.
      56   *
      57   * Prints usage information.
      58   */
      59  static void 
      60  usage(const char *name) {
      61      assert(name);
      62      
      63      fprintf(stderr, "Usage: %s <xml-file> <xpath-expr> <value>\n", name);
      64  }
      65  
      66  /**
      67   * example4:
      68   * @filename:		the input XML filename.
      69   * @xpathExpr:		the xpath expression for evaluation.
      70   * @value:		the new node content.
      71   *
      72   * Parses input XML file, evaluates XPath expression and update the nodes
      73   * then print the result.
      74   *
      75   * Returns 0 on success and a negative value otherwise.
      76   */
      77  static int 
      78  example4(const char* filename, const xmlChar* xpathExpr, const xmlChar* value) {
      79      xmlDocPtr doc;
      80      xmlXPathContextPtr xpathCtx; 
      81      xmlXPathObjectPtr xpathObj; 
      82      
      83      assert(filename);
      84      assert(xpathExpr);
      85      assert(value);
      86  
      87      /* Load XML document */
      88      doc = xmlParseFile(filename);
      89      if (doc == NULL) {
      90  	fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename);
      91  	return(-1);
      92      }
      93  
      94      /* Create xpath evaluation context */
      95      xpathCtx = xmlXPathNewContext(doc);
      96      if(xpathCtx == NULL) {
      97          fprintf(stderr,"Error: unable to create new XPath context\n");
      98          xmlFreeDoc(doc); 
      99          return(-1);
     100      }
     101      
     102      /* Evaluate xpath expression */
     103      xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
     104      if(xpathObj == NULL) {
     105          fprintf(stderr,"Error: unable to evaluate xpath expression \"%s\"\n", xpathExpr);
     106          xmlXPathFreeContext(xpathCtx); 
     107          xmlFreeDoc(doc); 
     108          return(-1);
     109      }
     110  
     111      /* update selected nodes */
     112      update_xpath_nodes(xpathObj->nodesetval, value);
     113  
     114      
     115      /* Cleanup of XPath data */
     116      xmlXPathFreeObject(xpathObj);
     117      xmlXPathFreeContext(xpathCtx); 
     118  
     119      /* dump the resulting document */
     120      xmlDocDump(stdout, doc);
     121  
     122  
     123      /* free the document */
     124      xmlFreeDoc(doc); 
     125      
     126      return(0);
     127  }
     128  
     129  /**
     130   * update_xpath_nodes:
     131   * @nodes:		the nodes set.
     132   * @value:		the new value for the node(s)
     133   *
     134   * Prints the @nodes content to @output.
     135   */
     136  static void
     137  update_xpath_nodes(xmlNodeSetPtr nodes, const xmlChar* value) {
     138      int size;
     139      int i;
     140      
     141      assert(value);
     142      size = (nodes) ? nodes->nodeNr : 0;
     143      
     144      /*
     145       * NOTE: the nodes are processed in reverse order, i.e. reverse document
     146       *       order because xmlNodeSetContent can actually free up descendant
     147       *       of the node and such nodes may have been selected too ! Handling
     148       *       in reverse order ensure that descendant are accessed first, before
     149       *       they get removed. Mixing XPath and modifications on a tree must be
     150       *       done carefully !
     151       */
     152      for(i = size - 1; i >= 0; i--) {
     153  	assert(nodes->nodeTab[i]);
     154  	
     155  	xmlNodeSetContent(nodes->nodeTab[i], value);
     156  	/*
     157  	 * All the elements returned by an XPath query are pointers to
     158  	 * elements from the tree *except* namespace nodes where the XPath
     159  	 * semantic is different from the implementation in libxml2 tree.
     160  	 * As a result when a returned node set is freed when
     161  	 * xmlXPathFreeObject() is called, that routine must check the
     162  	 * element type. But node from the returned set may have been removed
     163  	 * by xmlNodeSetContent() resulting in access to freed data.
     164  	 * This can be exercised by running
     165  	 *       valgrind xpath2 test3.xml '//discarded' discarded
     166  	 * There is 2 ways around it:
     167  	 *   - make a copy of the pointers to the nodes from the result set 
     168  	 *     then call xmlXPathFreeObject() and then modify the nodes
     169  	 * or
     170  	 *   - remove the reference to the modified nodes from the node set
     171  	 *     as they are processed, if they are not namespace nodes.
     172  	 */
     173  	if (nodes->nodeTab[i]->type != XML_NAMESPACE_DECL)
     174  	    nodes->nodeTab[i] = NULL;
     175      }
     176  }
     177  
     178  #else
     179  int main(void) {
     180      fprintf(stderr, "XPath support not compiled in\n");
     181      return 0;
     182  }
     183  #endif