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