summaryrefslogtreecommitdiff
path: root/libmateweather/parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmateweather/parser.c')
-rw-r--r--libmateweather/parser.c263
1 files changed, 263 insertions, 0 deletions
diff --git a/libmateweather/parser.c b/libmateweather/parser.c
new file mode 100644
index 0000000..9e4bb63
--- /dev/null
+++ b/libmateweather/parser.c
@@ -0,0 +1,263 @@
+/* -*- 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
+
+#define MATEWEATHER_I_KNOW_THIS_IS_UNSTABLE
+#include "weather-priv.h"
+
+#include "parser.h"
+
+#include <string.h>
+#include <glib.h>
+#include <libxml/xmlreader.h>
+
+/**
+ * mateweather_parser_get_value:
+ * @parser: a #MateWeatherParser
+ *
+ * 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 *
+mateweather_parser_get_value (MateWeatherParser *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;
+}
+
+/**
+ * mateweather_parser_get_localized_value:
+ * @parser: a #MateWeatherParser
+ *
+ * Looks at the name of the element @parser is currently pointing to, and
+ * returns the content of either that node, or a following node with
+ * the same name but an "xml:lang" attribute naming one of the locale
+ * languages. Leaves @parser pointing to the next node after the last
+ * consecutive element with the same name as the original element.
+ *
+ * Return value: the localized (or unlocalized) text, as a
+ * libxml-allocated string, or %NULL if the node is empty.
+ **/
+char *
+mateweather_parser_get_localized_value (MateWeatherParser *parser)
+{
+ const char *this_language;
+ int best_match = INT_MAX;
+ const char *lang, *tagname, *next_tagname;
+ gboolean keep_going;
+ char *name = NULL;
+ int i;
+
+ tagname = (const char *) xmlTextReaderConstName (parser->xml);
+
+ do {
+ /* First let's get the language */
+ lang = (const char *) xmlTextReaderConstXmlLang (parser->xml);
+
+ if (lang == NULL)
+ this_language = "C";
+ else
+ this_language = lang;
+
+ /* the next "node" is text node containing the actual name */
+ if (xmlTextReaderRead (parser->xml) != 1) {
+ if (name)
+ xmlFree (name);
+ return NULL;
+ }
+
+ for (i = 0; parser->locales[i] && i < best_match; i++) {
+ if (!strcmp (parser->locales[i], this_language)) {
+ /* if we've already encounted a less accurate
+ translation, then free it */
+ g_free (name);
+
+ name = (char *) xmlTextReaderValue (parser->xml);
+ best_match = i;
+
+ break;
+ }
+ }
+
+ /* Skip to close tag */
+ while (xmlTextReaderNodeType (parser->xml) != XML_READER_TYPE_END_ELEMENT) {
+ if (xmlTextReaderRead (parser->xml) != 1) {
+ xmlFree (name);
+ return NULL;
+ }
+ }
+
+ /* Skip junk */
+ do {
+ if (xmlTextReaderRead (parser->xml) != 1) {
+ xmlFree (name);
+ return NULL;
+ }
+ } while (xmlTextReaderNodeType (parser->xml) != XML_READER_TYPE_ELEMENT &&
+ xmlTextReaderNodeType (parser->xml) != XML_READER_TYPE_END_ELEMENT);
+
+ /* if the next tag has the same name then keep going */
+ next_tagname = (const char *) xmlTextReaderConstName (parser->xml);
+ keep_going = !strcmp (next_tagname, tagname);
+
+ } while (keep_going);
+
+ return name;
+}
+
+MateWeatherParser *
+mateweather_parser_new (gboolean use_regions)
+{
+ MateWeatherParser *parser;
+ int zlib_support;
+ int i, keep_going;
+ char *filename;
+ char *tagname, *format;
+ time_t now;
+ struct tm tm;
+
+ parser = g_slice_new0 (MateWeatherParser);
+ parser->use_regions = use_regions;
+ parser->locales = g_get_language_names ();
+
+ zlib_support = xmlHasFeature (XML_WITH_ZLIB);
+
+ /* First try to load a locale-specific XML. It's much faster. */
+ filename = NULL;
+ for (i = 0; parser->locales[i] != NULL; i++) {
+ filename = g_strdup_printf ("%s/Locations.%s.xml",
+ MATEWEATHER_XML_LOCATION_DIR,
+ parser->locales[i]);
+
+ if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+ break;
+
+ g_free (filename);
+ filename = NULL;
+
+ if (!zlib_support)
+ continue;
+
+ filename = g_strdup_printf ("%s/Locations.%s.xml.gz",
+ MATEWEATHER_XML_LOCATION_DIR,
+ parser->locales[i]);
+
+ if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+ break;
+
+ g_free (filename);
+ filename = NULL;
+ }
+
+ /* Fall back on the file containing either all translations, or only
+ * the english names (depending on the configure flags).
+ */
+ if (!filename)
+ filename = g_build_filename (MATEWEATHER_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 (MATEWEATHER_XML_LOCATION_DIR, "Locations.xml.gz", NULL);
+ }
+
+ /* Open the xml file containing the different locations */
+ parser->xml = xmlNewTextReaderFilename (filename);
+ g_free (filename);
+
+ 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, "mateweather");
+ 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);
+
+ return parser;
+
+error_out:
+ mateweather_parser_free (parser);
+ return NULL;
+}
+
+void
+mateweather_parser_free (MateWeatherParser *parser)
+{
+ if (parser->xml)
+ xmlFreeTextReader (parser->xml);
+ g_slice_free (MateWeatherParser, parser);
+}