Return to xpath2.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / libxml2 / doc / examples |
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