xpath2.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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. #include <libxml/tree.h>
  16. #include <libxml/parser.h>
  17. #include <libxml/xpath.h>
  18. #include <libxml/xpathInternals.h>
  19. #if defined(LIBXML_XPATH_ENABLED) && defined(LIBXML_SAX1_ENABLED) && \
  20. defined(LIBXML_OUTPUT_ENABLED)
  21. static void usage(const char *name);
  22. static int example4(const char *filename, const xmlChar * xpathExpr,
  23. const xmlChar * value);
  24. static void update_xpath_nodes(xmlNodeSetPtr nodes, const xmlChar * value);
  25. int
  26. main(int argc, char **argv) {
  27. /* Parse command line and process file */
  28. if (argc != 4) {
  29. fprintf(stderr, "Error: wrong number of arguments.\n");
  30. usage(argv[0]);
  31. return(-1);
  32. }
  33. /* Init libxml */
  34. xmlInitParser();
  35. LIBXML_TEST_VERSION
  36. /* Do the main job */
  37. if (example4(argv[1], BAD_CAST argv[2], BAD_CAST argv[3])) {
  38. usage(argv[0]);
  39. return(-1);
  40. }
  41. /* Shutdown libxml */
  42. xmlCleanupParser();
  43. /*
  44. * this is to debug memory for regression tests
  45. */
  46. xmlMemoryDump();
  47. return 0;
  48. }
  49. /**
  50. * usage:
  51. * @name: the program name.
  52. *
  53. * Prints usage information.
  54. */
  55. static void
  56. usage(const char *name) {
  57. assert(name);
  58. fprintf(stderr, "Usage: %s <xml-file> <xpath-expr> <value>\n", name);
  59. }
  60. /**
  61. * example4:
  62. * @filename: the input XML filename.
  63. * @xpathExpr: the xpath expression for evaluation.
  64. * @value: the new node content.
  65. *
  66. * Parses input XML file, evaluates XPath expression and update the nodes
  67. * then print the result.
  68. *
  69. * Returns 0 on success and a negative value otherwise.
  70. */
  71. static int
  72. example4(const char* filename, const xmlChar* xpathExpr, const xmlChar* value) {
  73. xmlDocPtr doc;
  74. xmlXPathContextPtr xpathCtx;
  75. xmlXPathObjectPtr xpathObj;
  76. assert(filename);
  77. assert(xpathExpr);
  78. assert(value);
  79. /* Load XML document */
  80. doc = xmlParseFile(filename);
  81. if (doc == NULL) {
  82. fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename);
  83. return(-1);
  84. }
  85. /* Create xpath evaluation context */
  86. xpathCtx = xmlXPathNewContext(doc);
  87. if(xpathCtx == NULL) {
  88. fprintf(stderr,"Error: unable to create new XPath context\n");
  89. xmlFreeDoc(doc);
  90. return(-1);
  91. }
  92. /* Evaluate xpath expression */
  93. xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
  94. if(xpathObj == NULL) {
  95. fprintf(stderr,"Error: unable to evaluate xpath expression \"%s\"\n", xpathExpr);
  96. xmlXPathFreeContext(xpathCtx);
  97. xmlFreeDoc(doc);
  98. return(-1);
  99. }
  100. /* update selected nodes */
  101. update_xpath_nodes(xpathObj->nodesetval, value);
  102. /* Cleanup of XPath data */
  103. xmlXPathFreeObject(xpathObj);
  104. xmlXPathFreeContext(xpathCtx);
  105. /* dump the resulting document */
  106. xmlDocDump(stdout, doc);
  107. /* free the document */
  108. xmlFreeDoc(doc);
  109. return(0);
  110. }
  111. /**
  112. * update_xpath_nodes:
  113. * @nodes: the nodes set.
  114. * @value: the new value for the node(s)
  115. *
  116. * Prints the @nodes content to @output.
  117. */
  118. static void
  119. update_xpath_nodes(xmlNodeSetPtr nodes, const xmlChar* value) {
  120. int size;
  121. int i;
  122. assert(value);
  123. size = (nodes) ? nodes->nodeNr : 0;
  124. /*
  125. * NOTE: the nodes are processed in reverse order, i.e. reverse document
  126. * order because xmlNodeSetContent can actually free up descendant
  127. * of the node and such nodes may have been selected too ! Handling
  128. * in reverse order ensure that descendant are accessed first, before
  129. * they get removed. Mixing XPath and modifications on a tree must be
  130. * done carefully !
  131. */
  132. for(i = size - 1; i >= 0; i--) {
  133. assert(nodes->nodeTab[i]);
  134. xmlNodeSetContent(nodes->nodeTab[i], value);
  135. /*
  136. * All the elements returned by an XPath query are pointers to
  137. * elements from the tree *except* namespace nodes where the XPath
  138. * semantic is different from the implementation in libxml2 tree.
  139. * As a result when a returned node set is freed when
  140. * xmlXPathFreeObject() is called, that routine must check the
  141. * element type. But node from the returned set may have been removed
  142. * by xmlNodeSetContent() resulting in access to freed data.
  143. * This can be exercised by running
  144. * valgrind xpath2 test3.xml '//discarded' discarded
  145. * There is 2 ways around it:
  146. * - make a copy of the pointers to the nodes from the result set
  147. * then call xmlXPathFreeObject() and then modify the nodes
  148. * or
  149. * - remove the reference to the modified nodes from the node set
  150. * as they are processed, if they are not namespace nodes.
  151. */
  152. if (nodes->nodeTab[i]->type != XML_NAMESPACE_DECL)
  153. nodes->nodeTab[i] = NULL;
  154. }
  155. }
  156. #else
  157. int main(void) {
  158. fprintf(stderr, "XPath support not compiled in\n");
  159. exit(1);
  160. }
  161. #endif