Annotation of embedaddon/libxml2/doc/examples/xpath2.c, revision 1.1.1.1
1.1 misho 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
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>