2015-06-30

xpath_demo.c

/*
File : xpath_demo.c
Description: A demo C program to print the text of a node from a xpath expression
Usage: xpath_demo.c xml_file xpath_expression
Dependence: libxml2
How to make:
cc -o xpath_demo -L/usr/local/lib -R/usr/local/lib -lxml2 -I/usr/local/include/libxml2 xpath_demo.c
*/

#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>   /* for function xmlXPathRegisterNs */
#include <assert.h>

/* Function Prototype */
int  register_namespaces(xmlXPathContextPtr xpathCtx, const xmlChar* nsList);

/* ===================================================================== */
int main(int argc, char **argv) {

xmlDocPtr doc;
/* xmlNodeSetPtr nodeset; */
xmlXPathObjectPtr result;
xmlNode *node;
xmlChar *node_text;
xmlXPathContextPtr context;
char *filename;
xmlChar *xpath_expression;
xmlChar *nsList;

if ((argc != 3) && (argc != 4)) {
  fprintf(stderr, "Usage: %s xml_file xpath_expression  [<known-ns-list>]\n", argv[0]);
  fprintf(stderr, "where <known-ns-list> is a list of known namespaces\n");
  fprintf(stderr, "in \"<prefix1>=<href1> <prefix2>=href2> ...\" format\n");
  return(1);
  }
filename = argv[1];
xpath_expression = (xmlChar*) argv[2];

fprintf (stderr, "DEBUG: LIBXML_VERSION is " LIBXML_VERSION_STRING "\n");

doc = xmlParseFile(filename);
if (doc == NULL ) {
  fprintf(stderr, "Error: Document not parsed successfully.\n");
  xmlCleanupParser();
  return 1;
  }

context = xmlXPathNewContext(doc);
if (context == NULL) {
  fprintf(stderr, "Error in xmlXPathNewContext\n");
  xmlFreeDoc(doc);
  xmlCleanupParser();
  return 2;
  }

if (argc == 4) {
  nsList = (xmlChar*) argv[3];
  if (register_namespaces(context, nsList) < 0) {
    fprintf(stderr,"Error: failed to register namespaces list \"%s\"\n", nsList);
    xmlXPathFreeContext(context);
    xmlFreeDoc(doc);
    xmlCleanupParser();
    return 3;
    }
  }
result = xmlXPathEvalExpression(xpath_expression, context);
xmlXPathFreeContext (context);
if (result == NULL) {
  fprintf(stderr, "Error in xmlXPathEvalExpression\n");
  xmlFreeDoc(doc);
  xmlCleanupParser();
  return 4;
  }

/*
xmlXPathEvalExpression() call returns a set of ALL the nodes that match the expression
We are only interested in the first node returned
*/

if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
  printf ("Empty\n");
  xmlXPathFreeObject(result);
  xmlFreeDoc(doc);
  xmlCleanupParser();
  return 5;
  }

/* Retrieve the data */
node = result->nodesetval->nodeTab[0];
node_text = xmlNodeGetContent(node);
/* xmlNodeGetContent retrieves the text values of all children too.  This is correct */

fprintf (stderr, "DEBUG: node-type: %d node-name: %s\n" ,node->type, node->name);
xmlAttr *attr = node->properties;
while ( attr ) {
  fprintf (stderr, "DEBUG: attribute-name:%s attribute-value:%s\n" , attr->name, attr->children->content);
  attr = attr->next;
  } /* while */
printf ("node-text: \"%s\"\n", node_text);
xmlFree (node_text);
xmlXPathFreeObject(result);

/* Final clean up */
xmlFreeDoc(doc);
xmlCleanupParser();
return (0);
} /* main */

/**************************************************************************************/
/* The following fucnction is extracted from http://www.xmlsoft.org/examples/xpath1.c */
/**************************************************************************************/

/**
 * register_namespaces:
 * @xpathCtx:           the pointer to an XPath context.
 * @nsList:             the list of known namespaces in
 *                      "<prefix1>=<href1> <prefix2>=href2> ..." format.
 *
 * Registers namespaces from @nsList in @xpathCtx.
 *
 * Returns 0 on success and a negative value otherwise.
 */
int
register_namespaces(xmlXPathContextPtr xpathCtx, const xmlChar* nsList) {
    xmlChar* nsListDup;
    xmlChar* prefix;
    xmlChar* href;
    xmlChar* next;

    assert(xpathCtx);
    assert(nsList);

    nsListDup = xmlStrdup(nsList);
    if(nsListDup == NULL) {
        fprintf(stderr, "Error: unable to strdup namespaces list\n");
        return(-1);
    }

    next = nsListDup;
    while(next != NULL) {
        /* skip spaces */
        while((*next) == ' ') next++;
        if((*next) == '\0') break;

        /* find prefix */
        prefix = next;
        next = (xmlChar*)xmlStrchr(next, '=');
        if(next == NULL) {
            fprintf(stderr,"Error: invalid namespaces list format\n");
            xmlFree(nsListDup);
            return(-1);
        }
        *(next++) = '\0';

        /* find href */
        href = next;
        next = (xmlChar*)xmlStrchr(next, ' ');
        if(next != NULL) {
            *(next++) = '\0';
        }
        /* do register namespace */
        if(xmlXPathRegisterNs(xpathCtx, prefix, href) != 0) {
            fprintf(stderr,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", prefix, href);
            xmlFree(nsListDup);
            return(-1);
        }
    }

    xmlFree(nsListDup);
    return(0);
}