#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string>
#include <curl/curl.h>
#include <libxml/HTMLparser.h>
#ifdef _MSC_VER
#define COMPARE(a, b) (!stricmp((a), (b)))
#else
#define COMPARE(a, b) (!strcasecmp((a), (b)))
#endif
struct Context
{
Context(): addTitle(false) { }
bool addTitle;
std::string title;
};
static char errorBuffer[CURL_ERROR_SIZE];
static std::string buffer;
static int writer(char *data, size_t size, size_t nmemb,
std::string *writerData)
{
if (writerData == NULL)
return 0;
writerData->append(data, size*nmemb);
return size * nmemb;
}
static bool init(CURL *&conn, char *url)
{
CURLcode code;
conn = curl_easy_init();
if (conn == NULL)
{
fprintf(stderr, "Failed to create CURL connection\n");
exit(EXIT_FAILURE);
}
code = curl_easy_setopt(conn, CURLOPT_ERRORBUFFER, errorBuffer);
if (code != CURLE_OK)
{
fprintf(stderr, "Failed to set error buffer [%d]\n", code);
return false;
}
code = curl_easy_setopt(conn, CURLOPT_URL, url);
if (code != CURLE_OK)
{
fprintf(stderr, "Failed to set URL [%s]\n", errorBuffer);
return false;
}
code = curl_easy_setopt(conn, CURLOPT_FOLLOWLOCATION, 1L);
if (code != CURLE_OK)
{
fprintf(stderr, "Failed to set redirect option [%s]\n", errorBuffer);
return false;
}
code = curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, writer);
if (code != CURLE_OK)
{
fprintf(stderr, "Failed to set writer [%s]\n", errorBuffer);
return false;
}
code = curl_easy_setopt(conn, CURLOPT_WRITEDATA, &buffer);
if (code != CURLE_OK)
{
fprintf(stderr, "Failed to set write data [%s]\n", errorBuffer);
return false;
}
return true;
}
static void StartElement(void *voidContext,
const xmlChar *name,
const xmlChar **attributes)
{
Context *context = (Context *)voidContext;
if (COMPARE((char *)name, "TITLE"))
{
context->title = "";
context->addTitle = true;
}
(void) attributes;
}
static void EndElement(void *voidContext,
const xmlChar *name)
{
Context *context = (Context *)voidContext;
if (COMPARE((char *)name, "TITLE"))
context->addTitle = false;
}
static void handleCharacters(Context *context,
const xmlChar *chars,
int length)
{
if (context->addTitle)
context->title.append((char *)chars, length);
}
static void Characters(void *voidContext,
const xmlChar *chars,
int length)
{
Context *context = (Context *)voidContext;
handleCharacters(context, chars, length);
}
static void cdata(void *voidContext,
const xmlChar *chars,
int length)
{
Context *context = (Context *)voidContext;
handleCharacters(context, chars, length);
}
static htmlSAXHandler saxHandler =
{
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
StartElement,
EndElement,
NULL,
Characters,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
cdata,
NULL
};
static void parseHtml(const std::string &html,
std::string &title)
{
htmlParserCtxtPtr ctxt;
Context context;
ctxt = htmlCreatePushParserCtxt(&saxHandler, &context, "", 0, "",
XML_CHAR_ENCODING_NONE);
htmlParseChunk(ctxt, html.c_str(), html.size(), 0);
htmlParseChunk(ctxt, "", 0, 1);
htmlFreeParserCtxt(ctxt);
title = context.title;
}
int main(int argc, char *argv[])
{
CURL *conn = NULL;
CURLcode code;
std::string title;
if (argc != 2)
{
fprintf(stderr, "Usage: %s <url>\n", argv[0]);
exit(EXIT_FAILURE);
}
curl_global_init(CURL_GLOBAL_DEFAULT);
if (!init(conn, argv[1]))
{
fprintf(stderr, "Connection initializion failed\n");
exit(EXIT_FAILURE);
}
code = curl_easy_perform(conn);
curl_easy_cleanup(conn);
if (code != CURLE_OK)
{
fprintf(stderr, "Failed to get '%s' [%s]\n", argv[1], errorBuffer);
exit(EXIT_FAILURE);
}
parseHtml(buffer, title);
printf("Title: %s\n", title.c_str());
return EXIT_SUCCESS;
}