Edit

kc3-lang/libxml2/doc/examples/xpath2.c

Branch :

  • Show log

    Commit

  • Author : Daniel Veillard
    Date : 2004-03-28 12:18:45
    Hash : 3d354a79
    Message : handle and explain a very tricky problem when modifying the tree based on * doc/examples/xpath2.c doc/examples/xpath2.res: handle and explain a very tricky problem when modifying the tree based on an XPath result query. Daniel

  • doc/examples/xpath2.c
  • /** 
     * section: 	XPath
     * synopsis: 	Load a document, locate subelements with XPath, modify
     *              said elements and save the resulting document.
     * purpose: 	Shows how to make a full round-trip from a load/edit/save
     * usage:	xpath2 <xml-file> <xpath-expr> <new-value>
     * test:	xpath2 test3.xml '//discarded' discarded > xpath2.tmp ; diff xpath2.tmp xpath2.res ; rm xpath2.tmp
     * author: 	Aleksey Sanin and Daniel Veillard
     * copy: 	see Copyright for the status of this software.
     */
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <assert.h>
    
    #include <libxml/tree.h>
    #include <libxml/parser.h>
    #include <libxml/xpath.h>
    #include <libxml/xpathInternals.h>
    
    #ifdef LIBXML_XPATH_ENABLED
    
    
    static void usage(const char *name);
    static int example4(const char *filename, const xmlChar * xpathExpr,
                        const xmlChar * value);
    static void update_xpath_nodes(xmlNodeSetPtr nodes, const xmlChar * value);
    
    
    int 
    main(int argc, char **argv) {
        /* Parse command line and process file */
        if (argc != 4) {
    	fprintf(stderr, "Error: wrong number of arguments.\n");
    	usage(argv[0]);
    	return(-1);
        } 
        
        /* Init libxml */     
        xmlInitParser();
        LIBXML_TEST_VERSION
    
        /* Do the main job */
        if (example4(argv[1], BAD_CAST argv[2], BAD_CAST argv[3])) {
    	usage(argv[0]);
    	return(-1);
        }
    
        /* Shutdown libxml */
        xmlCleanupParser();
        
        /*
         * this is to debug memory for regression tests
         */
        xmlMemoryDump();
        return 0;
    }
    
    /**
     * usage:
     * @name:		the program name.
     *
     * Prints usage information.
     */
    void 
    usage(const char *name) {
        assert(name);
        
        fprintf(stderr, "Usage: %s <xml-file> <xpath-expr> <value>\n", name);
    }
    
    /**
     * example4:
     * @filename:		the input XML filename.
     * @xpathExpr:		the xpath expression for evaluation.
     * @value:		the new node content.
     *
     * Parses input XML file, evaluates XPath expression and update the nodes
     * then print the result.
     *
     * Returns 0 on success and a negative value otherwise.
     */
    static int 
    example4(const char* filename, const xmlChar* xpathExpr, const xmlChar* value) {
        xmlDocPtr doc;
        xmlXPathContextPtr xpathCtx; 
        xmlXPathObjectPtr xpathObj; 
        
        assert(filename);
        assert(xpathExpr);
        assert(value);
    
        /* Load XML document */
        doc = xmlParseFile(filename);
        if (doc == NULL) {
    	fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename);
    	return(-1);
        }
    
        /* Create xpath evaluation context */
        xpathCtx = xmlXPathNewContext(doc);
        if(xpathCtx == NULL) {
            fprintf(stderr,"Error: unable to create new XPath context\n");
            xmlFreeDoc(doc); 
            return(-1);
        }
        
        /* Evaluate xpath expression */
        xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
        if(xpathObj == NULL) {
            fprintf(stderr,"Error: unable to evaluate xpath expression \"%s\"\n", xpathExpr);
            xmlXPathFreeContext(xpathCtx); 
            xmlFreeDoc(doc); 
            return(-1);
        }
    
        /* update selected nodes */
        update_xpath_nodes(xpathObj->nodesetval, value);
    
        
        /* Cleanup of XPath data */
        xmlXPathFreeObject(xpathObj);
        xmlXPathFreeContext(xpathCtx); 
    
        /* dump the resulting document */
        xmlDocDump(stdout, doc);
    
    
        /* free the document */
        xmlFreeDoc(doc); 
        
        return(0);
    }
    
    /**
     * update_xpath_nodes:
     * @nodes:		the nodes set.
     * @value:		the new value for the node(s)
     *
     * Prints the @nodes content to @output.
     */
    static void
    update_xpath_nodes(xmlNodeSetPtr nodes, const xmlChar* value) {
        int size;
        int i;
        
        assert(value);
        size = (nodes) ? nodes->nodeNr : 0;
        
        /*
         * NOTE: the nodes are processed in reverse order, i.e. reverse document
         *       order because xmlNodeSetContent can actually free up descendant
         *       of the node and such nodes may have been selected too ! Handling
         *       in reverse order ensure that descendant are accessed first, before
         *       they get removed. Mixing XPath and modifications on a tree must be
         *       done carefully !
         */
        for(i = size - 1; i >= 0; i--) {
    	assert(nodes->nodeTab[i]);
    	
    	xmlNodeSetContent(nodes->nodeTab[i], value);
    	/*
    	 * All the elements returned by an XPath query are pointers to
    	 * elements from the tree *except* namespace nodes where the XPath
    	 * semantic is different from the implementation in libxml2 tree.
    	 * As a result when a returned node set is freed when
    	 * xmlXPathFreeObject() is called, that routine must check the
    	 * element type. But node from the returned set may have been removed
    	 * by xmlNodeSetContent() resulting in access to freed data.
    	 * This can be exercised by running
    	 *       valgrind xpath2 test3.xml '//discarded' discarded
    	 * There is 2 ways around it:
    	 *   - make a copy of the pointers to the nodes from the result set 
    	 *     then call xmlXPathFreeObject() and then modify the nodes
    	 * or
    	 *   - remove the reference to the modified nodes from the node set
    	 *     as they are processed, if they are not namespace nodes.
    	 */
    	if (nodes->nodeTab[i]->type != XML_NAMESPACE_DECL)
    	    nodes->nodeTab[i] = NULL;
        }
    }
    
    #else
    int main(void) {
        fprintf(stderr, "XPath support not compiled in\n");
        exit(1);
    }
    #endif