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