/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * appcast.c * Copyright (C) John Stebbins 2008 * * appcast.c is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. */ #include #include #include #include #include "icon_tools.h" #include "plist.h" #include "values.h" enum { A_NONE = 0, A_DESCRIPTION, A_ENCLOSURE, A_ITEM, }; typedef struct { gchar *tag; gint id; } tag_map_t; static tag_map_t tag_map[] = { {"sparkle:releaseNotesLink", A_DESCRIPTION}, {"enclosure", A_ENCLOSURE}, {"item", A_ITEM}, }; #define TAG_MAP_SZ (sizeof(tag_map)/sizeof(tag_map_t)) typedef struct { gchar *key; gchar *value; GQueue *stack; GQueue *tag_stack; GString *description; gchar *build; gchar *version; gboolean item; } parse_data_t; static const gchar* lookup_attr_value( const gchar *name, const gchar **attr_names, const gchar **attr_values) { gint ii; if (attr_names == NULL) return NULL; for (ii = 0; attr_names[ii] != NULL; ii++) { if (strcmp(name, attr_names[ii]) == 0) return attr_values[ii]; } return NULL; } static void start_element( GMarkupParseContext *ctx, const gchar *tag, const gchar **attr_names, const gchar **attr_values, gpointer ud, GError **error) { parse_data_t *pd = (parse_data_t*)ud; union { gint id; gpointer pid; } id; gint ii; for (ii = 0; ii < TAG_MAP_SZ; ii++) { if (strcmp(tag, tag_map[ii].tag) == 0) { id.id = tag_map[ii].id; break; } } if (ii == TAG_MAP_SZ) { g_debug("Unrecognized start tag (%s)", tag); id.id = A_NONE; } g_queue_push_head(pd->tag_stack, id.pid); switch (id.id) { case A_ITEM: { pd->item = TRUE; } break; case A_ENCLOSURE: { const gchar *build, *version; build = lookup_attr_value( "sparkle:version", attr_names, attr_values); version = lookup_attr_value( "sparkle:shortVersionString", attr_names, attr_values); if (build) pd->build = g_strdup(build); if (version) pd->version = g_strdup(version); } break; } } static void end_element( GMarkupParseContext *ctx, const gchar *tag, gpointer ud, GError **error) { parse_data_t *pd = (parse_data_t*)ud; gint id; union { gint id; gpointer pid; } start_id; gint ii; for (ii = 0; ii < TAG_MAP_SZ; ii++) { if (strcmp(tag, tag_map[ii].tag) == 0) { id = tag_map[ii].id; break; } } if (ii == TAG_MAP_SZ) { g_debug("Unrecognized end tag (%s)", tag); id = A_NONE; } start_id.pid = g_queue_pop_head(pd->tag_stack); if (start_id.id != id) g_warning("start tag != end tag: (%s %d) %d", tag, start_id.id, id); switch (id) { case A_ITEM: { pd->item = FALSE; } break; default: { } break; } } static void text_data( GMarkupParseContext *ctx, const gchar *text, gsize len, gpointer ud, GError **error) { parse_data_t *pd = (parse_data_t*)ud; union { gint id; gpointer pid; } start_id; start_id.pid = g_queue_peek_head(pd->tag_stack); switch (start_id.id) { case A_DESCRIPTION: { if (pd->item) { g_string_append(pd->description, text); } } break; default: { if (pd->value) g_free(pd->value); pd->value = g_strdup(text); } break; } } static void passthrough( GMarkupParseContext *ctx, const gchar *text, gsize len, gpointer ud, GError **error) { //parse_data_t *pd = (parse_data_t*)ud; //g_debug("passthrough %s", text); } static void parse_error(GMarkupParseContext *ctx, GError *error, gpointer ud) { g_warning("Resource parse error: %s", error->message); } // This is required or the parser crashes static void destroy_notify(gpointer data) { // Do nothing //g_debug("destroy parser"); } void ghb_appcast_parse(gchar *buf, gchar **desc, gchar **build, gchar **version) { GMarkupParseContext *ctx; GMarkupParser parser; parse_data_t pd; GError *err = NULL; gint len; gchar *start; //gchar tmp[4096] // Skip junk at beginning of buffer start = strstr(buf, ""); if (glitch) *glitch = 0; *build = pd.build; *version = pd.version; }