/ libxml2 / doc / examples / xpath2.c
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      /* Shutdown libxml */
 51      xmlCleanupParser();
 52      
 53      /*
 54       * this is to debug memory for regression tests
 55       */
 56      xmlMemoryDump();
 57      return 0;
 58  }
 59  
 60  /**
 61   * usage:
 62   * @name:		the program name.
 63   *
 64   * Prints usage information.
 65   */
 66  static void 
 67  usage(const char *name) {
 68      assert(name);
 69      
 70      fprintf(stderr, "Usage: %s <xml-file> <xpath-expr> <value>\n", name);
 71  }
 72  
 73  /**
 74   * example4:
 75   * @filename:		the input XML filename.
 76   * @xpathExpr:		the xpath expression for evaluation.
 77   * @value:		the new node content.
 78   *
 79   * Parses input XML file, evaluates XPath expression and update the nodes
 80   * then print the result.
 81   *
 82   * Returns 0 on success and a negative value otherwise.
 83   */
 84  static int 
 85  example4(const char* filename, const xmlChar* xpathExpr, const xmlChar* value) {
 86      xmlDocPtr doc;
 87      xmlXPathContextPtr xpathCtx; 
 88      xmlXPathObjectPtr xpathObj; 
 89      
 90      assert(filename);
 91      assert(xpathExpr);
 92      assert(value);
 93  
 94      /* Load XML document */
 95      doc = xmlParseFile(filename);
 96      if (doc == NULL) {
 97  	fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename);
 98  	return(-1);
 99      }
100  
101      /* Create xpath evaluation context */
102      xpathCtx = xmlXPathNewContext(doc);
103      if(xpathCtx == NULL) {
104          fprintf(stderr,"Error: unable to create new XPath context\n");
105          xmlFreeDoc(doc); 
106          return(-1);
107      }
108      
109      /* Evaluate xpath expression */
110      xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
111      if(xpathObj == NULL) {
112          fprintf(stderr,"Error: unable to evaluate xpath expression \"%s\"\n", xpathExpr);
113          xmlXPathFreeContext(xpathCtx); 
114          xmlFreeDoc(doc); 
115          return(-1);
116      }
117  
118      /* update selected nodes */
119      update_xpath_nodes(xpathObj->nodesetval, value);
120  
121      
122      /* Cleanup of XPath data */
123      xmlXPathFreeObject(xpathObj);
124      xmlXPathFreeContext(xpathCtx); 
125  
126      /* dump the resulting document */
127      xmlDocDump(stdout, doc);
128  
129  
130      /* free the document */
131      xmlFreeDoc(doc); 
132      
133      return(0);
134  }
135  
136  /**
137   * update_xpath_nodes:
138   * @nodes:		the nodes set.
139   * @value:		the new value for the node(s)
140   *
141   * Prints the @nodes content to @output.
142   */
143  static void
144  update_xpath_nodes(xmlNodeSetPtr nodes, const xmlChar* value) {
145      int size;
146      int i;
147      
148      assert(value);
149      size = (nodes) ? nodes->nodeNr : 0;
150      
151      /*
152       * NOTE: the nodes are processed in reverse order, i.e. reverse document
153       *       order because xmlNodeSetContent can actually free up descendant
154       *       of the node and such nodes may have been selected too ! Handling
155       *       in reverse order ensure that descendant are accessed first, before
156       *       they get removed. Mixing XPath and modifications on a tree must be
157       *       done carefully !
158       */
159      for(i = size - 1; i >= 0; i--) {
160  	assert(nodes->nodeTab[i]);
161  	
162  	xmlNodeSetContent(nodes->nodeTab[i], value);
163  	/*
164  	 * All the elements returned by an XPath query are pointers to
165  	 * elements from the tree *except* namespace nodes where the XPath
166  	 * semantic is different from the implementation in libxml2 tree.
167  	 * As a result when a returned node set is freed when
168  	 * xmlXPathFreeObject() is called, that routine must check the
169  	 * element type. But node from the returned set may have been removed
170  	 * by xmlNodeSetContent() resulting in access to freed data.
171  	 * This can be exercised by running
172  	 *       valgrind xpath2 test3.xml '//discarded' discarded
173  	 * There is 2 ways around it:
174  	 *   - make a copy of the pointers to the nodes from the result set 
175  	 *     then call xmlXPathFreeObject() and then modify the nodes
176  	 * or
177  	 *   - remove the reference to the modified nodes from the node set
178  	 *     as they are processed, if they are not namespace nodes.
179  	 */
180  	if (nodes->nodeTab[i]->type != XML_NAMESPACE_DECL)
181  	    nodes->nodeTab[i] = NULL;
182      }
183  }
184  
185  #else
186  int main(void) {
187      fprintf(stderr, "XPath support not compiled in\n");
188      exit(1);
189  }
190  #endif