Blob Blame History Raw
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* parser.c - Locations.xml parser
 *
 * Copyright 2008, Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see
 * <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>
#include <glib.h>
#include <libxml/xmlreader.h>
#include <glib/gi18n-lib.h>

#include "gweather-private.h"
#include "gweather-parser.h"

/*
 * _gweather_parser_get_value:
 * @parser: a #GWeatherParser
 *
 * Gets the text of the element whose start tag @parser is pointing to.
 * Leaves @parser pointing at the next node after the element's end tag.
 *
 * Return value: the text of the current node, as a libxml-allocated
 * string, or %NULL if the node is empty.
 **/
char *
_gweather_parser_get_value (GWeatherParser *parser)
{
    char *value;

    /* check for null node */
    if (xmlTextReaderIsEmptyElement (parser->xml))
	return NULL;

    /* the next "node" is the text node containing the value we want to get */
    if (xmlTextReaderRead (parser->xml) != 1)
	return NULL;

    value = (char *) xmlTextReaderValue (parser->xml);

    /* move on to the end of this node */
    while (xmlTextReaderNodeType (parser->xml) != XML_READER_TYPE_END_ELEMENT) {
	if (xmlTextReaderRead (parser->xml) != 1) {
	    xmlFree (value);
	    return NULL;
	}
    }

    /* consume the end element too */
    if (xmlTextReaderRead (parser->xml) != 1) {
	xmlFree (value);
	return NULL;
    }

    return value;
}

/*
 * _gweather_parser_get_localized_value:
 * @parser: a #GWeatherParser
 *
 * Looks at the name of the element @parser is currently pointing to, and
 * returns the content of either that node, or the translation for
 * it from the gettext domain for gweather locations.
 *
 * Return value: the localized (or unlocalized) text, as a
 * glib-allocated string, or %NULL if the node is empty.
 **/
char *
_gweather_parser_get_localized_value (GWeatherParser *parser)
{
    char *untranslated_value = _gweather_parser_get_value (parser);
    char *ret;

    ret = (char*) g_dgettext ("libgweather-locations", (char*) untranslated_value);

    ret = g_strdup (ret);
    xmlFree (untranslated_value);
    return ret;
}

char *
_gweather_parser_get_msgctxt_value (GWeatherParser *parser)
{
    const char *value;
    const char *name;

    while(xmlTextReaderMoveToNextAttribute(parser->xml)) {
	name = (const char *)xmlTextReaderConstName(parser->xml);
	if (!strcmp (name, "msgctxt")) {
	    value = (const char *)xmlTextReaderConstValue(parser->xml);
	    return g_strdup (value);
	}
    }

    return NULL;
}

static void
gweather_location_list_free (gpointer list)
{
    g_list_free_full (list, (GDestroyNotify) gweather_location_unref);
}

GWeatherParser *
_gweather_parser_new (void)
{
    int zlib_support;
    char *filename;
    GWeatherParser *parser;

    zlib_support = xmlHasFeature (XML_WITH_ZLIB);

    filename = g_build_filename (GWEATHER_XML_LOCATION_DIR, "Locations.xml", NULL);

    if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR) && zlib_support) {
        g_free (filename);
	filename = g_build_filename (GWEATHER_XML_LOCATION_DIR, "Locations.xml.gz", NULL);
    }

    parser = _gweather_parser_new_for_path (filename);

    g_free (filename);

    return parser;
}

GWeatherParser *
_gweather_parser_new_for_path (const char *path)
{
    GWeatherParser *parser;
    int keep_going;
    char *tagname, *format;
    time_t now;
    struct tm tm;

    _gweather_gettext_init ();

    parser = g_slice_new0 (GWeatherParser);

    /* Open the xml file containing the different locations */
    parser->xml = xmlNewTextReaderFilename (path);

    if (parser->xml == NULL)
	goto error_out;

    /* fast forward to the first element */
    do {
	/* if we encounter a problem here, exit right away */
	if (xmlTextReaderRead (parser->xml) != 1)
	    goto error_out;
    } while (xmlTextReaderNodeType (parser->xml) != XML_READER_TYPE_ELEMENT);

    /* check the name and format */
    tagname = (char *) xmlTextReaderName (parser->xml);
    keep_going = tagname && !strcmp (tagname, "gweather");
    xmlFree (tagname);

    if (!keep_going)
	goto error_out;

    format = (char *) xmlTextReaderGetAttribute (parser->xml, (xmlChar *) "format");
    keep_going = format && !strcmp (format, "1.0");
    xmlFree (format);

    if (!keep_going)
	goto error_out;

    /* Get timestamps for the start and end of this year */
    now = time (NULL);
    tm = *gmtime (&now);
    tm.tm_mon = 0;
    tm.tm_mday = 1;
    tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
    parser->year_start = mktime (&tm);
    tm.tm_year++;
    parser->year_end = mktime (&tm);

    parser->metar_code_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
						      NULL, gweather_location_list_free);
    parser->timezone_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
						    NULL, (GDestroyNotify) gweather_timezone_unref);
    parser->country_code_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
							NULL, (GDestroyNotify) gweather_location_unref);

    return parser;

error_out:
    _gweather_parser_free (parser);
    return NULL;
}

void
_gweather_parser_free (GWeatherParser *parser)
{
    if (parser->xml)
	xmlFreeTextReader (parser->xml);
    if (parser->metar_code_cache)
	g_hash_table_unref (parser->metar_code_cache);
    if (parser->country_code_cache)
	g_hash_table_unref (parser->country_code_cache);
    if (parser->timezone_cache)
	g_hash_table_unref (parser->timezone_cache);

    g_slice_free (GWeatherParser, parser);
}