Git init
[external/libxml2.git] / doc / examples / xpath2.c
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 xpath2.res ; rm xpath2.tmp
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 
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     /* Shutdown libxml */
51     xmlCleanupParser();
52     
53     /*
54      * this is to debug memory for regression tests
55      */
56     xmlMemoryDump();
57     return 0;
58 }
59
60 /**
61  * usage:
62  * @name:               the program name.
63  *
64  * Prints usage information.
65  */
66 static void 
67 usage(const char *name) {
68     assert(name);
69     
70     fprintf(stderr, "Usage: %s <xml-file> <xpath-expr> <value>\n", name);
71 }
72
73 /**
74  * example4:
75  * @filename:           the input XML filename.
76  * @xpathExpr:          the xpath expression for evaluation.
77  * @value:              the new node content.
78  *
79  * Parses input XML file, evaluates XPath expression and update the nodes
80  * then print the result.
81  *
82  * Returns 0 on success and a negative value otherwise.
83  */
84 static int 
85 example4(const char* filename, const xmlChar* xpathExpr, const xmlChar* value) {
86     xmlDocPtr doc;
87     xmlXPathContextPtr xpathCtx; 
88     xmlXPathObjectPtr xpathObj; 
89     
90     assert(filename);
91     assert(xpathExpr);
92     assert(value);
93
94     /* Load XML document */
95     doc = xmlParseFile(filename);
96     if (doc == NULL) {
97         fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename);
98         return(-1);
99     }
100
101     /* Create xpath evaluation context */
102     xpathCtx = xmlXPathNewContext(doc);
103     if(xpathCtx == NULL) {
104         fprintf(stderr,"Error: unable to create new XPath context\n");
105         xmlFreeDoc(doc); 
106         return(-1);
107     }
108     
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); 
114         xmlFreeDoc(doc); 
115         return(-1);
116     }
117
118     /* update selected nodes */
119     update_xpath_nodes(xpathObj->nodesetval, value);
120
121     
122     /* Cleanup of XPath data */
123     xmlXPathFreeObject(xpathObj);
124     xmlXPathFreeContext(xpathCtx); 
125
126     /* dump the resulting document */
127     xmlDocDump(stdout, doc);
128
129
130     /* free the document */
131     xmlFreeDoc(doc); 
132     
133     return(0);
134 }
135
136 /**
137  * update_xpath_nodes:
138  * @nodes:              the nodes set.
139  * @value:              the new value for the node(s)
140  *
141  * Prints the @nodes content to @output.
142  */
143 static void
144 update_xpath_nodes(xmlNodeSetPtr nodes, const xmlChar* value) {
145     int size;
146     int i;
147     
148     assert(value);
149     size = (nodes) ? nodes->nodeNr : 0;
150     
151     /*
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
157      *       done carefully !
158      */
159     for(i = size - 1; i >= 0; i--) {
160         assert(nodes->nodeTab[i]);
161         
162         xmlNodeSetContent(nodes->nodeTab[i], value);
163         /*
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
176          * or
177          *   - remove the reference to the modified nodes from the node set
178          *     as they are processed, if they are not namespace nodes.
179          */
180         if (nodes->nodeTab[i]->type != XML_NAMESPACE_DECL)
181             nodes->nodeTab[i] = NULL;
182     }
183 }
184
185 #else
186 int main(void) {
187     fprintf(stderr, "XPath support not compiled in\n");
188     exit(1);
189 }
190 #endif