Blame doc/examples/xpath2.c

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