Home | History | Annotate | Download | only in examples
      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
     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
    191