Wednesday, August 24, 2011

Example of libxslt transform from a libcurl fetch

I've written various programs/network services that perform this function for one purpose or another. In light of some performance needs of one of these services I've been considering porting it from Perl to C in order to get the most performance possible. Being as I haven't written anything in C in a very long time I thought I would implement one aspect of the service as a simple command line tool.

This is the result:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <curl/curl.h>

#include <libxml/xmlmemory.h>

#include <libxslt/xslt.h>
#include <libxslt/xsltInternals.h>
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>

struct fetch {
	char *buffer;
	size_t size;
};

static size_t write_fetch ( void *chunk, size_t size, size_t nmemb, void *data ) {
	size_t realsize = size * nmemb;
	struct fetch *f = (struct fetch *)data;
	
	f->buffer = realloc( f->buffer, f->size + realsize + 1 );
	
	if ( f->buffer == NULL ) {
		fprintf( stderr, "Out of memory (realloc returned NULL)\n" );
		exit( EXIT_FAILURE );
	}

	memcpy( &( f->buffer[ f->size ] ), chunk, realsize );
	f->size += realsize;
	f->buffer[ f->size ] = 0;

	return realsize;
}

CURLcode get_curl_xml ( char *url, void *chunk ) {
	CURL *curl;
	CURLcode result;

	if ( curl = curl_easy_init() ) {
		curl_easy_setopt( curl, CURLOPT_URL, url );
		curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_fetch );
		curl_easy_setopt( curl, CURLOPT_WRITEDATA, chunk );

		if ( ( result = curl_easy_perform( curl ) ) != 0 ) {
			fprintf( stderr, "Error %i: %s\n", result, curl_easy_strerror( result ) );
			return result;	
		}
		
		curl_easy_cleanup( curl );
	} else {
		return -1;
	}

}

int main (int argc, char *argv[]) {

	struct fetch data;
	xsltStylesheetPtr xslt = NULL;
	xmlDocPtr source, normalized;
	
	data.buffer = malloc( 1 );
	data.size   = 0;

	if ( argc < 2 ) {
		fprintf( stderr, "Usage: %s <stylesheet> <url>\n", argv[0] );
		return 0;
	}


	get_curl_xml( argv[2], (void *)&data );

	fprintf( stderr, "Fetched %lu bytes\n", (long)data.size);
	fprintf( stderr, "Buffer size %lu\n", sizeof( data.buffer ) );

	if ( source = xmlReadMemory( data.buffer, data.size, "memory.xml", NULL, 0 ) ) {
		xslt       = xsltParseStylesheetFile( (const xmlChar *)argv[1] );
		normalized = xsltApplyStylesheet( xslt, source, NULL );
		
		fprintf( stderr, "Normlized XML data\n" );

		xmlSaveFile( "-", normalized );

	} else {
		fprintf( stderr, "Failed to parse XML document\n" );
	}

	xmlFreeDoc( source );
	xmlFreeDoc( normalized );
	xmlCleanupParser();

	free( data.buffer );

	return 0;
}

No comments:

Post a Comment