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