xref: /aosp_15_r20/external/libxml2/example/xpath2.c (revision 7c5688314b92172186c154356a6374bf7684c3ca)
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