midori/katze/katze-xbel.c
Christian Dywan d5a4f4cfd7 Move XBEL implementation to katze.
The implementation of XBEL is moved to katze, including the
appropriate api prefix and coding style changes. All relevant
uses are updated.
2008-01-06 01:44:37 +01:00

958 lines
27 KiB
C

/*
Copyright (C) 2007-2008 Christian Dywan <christian@twotoasts.de>
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.
See the file COPYING for the full license text.
*/
/**
* TODO:
* - Support info > metadata, alias, added, modified, visited
* - Compatibility: The Nokia 770 *requires* metadata and folder
* - Compatibility: Kazehakase's bookmarks
* - Compatibility: Epiphany's bookmarks
* - XML Indentation
**/
#include "katze-xbel.h"
#include <stdio.h>
#include <string.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "katze-utils.h"
struct _KatzeXbelItemPrivate
{
guint refs;
KatzeXbelItemKind kind;
KatzeXbelItem* parent;
GList* items; // folder
gboolean folded; // foolder
gchar* title; // !separator
gchar* desc; // folder and bookmark
gchar* href; // bookmark
//time_t added; // !separator
//time_t modfied; // bookmark
//time_t visited; // bookmark
} ;
#define KATZE_XBEL_ITEM_GET_PRIVATE(item) \
item->priv
// Private: Create a new item of a certain type
static KatzeXbelItem*
katze_xbel_item_new (KatzeXbelItemKind kind)
{
KatzeXbelItem* item = g_new (KatzeXbelItem, 1);
KATZE_XBEL_ITEM_GET_PRIVATE (item) = g_new (KatzeXbelItemPrivate, 1);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
priv->refs = 1;
priv->parent = NULL;
priv->kind = kind;
if (kind == KATZE_XBEL_ITEM_KIND_FOLDER)
{
priv->items = NULL;
priv->folded = TRUE;
}
if (kind != KATZE_XBEL_ITEM_KIND_SEPARATOR)
{
priv->title = NULL;
priv->desc = NULL;
}
if (kind == KATZE_XBEL_ITEM_KIND_BOOKMARK)
priv->href = g_strdup ("");
return item;
}
/**
* katze_xbel_bookmark_new:
*
* Create a new empty bookmark.
*
* Return value: a newly allocated bookmark
**/
KatzeXbelItem*
katze_xbel_bookmark_new (void)
{
return katze_xbel_item_new (KATZE_XBEL_ITEM_KIND_BOOKMARK);
}
static KatzeXbelItem*
katze_xbel_bookmark_from_xmlNodePtr (xmlNodePtr cur)
{
g_return_val_if_fail (cur, NULL);
KatzeXbelItem* bookmark = katze_xbel_bookmark_new ();
xmlChar* key = xmlGetProp (cur, (xmlChar*)"href");
katze_xbel_bookmark_set_href (bookmark, (gchar*)key);
cur = cur->xmlChildrenNode;
while (cur)
{
if (!xmlStrcmp (cur->name, (const xmlChar*)"title"))
{
xmlChar* key = xmlNodeGetContent (cur);
katze_xbel_item_set_title (bookmark, g_strstrip ((gchar*)key));
}
else if (!xmlStrcmp (cur->name, (const xmlChar*)"desc"))
{
xmlChar* key = xmlNodeGetContent (cur);
katze_xbel_item_set_desc (bookmark, g_strstrip ((gchar*)key));
}
cur = cur->next;
}
return bookmark;
}
/**
* katze_katze_xbel_separator_new:
*
* Create a new separator.
*
* The returned item must be freed eventually.
*
* Return value: a newly allocated separator.
**/
KatzeXbelItem*
katze_xbel_separator_new (void)
{
return katze_xbel_item_new (KATZE_XBEL_ITEM_KIND_SEPARATOR);
}
/**
* katze_xbel_folder_new:
*
* Create a new empty folder.
*
* The returned item must be freed eventually.
*
* Return value: a newly allocated folder.
**/
KatzeXbelItem*
katze_xbel_folder_new (void)
{
return katze_xbel_item_new (KATZE_XBEL_ITEM_KIND_FOLDER);
}
// Private: Create a folder from an xmlNodePtr
static KatzeXbelItem*
katze_xbel_folder_from_xmlNodePtr (xmlNodePtr cur)
{
g_return_val_if_fail (cur, NULL);
KatzeXbelItem* folder = katze_xbel_folder_new ();
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
xmlChar* key = xmlGetProp (cur, (xmlChar*)"folded");
if (key)
{
if (!g_ascii_strncasecmp ((gchar*)key, "yes", 3))
priv->folded = TRUE;
else if (!g_ascii_strncasecmp ((gchar*)key, "no", 2))
priv->folded = FALSE;
else
g_warning ("XBEL: Unknown value for folded.");
xmlFree (key);
}
cur = cur->xmlChildrenNode;
while (cur)
{
if (!xmlStrcmp (cur->name, (const xmlChar*)"title"))
{
xmlChar* key = xmlNodeGetContent (cur);
katze_assign (priv->title, g_strstrip ((gchar*)key));
}
else if (!xmlStrcmp (cur->name, (const xmlChar*)"desc"))
{
xmlChar* key = xmlNodeGetContent (cur);
katze_assign (priv->desc, g_strstrip ((gchar*)key));
}
else if (!xmlStrcmp (cur->name, (const xmlChar*)"folder"))
{
KatzeXbelItem* item = katze_xbel_folder_from_xmlNodePtr (cur);
KATZE_XBEL_ITEM_GET_PRIVATE (item)->parent = folder;
priv->items = g_list_prepend (priv->items, item);
}
else if (!xmlStrcmp (cur->name, (const xmlChar*)"bookmark"))
{
KatzeXbelItem* item = katze_xbel_bookmark_from_xmlNodePtr (cur);
priv->parent = folder;
priv->items = g_list_prepend (priv->items, item);
}
else if (!xmlStrcmp (cur->name, (const xmlChar*)"separator"))
{
KatzeXbelItem* item = katze_xbel_separator_new ();
priv->parent = folder;
priv->items = g_list_prepend (priv->items, item);
}
cur = cur->next;
}
// Prepending and reversing is faster than appending
priv->items = g_list_reverse (priv->items);
return folder;
}
// Private: Loads the contents from an xmlNodePtr into a folder.
static gboolean
katze_xbel_folder_from_xmlDocPtr (KatzeXbelItem* folder,
xmlDocPtr doc)
{
g_return_val_if_fail (katze_xbel_folder_is_empty (folder), FALSE);
g_return_val_if_fail (doc, FALSE);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
xmlNodePtr cur = xmlDocGetRootElement (doc);
xmlChar* version = xmlGetProp (cur, (xmlChar*)"version");
if (xmlStrcmp (version, (xmlChar*)"1.0"))
g_warning ("XBEL version is not 1.0.");
xmlFree (version);
katze_assign (priv->title, (gchar*)xmlGetProp (cur, (xmlChar*)"title"));
katze_assign (priv->desc, (gchar*)xmlGetProp (cur, (xmlChar*)"desc"));
if ((cur = xmlDocGetRootElement (doc)) == NULL)
{
// Empty document
return FALSE;
}
if (xmlStrcmp (cur->name, (const xmlChar*)"xbel"))
{
// Wrong document kind
return FALSE;
}
cur = cur->xmlChildrenNode;
while (cur)
{
KatzeXbelItem* item = NULL;
if (!xmlStrcmp (cur->name, (const xmlChar*)"folder"))
item = katze_xbel_folder_from_xmlNodePtr (cur);
else if (!xmlStrcmp (cur->name, (const xmlChar*)"bookmark"))
item = katze_xbel_bookmark_from_xmlNodePtr (cur);
else if (!xmlStrcmp (cur->name, (const xmlChar*)"separator"))
item = katze_xbel_separator_new ();
/*else if (!xmlStrcmp (cur->name, (const xmlChar*)"info"))
item = katze_xbel_parse_info (xbel, cur);*/
if (item)
{
KATZE_XBEL_ITEM_GET_PRIVATE (item)->parent = folder;
priv->items = g_list_prepend (priv->items, item);
}
cur = cur->next;
}
// Prepending and reversing is faster than appending
priv->items = g_list_reverse (priv->items);
return TRUE;
}
/**
* katze_xbel_item_ref:
* @item: a valid item
*
* Ref an KatzeXbelItem.
*
* Ref means that the reference count is increased by one.
*
* This has no effect on children of a folder.
**/
void
katze_xbel_item_ref (KatzeXbelItem* item)
{
g_return_if_fail (item);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
priv->refs++;
}
/**
* katze_xbel_item_unref:
* @item: a valid item
*
* Unref an KatzeXbelItem. If @item is a folder all of its children will also
* be unreffed automatically.
*
* Unref means that the reference count is decreased. If there are no
* references left, the memory will be freed and if needed removed from
* its containing folder.
**/
void
katze_xbel_item_unref (KatzeXbelItem* item)
{
g_return_if_fail (item);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
priv->refs--;
if (priv->refs)
return;
if (priv->parent)
katze_xbel_folder_remove_item (priv->parent, item);
if (priv->kind == KATZE_XBEL_ITEM_KIND_FOLDER)
{
guint n = katze_xbel_folder_get_n_items (item);
guint i;
for (i = 0; i < n; i++)
{
KatzeXbelItem* _item = katze_xbel_folder_get_nth_item (item, i);
KATZE_XBEL_ITEM_GET_PRIVATE (_item)->parent = NULL;
katze_xbel_item_unref (_item);
}
g_list_free (priv->items);
}
if (priv->kind != KATZE_XBEL_ITEM_KIND_SEPARATOR)
{
g_free (priv->title);
g_free (priv->desc);
}
if (priv->kind == KATZE_XBEL_ITEM_KIND_BOOKMARK)
g_free (priv->href);
g_free (item);
}
/**
* katze_xbel_item_copy:
* @item: the item to copy
*
* Copy an KatzeXbelItem.
*
* The returned item must be unreffed eventually.
*
* Return value: a copy of @item
**/
KatzeXbelItem*
katze_xbel_item_copy (KatzeXbelItem* item)
{
g_return_val_if_fail (item, NULL);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
KatzeXbelItem* copy = katze_xbel_item_new (priv->kind);
if (katze_xbel_item_is_folder (item))
{
guint n = katze_xbel_folder_get_n_items (item);
guint i;
for (i = 0; i < n; i++)
{
KatzeXbelItem* _item = katze_xbel_folder_get_nth_item (item, i);
katze_xbel_folder_append_item (copy, katze_xbel_item_copy (_item));
}
}
if (priv->kind != KATZE_XBEL_ITEM_KIND_SEPARATOR)
{
katze_xbel_item_set_title (copy, priv->title);
katze_xbel_item_set_desc (copy, priv->desc);
}
if (priv->kind == KATZE_XBEL_ITEM_KIND_BOOKMARK)
katze_xbel_bookmark_set_href (copy, priv->href);
return copy;
}
GType
katze_xbel_item_get_type (void)
{
static GType type = 0;
if (!type)
type = g_pointer_type_register_static ("katze_xbel_item");
return type;
}
/**
* katze_xbel_folder_append_item:
* @folder: a folder
* @item: the item to append
*
* Append the given item to a folder.
*
* The item is actually moved and must not be contained in another folder.
*
**/
void
katze_xbel_folder_append_item (KatzeXbelItem* folder,
KatzeXbelItem* item)
{
g_return_if_fail (!katze_xbel_item_get_parent (item));
g_return_if_fail (katze_xbel_item_is_folder (folder));
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
priv->items = g_list_append (priv->items, item);
KATZE_XBEL_ITEM_GET_PRIVATE (item)->parent = folder;
}
/**
* katze_xbel_folder_prepend_item:
* @folder: a folder
* @item: the item to prepend
*
* Prepend the given item to a folder.
*
* The item is actually moved and must not be contained in another folder.
*
**/
void
katze_xbel_folder_prepend_item (KatzeXbelItem* folder,
KatzeXbelItem* item)
{
g_return_if_fail (!katze_xbel_item_get_parent (item));
g_return_if_fail (katze_xbel_item_is_folder (folder));
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
priv->items = g_list_prepend (priv->items, item);
KATZE_XBEL_ITEM_GET_PRIVATE (item)->parent = folder;
}
/**
* katze_xbel_folder_remove_item:
* @folder: a folder
* @item: the item to remove
*
* Remove the given @item from a @folder.
**/
void
katze_xbel_folder_remove_item (KatzeXbelItem* folder,
KatzeXbelItem* item)
{
g_return_if_fail (item);
g_return_if_fail (katze_xbel_item_get_parent(folder) != item);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
// Fortunately we know that items are unique
priv->items = g_list_remove (priv->items, item);
KATZE_XBEL_ITEM_GET_PRIVATE (item)->parent = NULL;
}
/**
* katze_xbel_folder_get_n_items:
* @folder: a folder
*
* Retrieve the number of items contained in the given @folder.
*
* Return value: number of items
**/
guint
katze_xbel_folder_get_n_items (KatzeXbelItem* folder)
{
g_return_val_if_fail (katze_xbel_item_is_folder (folder), 0);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
return g_list_length (priv->items);
}
/**
* katze_xbel_folder_get_nth_item:
* @folder: a folder
* @n: the index of an item
*
* Retrieve an item contained in the given @folder by its index.
*
* Return value: the item at the given index or %NULL
**/
KatzeXbelItem*
katze_xbel_folder_get_nth_item (KatzeXbelItem* folder,
guint n)
{
g_return_val_if_fail (katze_xbel_item_is_folder(folder), NULL);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
return (KatzeXbelItem*) g_list_nth_data (priv->items, n);
}
/**
* katze_xbel_folder_is_empty:
* @folder: A folder.
*
* Determines wether or not a folder contains no items. This is significantly
* faster than katze_xbel_folder_get_n_items for this particular purpose.
*
* Return value: Wether the given @folder is folded.
**/
gboolean
katze_xbel_folder_is_empty (KatzeXbelItem* folder)
{
return !katze_xbel_folder_get_nth_item (folder, 0);
}
/**
* katze_xbel_folder_get_folded:
* @folder: A folder.
*
* Determines wether or not a folder is folded. If a folder is not folded
* it should not be exposed in a user interface.
*
* New folders are folded by default.
*
* Return value: Wether the given @folder is folded.
**/
gboolean
katze_xbel_folder_get_folded (KatzeXbelItem* folder)
{
g_return_val_if_fail (katze_xbel_item_is_folder (folder), TRUE);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
return priv->folded;
}
/**
* katze_xbel_item_get_kind:
* @item: A item.
*
* Determines the kind of an item.
*
* Return value: The kind of the given @item.
**/
KatzeXbelItemKind
katze_xbel_item_get_kind (KatzeXbelItem* item)
{
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
return priv->kind;
}
/**
* katze_xbel_item_get_parent:
* @item: A valid item.
*
* Retrieves the parent folder of an item.
*
* Return value: The parent folder of the given @item or %NULL.
**/
KatzeXbelItem*
katze_xbel_item_get_parent (KatzeXbelItem* item)
{
g_return_val_if_fail (item, NULL);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
return priv->parent;
}
/**
* katze_xbel_item_get_title:
* @item: A valid item.
*
* Retrieves the title of an item.
*
* Return value: The title of the given @item or %NULL.
**/
G_CONST_RETURN gchar*
katze_xbel_item_get_title (KatzeXbelItem* item)
{
g_return_val_if_fail (!katze_xbel_item_is_separator (item), NULL);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
return priv->title;
}
/**
* katze_xbel_item_get_desc:
* @item: A valid item.
*
* Retrieves the description of an item.
*
* Return value: The description of the @item or %NULL.
**/
G_CONST_RETURN gchar*
katze_xbel_item_get_desc (KatzeXbelItem* item)
{
g_return_val_if_fail (!katze_xbel_item_is_separator (item), NULL);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
return priv->desc;
}
/**
* katze_xbel_bookmark_get_href:
* @bookmark: A bookmark.
*
* Retrieves the uri of a bookmark. The value is guaranteed to not be %NULL.
*
* Return value: The uri of the @bookmark.
**/
G_CONST_RETURN gchar*
katze_xbel_bookmark_get_href (KatzeXbelItem* bookmark)
{
g_return_val_if_fail (katze_xbel_item_is_bookmark (bookmark), NULL);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (bookmark);
return priv->href;
}
/**
* katze_xbel_item_is_bookmark:
* @item: A valid item.
*
* Determines wether or not an item is a bookmark.
*
* Return value: %TRUE if the @item is a bookmark.
**/
gboolean
katze_xbel_item_is_bookmark (KatzeXbelItem* item)
{
g_return_val_if_fail (item, FALSE);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
return priv->kind == KATZE_XBEL_ITEM_KIND_BOOKMARK;
}
/**
* katze_xbel_item_is_separator:
* @item: A valid item.
*
* Determines wether or not an item is a separator.
*
* Return value: %TRUE if the @item is a separator.
**/
gboolean katze_xbel_item_is_separator (KatzeXbelItem* item)
{
g_return_val_if_fail (item, FALSE);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
return priv->kind == KATZE_XBEL_ITEM_KIND_SEPARATOR;
}
/**
* katze_xbel_item_is_folder:
* @item: A valid item.
*
* Determines wether or not an item is a folder.
*
* Return value: %TRUE if the item is a folder.
**/
gboolean
katze_xbel_item_is_folder (KatzeXbelItem* item)
{
g_return_val_if_fail (item, FALSE);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
return priv->kind == KATZE_XBEL_ITEM_KIND_FOLDER;
}
/**
* katze_xbel_folder_set_folded:
* @folder: A folder.
* @folded: TRUE if the folder is folded.
*
* Sets the foldedness of the @folder.
**/
void
katze_xbel_folder_set_folded (KatzeXbelItem* folder,
gboolean folded)
{
g_return_if_fail (katze_xbel_item_is_folder (folder));
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
priv->folded = folded;
}
/**
* katze_xbel_item_set_title:
* @item: A valid item.
* @title: A string to use for the title.
*
* Sets the title of the @item.
**/
void
katze_xbel_item_set_title (KatzeXbelItem* item,
const gchar* title)
{
g_return_if_fail (!katze_xbel_item_is_separator (item));
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
katze_assign (priv->title, g_strdup (title));
}
/**
* katze_xbel_item_set_desc:
* @item: A valid item.
* @title: A string to use for the description.
*
* Sets the description of the @item.
**/
void
katze_xbel_item_set_desc (KatzeXbelItem* item,
const gchar* desc)
{
g_return_if_fail (!katze_xbel_item_is_separator (item));
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
katze_assign (priv->desc, g_strdup (desc));
}
/**
* katze_xbel_bookmark_set_href:
* @bookmark: A bookmark.
* @href: A string containing a valid uri.
*
* Sets the uri of the bookmark.
*
* The uri must not be %NULL.
*
* This uri is not currently validated in any way. This may change in the future.
**/
void
katze_xbel_bookmark_set_href (KatzeXbelItem* bookmark,
const gchar* href)
{
g_return_if_fail (katze_xbel_item_is_bookmark (bookmark));
g_return_if_fail (href);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (bookmark);
katze_assign (priv->href, g_strdup (href));
}
gboolean
katze_xbel_folder_from_data (KatzeXbelItem* folder,
const gchar* data,
GError** error)
{
g_return_val_if_fail (katze_xbel_folder_is_empty (folder), FALSE);
g_return_val_if_fail (data, FALSE);
xmlDocPtr doc;
if((doc = xmlParseMemory (data, strlen (data))) == NULL)
{
// No valid xml or broken encoding
*error = g_error_new (KATZE_XBEL_ERROR, KATZE_XBEL_ERROR_READ,
"Malformed document.");
return FALSE;
}
if (!katze_xbel_folder_from_xmlDocPtr (folder, doc))
{
// Parsing failed
xmlFreeDoc(doc);
*error = g_error_new (KATZE_XBEL_ERROR, KATZE_XBEL_ERROR_READ,
"Malformed document.");
return FALSE;
}
xmlFreeDoc(doc);
return TRUE;
}
/**
* katze_xbel_folder_from_file:
* @folder: An empty folder.
* @file: A relative path to a file.
* @error: return location for a GError or %NULL
*
* Tries to load @file.
*
* Return value: %TRUE on success or %FALSE when an error occured.
**/
gboolean
katze_xbel_folder_from_file (KatzeXbelItem* folder,
const gchar* file,
GError** error)
{
g_return_val_if_fail (katze_xbel_folder_is_empty (folder), FALSE);
g_return_val_if_fail (file, FALSE);
if (!g_file_test (file, G_FILE_TEST_EXISTS))
{
// File doesn't exist
*error = g_error_new (G_FILE_ERROR, G_FILE_ERROR_NOENT,
"File not found.");
return FALSE;
}
xmlDocPtr doc;
if ((doc = xmlParseFile (file)) == NULL)
{
// No valid xml or broken encoding
*error = g_error_new (KATZE_XBEL_ERROR, KATZE_XBEL_ERROR_READ,
"Malformed document.");
return FALSE;
}
if (!katze_xbel_folder_from_xmlDocPtr (folder, doc))
{
// Parsing failed
xmlFreeDoc (doc);
*error = g_error_new (KATZE_XBEL_ERROR, KATZE_XBEL_ERROR_READ,
"Malformed document.");
return FALSE;
}
xmlFreeDoc (doc);
return TRUE;
}
/**
* katze_xbel_folder_from_data_dirs:
* @folder: An empty folder.
* @file: A relative path to a file.
* @full_path: return location for the full path of the file or %NULL
* @error: return location for a GError or %NULL
*
* Tries to load @file from the user data dir or any of the system data dirs.
*
* Return value: %TRUE on success or %FALSE when an error occured.
**/
gboolean
katze_xbel_folder_from_data_dirs (KatzeXbelItem* folder,
const gchar* file,
gchar** full_path,
GError** error)
{
g_return_val_if_fail (katze_xbel_folder_is_empty (folder), FALSE);
g_return_val_if_fail (file, FALSE);
// FIXME: Essentially unimplemented
*error = g_error_new (KATZE_XBEL_ERROR, KATZE_XBEL_ERROR_READ,
"Malformed document.");
return FALSE;
}
static gchar*
katze_xbel_xml_element (const gchar* name,
const gchar* value)
{
if (!value)
return g_strdup ("");
gchar* valueEscaped = g_markup_escape_text (value, -1);
gchar* markup = g_strdup_printf ("<%s>%s</%s>\n",
name, valueEscaped, name);
g_free (valueEscaped);
return markup;
}
static gchar*
katze_xbel_item_to_data (KatzeXbelItem* item)
{
g_return_val_if_fail (item, NULL);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
gchar* markup = NULL;
switch (priv->kind)
{
case KATZE_XBEL_ITEM_KIND_FOLDER:
{
GString* _markup = g_string_new (NULL);
guint n = katze_xbel_folder_get_n_items (item);
guint i;
for (i = 0; i < n; i++)
{
KatzeXbelItem* _item = katze_xbel_folder_get_nth_item (item, i);
gchar* item_markup = katze_xbel_item_to_data (_item);
g_string_append (_markup, item_markup);
g_free (item_markup);
}
gchar* folded = priv->folded ? NULL : g_strdup_printf (" folded=\"no\"");
gchar* title = katze_xbel_xml_element ("title", priv->title);
gchar* desc = katze_xbel_xml_element ("desc", priv->desc);
markup = g_strdup_printf ("<folder%s>\n%s%s%s</folder>\n",
folded ? folded : "",
title, desc,
g_string_free (_markup, FALSE));
g_free (folded);
g_free (title);
g_free (desc);
break;
}
case KATZE_XBEL_ITEM_KIND_BOOKMARK:
{
gchar* href_escaped = g_markup_escape_text (priv->href, -1);
gchar* href = g_strdup_printf (" href=\"%s\"", href_escaped);
g_free (href_escaped);
gchar* title = katze_xbel_xml_element ("title", priv->title);
gchar* desc = katze_xbel_xml_element ("desc", priv->desc);
markup = g_strdup_printf ("<bookmark%s>\n%s%s%s</bookmark>\n",
href,
title, desc,
"");
g_free (href);
g_free (title);
g_free (desc);
break;
}
case KATZE_XBEL_ITEM_KIND_SEPARATOR:
markup = g_strdup ("<separator/>\n");
break;
default:
g_warning ("XBEL: Unknown item kind");
}
return markup;
}
/**
* katze_xbel_folder_to_data:
* @folder: A folder.
* @length: return location for the length of the created string or %NULL
* @error: return location for a GError or %NULL
*
* Retrieve the contents of @folder as a string.
*
* Return value: %TRUE on success or %FALSE when an error occured.
**/
gchar*
katze_xbel_folder_to_data (KatzeXbelItem* folder,
gsize* length,
GError** error)
{
g_return_val_if_fail (katze_xbel_item_is_folder (folder), FALSE);
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
GString* inner_markup = g_string_new (NULL);
guint n = katze_xbel_folder_get_n_items (folder);
guint i;
for (i = 0; i < n; i++)
{
KatzeXbelItem* item = katze_xbel_folder_get_nth_item (folder, i);
gchar* sItem = katze_xbel_item_to_data (item);
g_string_append (inner_markup, sItem);
g_free (sItem);
}
gchar* title = katze_xbel_xml_element ("title", priv->title);
gchar* desc = katze_xbel_xml_element ("desc", priv->desc);
gchar* outer_markup;
outer_markup = g_strdup_printf (
"%s%s<xbel version=\"1.0\">\n%s%s%s</xbel>\n",
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
"<!DOCTYPE xbel PUBLIC \"+//IDN python.org//DTD "
"XML Bookmark Exchange Language 1.0//EN//XML\" "
"\"http://www.python.org/topics/xml/dtds/xbel-1.0.dtd\">\n",
title,
desc,
g_string_free (inner_markup, FALSE));
g_free (title);
g_free (desc);
if (length)
*length = strlen (outer_markup);
return outer_markup;
}
/**
* katze_xbel_folder_to_file:
* @folder: A folder.
* @file: The destination filename.
* @error: return location for a GError or %NULL
*
* Write the contents of @folder to the specified file, creating it if necessary.
*
* Return value: %TRUE on success or %FALSE when an error occured.
**/
gboolean
katze_xbel_folder_to_file (KatzeXbelItem* folder,
const gchar* file,
GError** error)
{
g_return_val_if_fail (file, FALSE);
gchar* data;
if (!(data = katze_xbel_folder_to_data (folder, NULL, error)))
return FALSE;
FILE* fp;
if(!(fp = fopen (file, "w")))
{
*error = g_error_new (G_FILE_ERROR, G_FILE_ERROR_ACCES,
"Writing failed.");
return FALSE;
}
fputs (data, fp);
fclose (fp);
g_free (data);
return TRUE;
}