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 xpath2.res ; rm xpath2.tmp
8 * author: Aleksey Sanin and Daniel Veillard
9 * copy: see Copyright for the status of this software.
16 #include <libxml/tree.h>
17 #include <libxml/parser.h>
18 #include <libxml/xpath.h>
19 #include <libxml/xpathInternals.h>
21 #if defined(LIBXML_XPATH_ENABLED) && defined(LIBXML_SAX1_ENABLED) && \
22 defined(LIBXML_OUTPUT_ENABLED)
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);
32 main(int argc, char **argv) {
33 /* Parse command line and process file */
35 fprintf(stderr, "Error: wrong number of arguments.\n");
45 if (example4(argv[1], BAD_CAST argv[2], BAD_CAST argv[3])) {
54 * this is to debug memory for regression tests
62 * @name: the program name.
64 * Prints usage information.
67 usage(const char *name) {
70 fprintf(stderr, "Usage: %s <xml-file> <xpath-expr> <value>\n", name);
75 * @filename: the input XML filename.
76 * @xpathExpr: the xpath expression for evaluation.
77 * @value: the new node content.
79 * Parses input XML file, evaluates XPath expression and update the nodes
80 * then print the result.
82 * Returns 0 on success and a negative value otherwise.
85 example4(const char* filename, const xmlChar* xpathExpr, const xmlChar* value) {
87 xmlXPathContextPtr xpathCtx;
88 xmlXPathObjectPtr xpathObj;
94 /* Load XML document */
95 doc = xmlParseFile(filename);
97 fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename);
101 /* Create xpath evaluation context */
102 xpathCtx = xmlXPathNewContext(doc);
103 if(xpathCtx == NULL) {
104 fprintf(stderr,"Error: unable to create new XPath context\n");
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);
118 /* update selected nodes */
119 update_xpath_nodes(xpathObj->nodesetval, value);
122 /* Cleanup of XPath data */
123 xmlXPathFreeObject(xpathObj);
124 xmlXPathFreeContext(xpathCtx);
126 /* dump the resulting document */
127 xmlDocDump(stdout, doc);
130 /* free the document */
137 * update_xpath_nodes:
138 * @nodes: the nodes set.
139 * @value: the new value for the node(s)
141 * Prints the @nodes content to @output.
144 update_xpath_nodes(xmlNodeSetPtr nodes, const xmlChar* value) {
149 size = (nodes) ? nodes->nodeNr : 0;
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
159 for(i = size - 1; i >= 0; i--) {
160 assert(nodes->nodeTab[i]);
162 xmlNodeSetContent(nodes->nodeTab[i], value);
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
177 * - remove the reference to the modified nodes from the node set
178 * as they are processed, if they are not namespace nodes.
180 if (nodes->nodeTab[i]->type != XML_NAMESPACE_DECL)
181 nodes->nodeTab[i] = NULL;
187 fprintf(stderr, "XPath support not compiled in\n");