Revert addition of g_key_file_has_key_full
[glib.git] / glib / gbookmarkfile.c
blobdb2a8bcb1f284133751d4a2ac0ea68142766a6ef
1 /* gbookmarkfile.c: parsing and building desktop bookmarks
3 * Copyright (C) 2005-2006 Emmanuele Bassi
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 #include "config.h"
22 #include "gbookmarkfile.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <locale.h>
30 #include <time.h>
31 #include <stdarg.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
38 #include "gconvert.h"
39 #include "gdataset.h"
40 #include "gerror.h"
41 #include "gfileutils.h"
42 #include "ghash.h"
43 #include "glibintl.h"
44 #include "glist.h"
45 #include "gslist.h"
46 #include "gmain.h"
47 #include "gmarkup.h"
48 #include "gmem.h"
49 #include "gmessages.h"
50 #include "gshell.h"
51 #include "gslice.h"
52 #include "gstdio.h"
53 #include "gstring.h"
54 #include "gstrfuncs.h"
55 #include "gtimer.h"
56 #include "gutils.h"
59 /**
60 * SECTION:bookmarkfile
61 * @title: Bookmark file parser
62 * @short_description: parses files containing bookmarks
64 * GBookmarkFile lets you parse, edit or create files containing bookmarks
65 * to URI, along with some meta-data about the resource pointed by the URI
66 * like its MIME type, the application that is registering the bookmark and
67 * the icon that should be used to represent the bookmark. The data is stored
68 * using the
69 * <ulink url="http://www.gnome.org/~ebassi/bookmark-spec">Desktop Bookmark
70 * Specification</ulink>.
72 * The syntax of the bookmark files is described in detail inside the Desktop
73 * Bookmark Specification, here is a quick summary: bookmark files use a
74 * sub-class of the <ulink url="">XML Bookmark Exchange Language</ulink>
75 * specification, consisting of valid UTF-8 encoded XML, under the
76 * <literal>xbel</literal> root element; each bookmark is stored inside a
77 * <literal>bookmark</literal> element, using its URI: no relative paths can
78 * be used inside a bookmark file. The bookmark may have a user defined title
79 * and description, to be used instead of the URI. Under the
80 * <literal>metadata</literal> element, with its <literal>owner</literal>
81 * attribute set to <literal>http://freedesktop.org</literal>, is stored the
82 * meta-data about a resource pointed by its URI. The meta-data consists of
83 * the resource's MIME type; the applications that have registered a bookmark;
84 * the groups to which a bookmark belongs to; a visibility flag, used to set
85 * the bookmark as "private" to the applications and groups that has it
86 * registered; the URI and MIME type of an icon, to be used when displaying
87 * the bookmark inside a GUI.
88 * |[<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../glib/tests/bookmarks.xbel"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include>]|
90 * A bookmark file might contain more than one bookmark; each bookmark
91 * is accessed through its URI.
93 * The important caveat of bookmark files is that when you add a new
94 * bookmark you must also add the application that is registering it, using
95 * g_bookmark_file_add_application() or g_bookmark_file_set_app_info().
96 * If a bookmark has no applications then it won't be dumped when creating
97 * the on disk representation, using g_bookmark_file_to_data() or
98 * g_bookmark_file_to_file().
100 * The #GBookmarkFile parser was added in GLib 2.12.
103 /* XBEL 1.0 standard entities */
104 #define XBEL_VERSION "1.0"
105 #define XBEL_DTD_NICK "xbel"
106 #define XBEL_DTD_SYSTEM "+//IDN python.org//DTD XML Bookmark " \
107 "Exchange Language 1.0//EN//XML"
109 #define XBEL_DTD_URI "http://www.python.org/topics/xml/dtds/xbel-1.0.dtd"
111 #define XBEL_ROOT_ELEMENT "xbel"
112 #define XBEL_FOLDER_ELEMENT "folder" /* unused */
113 #define XBEL_BOOKMARK_ELEMENT "bookmark"
114 #define XBEL_ALIAS_ELEMENT "alias" /* unused */
115 #define XBEL_SEPARATOR_ELEMENT "separator" /* unused */
116 #define XBEL_TITLE_ELEMENT "title"
117 #define XBEL_DESC_ELEMENT "desc"
118 #define XBEL_INFO_ELEMENT "info"
119 #define XBEL_METADATA_ELEMENT "metadata"
121 #define XBEL_VERSION_ATTRIBUTE "version"
122 #define XBEL_FOLDED_ATTRIBUTE "folded" /* unused */
123 #define XBEL_OWNER_ATTRIBUTE "owner"
124 #define XBEL_ADDED_ATTRIBUTE "added"
125 #define XBEL_VISITED_ATTRIBUTE "visited"
126 #define XBEL_MODIFIED_ATTRIBUTE "modified"
127 #define XBEL_ID_ATTRIBUTE "id"
128 #define XBEL_HREF_ATTRIBUTE "href"
129 #define XBEL_REF_ATTRIBUTE "ref" /* unused */
131 #define XBEL_YES_VALUE "yes"
132 #define XBEL_NO_VALUE "no"
134 /* Desktop bookmark spec entities */
135 #define BOOKMARK_METADATA_OWNER "http://freedesktop.org"
137 #define BOOKMARK_NAMESPACE_NAME "bookmark"
138 #define BOOKMARK_NAMESPACE_URI "http://www.freedesktop.org/standards/desktop-bookmarks"
140 #define BOOKMARK_GROUPS_ELEMENT "groups"
141 #define BOOKMARK_GROUP_ELEMENT "group"
142 #define BOOKMARK_APPLICATIONS_ELEMENT "applications"
143 #define BOOKMARK_APPLICATION_ELEMENT "application"
144 #define BOOKMARK_ICON_ELEMENT "icon"
145 #define BOOKMARK_PRIVATE_ELEMENT "private"
147 #define BOOKMARK_NAME_ATTRIBUTE "name"
148 #define BOOKMARK_EXEC_ATTRIBUTE "exec"
149 #define BOOKMARK_COUNT_ATTRIBUTE "count"
150 #define BOOKMARK_TIMESTAMP_ATTRIBUTE "timestamp" /* deprecated by "modified" */
151 #define BOOKMARK_MODIFIED_ATTRIBUTE "modified"
152 #define BOOKMARK_HREF_ATTRIBUTE "href"
153 #define BOOKMARK_TYPE_ATTRIBUTE "type"
155 /* Shared MIME Info entities */
156 #define MIME_NAMESPACE_NAME "mime"
157 #define MIME_NAMESPACE_URI "http://www.freedesktop.org/standards/shared-mime-info"
158 #define MIME_TYPE_ELEMENT "mime-type"
159 #define MIME_TYPE_ATTRIBUTE "type"
162 typedef struct _BookmarkAppInfo BookmarkAppInfo;
163 typedef struct _BookmarkMetadata BookmarkMetadata;
164 typedef struct _BookmarkItem BookmarkItem;
165 typedef struct _ParseData ParseData;
167 struct _BookmarkAppInfo
169 gchar *name;
170 gchar *exec;
172 guint count;
174 time_t stamp;
177 struct _BookmarkMetadata
179 gchar *mime_type;
181 GList *groups;
183 GList *applications;
184 GHashTable *apps_by_name;
186 gchar *icon_href;
187 gchar *icon_mime;
189 guint is_private : 1;
192 struct _BookmarkItem
194 gchar *uri;
196 gchar *title;
197 gchar *description;
199 time_t added;
200 time_t modified;
201 time_t visited;
203 BookmarkMetadata *metadata;
206 struct _GBookmarkFile
208 gchar *title;
209 gchar *description;
211 /* we store our items in a list and keep a copy inside
212 * an hash table for faster lookup performances
214 GList *items;
215 GHashTable *items_by_uri;
218 /* parser state machine */
219 enum
221 STATE_STARTED = 0,
223 STATE_ROOT,
224 STATE_BOOKMARK,
225 STATE_TITLE,
226 STATE_DESC,
227 STATE_INFO,
228 STATE_METADATA,
229 STATE_APPLICATIONS,
230 STATE_APPLICATION,
231 STATE_GROUPS,
232 STATE_GROUP,
233 STATE_MIME,
234 STATE_ICON,
236 STATE_FINISHED
239 static void g_bookmark_file_init (GBookmarkFile *bookmark);
240 static void g_bookmark_file_clear (GBookmarkFile *bookmark);
241 static gboolean g_bookmark_file_parse (GBookmarkFile *bookmark,
242 const gchar *buffer,
243 gsize length,
244 GError **error);
245 static gchar * g_bookmark_file_dump (GBookmarkFile *bookmark,
246 gsize *length,
247 GError **error);
248 static BookmarkItem *g_bookmark_file_lookup_item (GBookmarkFile *bookmark,
249 const gchar *uri);
250 static void g_bookmark_file_add_item (GBookmarkFile *bookmark,
251 BookmarkItem *item,
252 GError **error);
254 static time_t timestamp_from_iso8601 (const gchar *iso_date);
255 static gchar * timestamp_to_iso8601 (time_t timestamp);
257 /********************************
258 * BookmarkAppInfo *
260 * Application metadata storage *
261 ********************************/
262 static BookmarkAppInfo *
263 bookmark_app_info_new (const gchar *name)
265 BookmarkAppInfo *retval;
267 g_warn_if_fail (name != NULL);
269 retval = g_slice_new (BookmarkAppInfo);
271 retval->name = g_strdup (name);
272 retval->exec = NULL;
273 retval->count = 0;
274 retval->stamp = 0;
276 return retval;
279 static void
280 bookmark_app_info_free (BookmarkAppInfo *app_info)
282 if (!app_info)
283 return;
285 g_free (app_info->name);
286 g_free (app_info->exec);
288 g_slice_free (BookmarkAppInfo, app_info);
291 static gchar *
292 bookmark_app_info_dump (BookmarkAppInfo *app_info)
294 gchar *retval;
295 gchar *name, *exec, *modified, *count;
297 g_warn_if_fail (app_info != NULL);
299 if (app_info->count == 0)
300 return NULL;
302 name = g_markup_escape_text (app_info->name, -1);
303 exec = g_markup_escape_text (app_info->exec, -1);
304 modified = timestamp_to_iso8601 (app_info->stamp);
305 count = g_strdup_printf ("%u", app_info->count);
307 retval = g_strconcat (" "
308 "<" BOOKMARK_NAMESPACE_NAME ":" BOOKMARK_APPLICATION_ELEMENT
309 " " BOOKMARK_NAME_ATTRIBUTE "=\"", name, "\""
310 " " BOOKMARK_EXEC_ATTRIBUTE "=\"", exec, "\""
311 " " BOOKMARK_MODIFIED_ATTRIBUTE "=\"", modified, "\""
312 " " BOOKMARK_COUNT_ATTRIBUTE "=\"", count, "\"/>\n",
313 NULL);
315 g_free (name);
316 g_free (exec);
317 g_free (modified);
318 g_free (count);
320 return retval;
324 /***********************
325 * BookmarkMetadata *
327 * Metadata storage *
328 ***********************/
329 static BookmarkMetadata *
330 bookmark_metadata_new (void)
332 BookmarkMetadata *retval;
334 retval = g_slice_new (BookmarkMetadata);
336 retval->mime_type = NULL;
338 retval->groups = NULL;
340 retval->applications = NULL;
341 retval->apps_by_name = g_hash_table_new_full (g_str_hash,
342 g_str_equal,
343 NULL,
344 NULL);
346 retval->is_private = FALSE;
348 retval->icon_href = NULL;
349 retval->icon_mime = NULL;
351 return retval;
354 static void
355 bookmark_metadata_free (BookmarkMetadata *metadata)
357 if (!metadata)
358 return;
360 g_free (metadata->mime_type);
362 if (metadata->groups)
364 g_list_foreach (metadata->groups,
365 (GFunc) g_free,
366 NULL);
367 g_list_free (metadata->groups);
370 if (metadata->applications)
372 g_list_foreach (metadata->applications,
373 (GFunc) bookmark_app_info_free,
374 NULL);
375 g_list_free (metadata->applications);
378 g_hash_table_destroy (metadata->apps_by_name);
380 g_free (metadata->icon_href);
381 g_free (metadata->icon_mime);
383 g_slice_free (BookmarkMetadata, metadata);
386 static gchar *
387 bookmark_metadata_dump (BookmarkMetadata *metadata)
389 GString *retval;
390 gchar *buffer;
392 if (!metadata->applications)
393 return NULL;
395 retval = g_string_sized_new (1024);
397 /* metadata container */
398 g_string_append (retval,
400 "<" XBEL_METADATA_ELEMENT
401 " " XBEL_OWNER_ATTRIBUTE "=\"" BOOKMARK_METADATA_OWNER
402 "\">\n");
404 /* mime type */
405 if (metadata->mime_type) {
406 buffer = g_strconcat (" "
407 "<" MIME_NAMESPACE_NAME ":" MIME_TYPE_ELEMENT " "
408 MIME_TYPE_ATTRIBUTE "=\"", metadata->mime_type, "\"/>\n",
409 NULL);
410 g_string_append (retval, buffer);
411 g_free (buffer);
414 if (metadata->groups)
416 GList *l;
418 /* open groups container */
419 g_string_append (retval,
421 "<" BOOKMARK_NAMESPACE_NAME
422 ":" BOOKMARK_GROUPS_ELEMENT ">\n");
424 for (l = g_list_last (metadata->groups); l != NULL; l = l->prev)
426 gchar *group_name;
428 group_name = g_markup_escape_text ((gchar *) l->data, -1);
429 buffer = g_strconcat (" "
430 "<" BOOKMARK_NAMESPACE_NAME
431 ":" BOOKMARK_GROUP_ELEMENT ">",
432 group_name,
433 "</" BOOKMARK_NAMESPACE_NAME
434 ":" BOOKMARK_GROUP_ELEMENT ">\n", NULL);
435 g_string_append (retval, buffer);
437 g_free (buffer);
438 g_free (group_name);
441 /* close groups container */
442 g_string_append (retval,
444 "</" BOOKMARK_NAMESPACE_NAME
445 ":" BOOKMARK_GROUPS_ELEMENT ">\n");
448 if (metadata->applications)
450 GList *l;
452 /* open applications container */
453 g_string_append (retval,
455 "<" BOOKMARK_NAMESPACE_NAME
456 ":" BOOKMARK_APPLICATIONS_ELEMENT ">\n");
458 for (l = g_list_last (metadata->applications); l != NULL; l = l->prev)
460 BookmarkAppInfo *app_info = (BookmarkAppInfo *) l->data;
461 gchar *app_data;
463 g_warn_if_fail (app_info != NULL);
465 app_data = bookmark_app_info_dump (app_info);
467 if (app_data)
469 retval = g_string_append (retval, app_data);
471 g_free (app_data);
475 /* close applications container */
476 g_string_append (retval,
478 "</" BOOKMARK_NAMESPACE_NAME
479 ":" BOOKMARK_APPLICATIONS_ELEMENT ">\n");
482 /* icon */
483 if (metadata->icon_href)
485 if (!metadata->icon_mime)
486 metadata->icon_mime = g_strdup ("application/octet-stream");
488 buffer = g_strconcat (" "
489 "<" BOOKMARK_NAMESPACE_NAME
490 ":" BOOKMARK_ICON_ELEMENT
491 " " BOOKMARK_HREF_ATTRIBUTE "=\"", metadata->icon_href,
492 "\" " BOOKMARK_TYPE_ATTRIBUTE "=\"", metadata->icon_mime, "\"/>\n", NULL);
493 g_string_append (retval, buffer);
495 g_free (buffer);
498 /* private hint */
499 if (metadata->is_private)
500 g_string_append (retval,
502 "<" BOOKMARK_NAMESPACE_NAME
503 ":" BOOKMARK_PRIVATE_ELEMENT "/>\n");
505 /* close metadata container */
506 g_string_append (retval,
508 "</" XBEL_METADATA_ELEMENT ">\n");
510 return g_string_free (retval, FALSE);
513 /******************************************************
514 * BookmarkItem *
516 * Storage for a single bookmark item inside the list *
517 ******************************************************/
518 static BookmarkItem *
519 bookmark_item_new (const gchar *uri)
521 BookmarkItem *item;
523 g_warn_if_fail (uri != NULL);
525 item = g_slice_new (BookmarkItem);
526 item->uri = g_strdup (uri);
528 item->title = NULL;
529 item->description = NULL;
531 item->added = (time_t) -1;
532 item->modified = (time_t) -1;
533 item->visited = (time_t) -1;
535 item->metadata = NULL;
537 return item;
540 static void
541 bookmark_item_free (BookmarkItem *item)
543 if (!item)
544 return;
546 g_free (item->uri);
547 g_free (item->title);
548 g_free (item->description);
550 if (item->metadata)
551 bookmark_metadata_free (item->metadata);
553 g_slice_free (BookmarkItem, item);
556 static gchar *
557 bookmark_item_dump (BookmarkItem *item)
559 GString *retval;
560 gchar *added, *visited, *modified;
561 gchar *escaped_uri;
562 gchar *buffer;
564 /* at this point, we must have at least a registered application; if we don't
565 * we don't screw up the bookmark file, and just skip this item
567 if (!item->metadata || !item->metadata->applications)
569 g_warning ("Item for URI '%s' has no registered applications: skipping.\n", item->uri);
570 return NULL;
573 retval = g_string_sized_new (4096);
575 added = timestamp_to_iso8601 (item->added);
576 modified = timestamp_to_iso8601 (item->modified);
577 visited = timestamp_to_iso8601 (item->visited);
579 escaped_uri = g_markup_escape_text (item->uri, -1);
581 buffer = g_strconcat (" <"
582 XBEL_BOOKMARK_ELEMENT
584 XBEL_HREF_ATTRIBUTE "=\"", escaped_uri, "\" "
585 XBEL_ADDED_ATTRIBUTE "=\"", added, "\" "
586 XBEL_MODIFIED_ATTRIBUTE "=\"", modified, "\" "
587 XBEL_VISITED_ATTRIBUTE "=\"", visited, "\">\n",
588 NULL);
590 g_string_append (retval, buffer);
592 g_free (escaped_uri);
593 g_free (visited);
594 g_free (modified);
595 g_free (added);
596 g_free (buffer);
598 if (item->title)
600 gchar *escaped_title;
602 escaped_title = g_markup_escape_text (item->title, -1);
603 buffer = g_strconcat (" "
604 "<" XBEL_TITLE_ELEMENT ">",
605 escaped_title,
606 "</" XBEL_TITLE_ELEMENT ">\n",
607 NULL);
608 g_string_append (retval, buffer);
610 g_free (escaped_title);
611 g_free (buffer);
614 if (item->description)
616 gchar *escaped_desc;
618 escaped_desc = g_markup_escape_text (item->description, -1);
619 buffer = g_strconcat (" "
620 "<" XBEL_DESC_ELEMENT ">",
621 escaped_desc,
622 "</" XBEL_DESC_ELEMENT ">\n",
623 NULL);
624 g_string_append (retval, buffer);
626 g_free (escaped_desc);
627 g_free (buffer);
630 if (item->metadata)
632 gchar *metadata;
634 metadata = bookmark_metadata_dump (item->metadata);
635 if (metadata)
637 buffer = g_strconcat (" "
638 "<" XBEL_INFO_ELEMENT ">\n",
639 metadata,
641 "</" XBEL_INFO_ELEMENT ">\n",
642 NULL);
643 retval = g_string_append (retval, buffer);
645 g_free (buffer);
646 g_free (metadata);
650 g_string_append (retval, " </" XBEL_BOOKMARK_ELEMENT ">\n");
652 return g_string_free (retval, FALSE);
655 static BookmarkAppInfo *
656 bookmark_item_lookup_app_info (BookmarkItem *item,
657 const gchar *app_name)
659 g_warn_if_fail (item != NULL && app_name != NULL);
661 if (!item->metadata)
662 return NULL;
664 return g_hash_table_lookup (item->metadata->apps_by_name, app_name);
667 /*************************
668 * GBookmarkFile *
669 *************************/
671 static void
672 g_bookmark_file_init (GBookmarkFile *bookmark)
674 bookmark->title = NULL;
675 bookmark->description = NULL;
677 bookmark->items = NULL;
678 bookmark->items_by_uri = g_hash_table_new_full (g_str_hash,
679 g_str_equal,
680 NULL,
681 NULL);
684 static void
685 g_bookmark_file_clear (GBookmarkFile *bookmark)
687 g_free (bookmark->title);
688 g_free (bookmark->description);
690 if (bookmark->items)
692 g_list_foreach (bookmark->items,
693 (GFunc) bookmark_item_free,
694 NULL);
695 g_list_free (bookmark->items);
697 bookmark->items = NULL;
700 if (bookmark->items_by_uri)
702 g_hash_table_destroy (bookmark->items_by_uri);
704 bookmark->items_by_uri = NULL;
708 struct _ParseData
710 gint state;
712 GHashTable *namespaces;
714 GBookmarkFile *bookmark_file;
715 BookmarkItem *current_item;
718 static ParseData *
719 parse_data_new (void)
721 ParseData *retval;
723 retval = g_new (ParseData, 1);
725 retval->state = STATE_STARTED;
726 retval->namespaces = g_hash_table_new_full (g_str_hash, g_str_equal,
727 (GDestroyNotify) g_free,
728 (GDestroyNotify) g_free);
729 retval->bookmark_file = NULL;
730 retval->current_item = NULL;
732 return retval;
735 static void
736 parse_data_free (ParseData *parse_data)
738 g_hash_table_destroy (parse_data->namespaces);
740 g_free (parse_data);
743 #define IS_ATTRIBUTE(s,a) ((0 == strcmp ((s), (a))))
745 static void
746 parse_bookmark_element (GMarkupParseContext *context,
747 ParseData *parse_data,
748 const gchar **attribute_names,
749 const gchar **attribute_values,
750 GError **error)
752 const gchar *uri, *added, *modified, *visited;
753 const gchar *attr;
754 gint i;
755 BookmarkItem *item;
756 GError *add_error;
758 g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_BOOKMARK));
760 i = 0;
761 uri = added = modified = visited = NULL;
762 for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
764 if (IS_ATTRIBUTE (attr, XBEL_HREF_ATTRIBUTE))
765 uri = attribute_values[i];
766 else if (IS_ATTRIBUTE (attr, XBEL_ADDED_ATTRIBUTE))
767 added = attribute_values[i];
768 else if (IS_ATTRIBUTE (attr, XBEL_MODIFIED_ATTRIBUTE))
769 modified = attribute_values[i];
770 else if (IS_ATTRIBUTE (attr, XBEL_VISITED_ATTRIBUTE))
771 visited = attribute_values[i];
772 else
774 /* bookmark is defined by the XBEL spec, so we need
775 * to error out if the element has different or
776 * missing attributes
778 g_set_error (error, G_MARKUP_ERROR,
779 G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
780 _("Unexpected attribute '%s' for element '%s'"),
781 attr,
782 XBEL_BOOKMARK_ELEMENT);
783 return;
787 if (!uri)
789 g_set_error (error, G_MARKUP_ERROR,
790 G_MARKUP_ERROR_INVALID_CONTENT,
791 _("Attribute '%s' of element '%s' not found"),
792 XBEL_HREF_ATTRIBUTE,
793 XBEL_BOOKMARK_ELEMENT);
794 return;
797 g_warn_if_fail (parse_data->current_item == NULL);
799 item = bookmark_item_new (uri);
801 if (added)
802 item->added = timestamp_from_iso8601 (added);
804 if (modified)
805 item->modified = timestamp_from_iso8601 (modified);
807 if (visited)
808 item->visited = timestamp_from_iso8601 (visited);
810 add_error = NULL;
811 g_bookmark_file_add_item (parse_data->bookmark_file,
812 item,
813 &add_error);
814 if (add_error)
816 bookmark_item_free (item);
818 g_propagate_error (error, add_error);
820 return;
823 parse_data->current_item = item;
826 static void
827 parse_application_element (GMarkupParseContext *context,
828 ParseData *parse_data,
829 const gchar **attribute_names,
830 const gchar **attribute_values,
831 GError **error)
833 const gchar *name, *exec, *count, *stamp, *modified;
834 const gchar *attr;
835 gint i;
836 BookmarkItem *item;
837 BookmarkAppInfo *ai;
839 g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_APPLICATION));
841 i = 0;
842 name = exec = count = stamp = modified = NULL;
843 for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
845 if (IS_ATTRIBUTE (attr, BOOKMARK_NAME_ATTRIBUTE))
846 name = attribute_values[i];
847 else if (IS_ATTRIBUTE (attr, BOOKMARK_EXEC_ATTRIBUTE))
848 exec = attribute_values[i];
849 else if (IS_ATTRIBUTE (attr, BOOKMARK_COUNT_ATTRIBUTE))
850 count = attribute_values[i];
851 else if (IS_ATTRIBUTE (attr, BOOKMARK_TIMESTAMP_ATTRIBUTE))
852 stamp = attribute_values[i];
853 else if (IS_ATTRIBUTE (attr, BOOKMARK_MODIFIED_ATTRIBUTE))
854 modified = attribute_values[i];
857 /* the "name" and "exec" attributes are mandatory */
858 if (!name)
860 g_set_error (error, G_MARKUP_ERROR,
861 G_MARKUP_ERROR_INVALID_CONTENT,
862 _("Attribute '%s' of element '%s' not found"),
863 BOOKMARK_NAME_ATTRIBUTE,
864 BOOKMARK_APPLICATION_ELEMENT);
865 return;
868 if (!exec)
870 g_set_error (error, G_MARKUP_ERROR,
871 G_MARKUP_ERROR_INVALID_CONTENT,
872 _("Attribute '%s' of element '%s' not found"),
873 BOOKMARK_EXEC_ATTRIBUTE,
874 BOOKMARK_APPLICATION_ELEMENT);
875 return;
878 g_warn_if_fail (parse_data->current_item != NULL);
879 item = parse_data->current_item;
881 ai = bookmark_item_lookup_app_info (item, name);
882 if (!ai)
884 ai = bookmark_app_info_new (name);
886 if (!item->metadata)
887 item->metadata = bookmark_metadata_new ();
889 item->metadata->applications = g_list_prepend (item->metadata->applications, ai);
890 g_hash_table_replace (item->metadata->apps_by_name, ai->name, ai);
893 ai->exec = g_strdup (exec);
895 if (count)
896 ai->count = atoi (count);
897 else
898 ai->count = 1;
900 if (modified)
901 ai->stamp = timestamp_from_iso8601 (modified);
902 else
904 /* the timestamp attribute has been deprecated but we still parse
905 * it for backward compatibility
907 if (stamp)
908 ai->stamp = (time_t) atol (stamp);
909 else
910 ai->stamp = time (NULL);
914 static void
915 parse_mime_type_element (GMarkupParseContext *context,
916 ParseData *parse_data,
917 const gchar **attribute_names,
918 const gchar **attribute_values,
919 GError **error)
921 const gchar *type;
922 const gchar *attr;
923 gint i;
924 BookmarkItem *item;
926 g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_MIME));
928 i = 0;
929 type = NULL;
930 for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
932 if (IS_ATTRIBUTE (attr, MIME_TYPE_ATTRIBUTE))
933 type = attribute_values[i];
936 if (!type)
937 type = "application/octet-stream";
939 g_warn_if_fail (parse_data->current_item != NULL);
940 item = parse_data->current_item;
942 if (!item->metadata)
943 item->metadata = bookmark_metadata_new ();
945 item->metadata->mime_type = g_strdup (type);
948 static void
949 parse_icon_element (GMarkupParseContext *context,
950 ParseData *parse_data,
951 const gchar **attribute_names,
952 const gchar **attribute_values,
953 GError **error)
955 const gchar *href;
956 const gchar *type;
957 const gchar *attr;
958 gint i;
959 BookmarkItem *item;
961 g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_ICON));
963 i = 0;
964 href = NULL;
965 type = NULL;
966 for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
968 if (IS_ATTRIBUTE (attr, BOOKMARK_HREF_ATTRIBUTE))
969 href = attribute_values[i];
970 else if (IS_ATTRIBUTE (attr, BOOKMARK_TYPE_ATTRIBUTE))
971 type = attribute_values[i];
974 /* the "href" attribute is mandatory */
975 if (!href)
977 g_set_error (error, G_MARKUP_ERROR,
978 G_MARKUP_ERROR_INVALID_CONTENT,
979 _("Attribute '%s' of element '%s' not found"),
980 BOOKMARK_HREF_ATTRIBUTE,
981 BOOKMARK_ICON_ELEMENT);
982 return;
985 if (!type)
986 type = "application/octet-stream";
988 g_warn_if_fail (parse_data->current_item != NULL);
989 item = parse_data->current_item;
991 if (!item->metadata)
992 item->metadata = bookmark_metadata_new ();
994 item->metadata->icon_href = g_strdup (href);
995 item->metadata->icon_mime = g_strdup (type);
998 /* scans through the attributes of an element for the "xmlns" pragma, and
999 * adds any resulting namespace declaration to a per-parser hashtable, using
1000 * the namespace name as a key for the namespace URI; if no key was found,
1001 * the namespace is considered as default, and stored under the "default" key.
1003 * FIXME: this works on the assumption that the generator of the XBEL file
1004 * is either this code or is smart enough to place the namespace declarations
1005 * inside the main root node or inside the metadata node and does not redefine
1006 * a namespace inside an inner node; this does *not* conform to the
1007 * XML-NS standard, although is a close approximation. In order to make this
1008 * conformant to the XML-NS specification we should use a per-element
1009 * namespace table inside GMarkup and ask it to resolve the namespaces for us.
1011 static void
1012 map_namespace_to_name (ParseData *parse_data,
1013 const gchar **attribute_names,
1014 const gchar **attribute_values)
1016 const gchar *attr;
1017 gint i;
1019 g_warn_if_fail (parse_data != NULL);
1021 if (!attribute_names || !attribute_names[0])
1022 return;
1024 i = 0;
1025 for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
1027 if (g_str_has_prefix (attr, "xmlns"))
1029 gchar *namespace_name, *namespace_uri;
1030 gchar *p;
1032 p = g_utf8_strchr (attr, -1, ':');
1033 if (p)
1034 p = g_utf8_next_char (p);
1035 else
1036 p = "default";
1038 namespace_name = g_strdup (p);
1039 namespace_uri = g_strdup (attribute_values[i]);
1041 g_hash_table_replace (parse_data->namespaces,
1042 namespace_name,
1043 namespace_uri);
1048 /* checks whether @element_full is equal to @element.
1050 * if @namespace is set, it tries to resolve the namespace to a known URI,
1051 * and if found is prepended to the element name, from which is separated
1052 * using the character specified in the @sep parameter.
1054 static gboolean
1055 is_element_full (ParseData *parse_data,
1056 const gchar *element_full,
1057 const gchar *namespace,
1058 const gchar *element,
1059 const gchar sep)
1061 gchar *ns_uri, *ns_name;
1062 const gchar *p, *element_name;
1063 gboolean retval;
1065 g_warn_if_fail (parse_data != NULL);
1066 g_warn_if_fail (element_full != NULL);
1068 if (!element)
1069 return FALSE;
1071 /* no namespace requested: dumb element compare */
1072 if (!namespace)
1073 return (0 == strcmp (element_full, element));
1075 /* search for namespace separator; if none found, assume we are under the
1076 * default namespace, and set ns_name to our "default" marker; if no default
1077 * namespace has been set, just do a plain comparison between @full_element
1078 * and @element.
1080 p = g_utf8_strchr (element_full, -1, ':');
1081 if (p)
1083 ns_name = g_strndup (element_full, p - element_full);
1084 element_name = g_utf8_next_char (p);
1086 else
1088 ns_name = g_strdup ("default");
1089 element_name = element_full;
1092 ns_uri = g_hash_table_lookup (parse_data->namespaces, ns_name);
1093 if (!ns_uri)
1095 /* no default namespace found */
1096 g_free (ns_name);
1098 return (0 == strcmp (element_full, element));
1101 retval = (0 == strcmp (ns_uri, namespace) &&
1102 0 == strcmp (element_name, element));
1104 g_free (ns_name);
1106 return retval;
1109 #define IS_ELEMENT(p,s,e) (is_element_full ((p), (s), NULL, (e), '\0'))
1110 #define IS_ELEMENT_NS(p,s,n,e) (is_element_full ((p), (s), (n), (e), '|'))
1112 static void
1113 start_element_raw_cb (GMarkupParseContext *context,
1114 const gchar *element_name,
1115 const gchar **attribute_names,
1116 const gchar **attribute_values,
1117 gpointer user_data,
1118 GError **error)
1120 ParseData *parse_data = (ParseData *) user_data;
1122 /* we must check for namespace declarations first
1124 * XXX - we could speed up things by checking for namespace declarations
1125 * only on the root node, where they usually are; this would probably break
1126 * on streams not produced by us or by "smart" generators
1128 map_namespace_to_name (parse_data, attribute_names, attribute_values);
1130 switch (parse_data->state)
1132 case STATE_STARTED:
1133 if (IS_ELEMENT (parse_data, element_name, XBEL_ROOT_ELEMENT))
1135 const gchar *attr;
1136 gint i;
1138 i = 0;
1139 for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
1141 if ((IS_ATTRIBUTE (attr, XBEL_VERSION_ATTRIBUTE)) &&
1142 (0 == strcmp (attribute_values[i], XBEL_VERSION)))
1143 parse_data->state = STATE_ROOT;
1146 else
1147 g_set_error (error, G_MARKUP_ERROR,
1148 G_MARKUP_ERROR_INVALID_CONTENT,
1149 _("Unexpected tag '%s', tag '%s' expected"),
1150 element_name, XBEL_ROOT_ELEMENT);
1151 break;
1152 case STATE_ROOT:
1153 if (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT))
1154 parse_data->state = STATE_TITLE;
1155 else if (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT))
1156 parse_data->state = STATE_DESC;
1157 else if (IS_ELEMENT (parse_data, element_name, XBEL_BOOKMARK_ELEMENT))
1159 GError *inner_error = NULL;
1161 parse_data->state = STATE_BOOKMARK;
1163 parse_bookmark_element (context,
1164 parse_data,
1165 attribute_names,
1166 attribute_values,
1167 &inner_error);
1168 if (inner_error)
1169 g_propagate_error (error, inner_error);
1171 else
1172 g_set_error (error, G_MARKUP_ERROR,
1173 G_MARKUP_ERROR_INVALID_CONTENT,
1174 _("Unexpected tag '%s' inside '%s'"),
1175 element_name,
1176 XBEL_ROOT_ELEMENT);
1177 break;
1178 case STATE_BOOKMARK:
1179 if (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT))
1180 parse_data->state = STATE_TITLE;
1181 else if (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT))
1182 parse_data->state = STATE_DESC;
1183 else if (IS_ELEMENT (parse_data, element_name, XBEL_INFO_ELEMENT))
1184 parse_data->state = STATE_INFO;
1185 else
1186 g_set_error (error, G_MARKUP_ERROR,
1187 G_MARKUP_ERROR_INVALID_CONTENT,
1188 _("Unexpected tag '%s' inside '%s'"),
1189 element_name,
1190 XBEL_BOOKMARK_ELEMENT);
1191 break;
1192 case STATE_INFO:
1193 if (IS_ELEMENT (parse_data, element_name, XBEL_METADATA_ELEMENT))
1195 const gchar *attr;
1196 gint i;
1198 i = 0;
1199 for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
1201 if ((IS_ATTRIBUTE (attr, XBEL_OWNER_ATTRIBUTE)) &&
1202 (0 == strcmp (attribute_values[i], BOOKMARK_METADATA_OWNER)))
1204 parse_data->state = STATE_METADATA;
1206 if (!parse_data->current_item->metadata)
1207 parse_data->current_item->metadata = bookmark_metadata_new ();
1211 else
1212 g_set_error (error, G_MARKUP_ERROR,
1213 G_MARKUP_ERROR_INVALID_CONTENT,
1214 _("Unexpected tag '%s', tag '%s' expected"),
1215 element_name,
1216 XBEL_METADATA_ELEMENT);
1217 break;
1218 case STATE_METADATA:
1219 if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATIONS_ELEMENT))
1220 parse_data->state = STATE_APPLICATIONS;
1221 else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUPS_ELEMENT))
1222 parse_data->state = STATE_GROUPS;
1223 else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_PRIVATE_ELEMENT))
1224 parse_data->current_item->metadata->is_private = TRUE;
1225 else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_ICON_ELEMENT))
1227 GError *inner_error = NULL;
1229 parse_data->state = STATE_ICON;
1231 parse_icon_element (context,
1232 parse_data,
1233 attribute_names,
1234 attribute_values,
1235 &inner_error);
1236 if (inner_error)
1237 g_propagate_error (error, inner_error);
1239 else if (IS_ELEMENT_NS (parse_data, element_name, MIME_NAMESPACE_URI, MIME_TYPE_ELEMENT))
1241 GError *inner_error = NULL;
1243 parse_data->state = STATE_MIME;
1245 parse_mime_type_element (context,
1246 parse_data,
1247 attribute_names,
1248 attribute_values,
1249 &inner_error);
1250 if (inner_error)
1251 g_propagate_error (error, inner_error);
1253 else
1254 g_set_error (error, G_MARKUP_ERROR,
1255 G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1256 _("Unexpected tag '%s' inside '%s'"),
1257 element_name,
1258 XBEL_METADATA_ELEMENT);
1259 break;
1260 case STATE_APPLICATIONS:
1261 if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATION_ELEMENT))
1263 GError *inner_error = NULL;
1265 parse_data->state = STATE_APPLICATION;
1267 parse_application_element (context,
1268 parse_data,
1269 attribute_names,
1270 attribute_values,
1271 &inner_error);
1272 if (inner_error)
1273 g_propagate_error (error, inner_error);
1275 else
1276 g_set_error (error, G_MARKUP_ERROR,
1277 G_MARKUP_ERROR_INVALID_CONTENT,
1278 _("Unexpected tag '%s', tag '%s' expected"),
1279 element_name,
1280 BOOKMARK_APPLICATION_ELEMENT);
1281 break;
1282 case STATE_GROUPS:
1283 if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUP_ELEMENT))
1284 parse_data->state = STATE_GROUP;
1285 else
1286 g_set_error (error, G_MARKUP_ERROR,
1287 G_MARKUP_ERROR_INVALID_CONTENT,
1288 _("Unexpected tag '%s', tag '%s' expected"),
1289 element_name,
1290 BOOKMARK_GROUP_ELEMENT);
1291 break;
1292 case STATE_ICON:
1293 if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_ICON_ELEMENT))
1295 GError *inner_error = NULL;
1297 parse_icon_element (context,
1298 parse_data,
1299 attribute_names,
1300 attribute_values,
1301 &inner_error);
1302 if (inner_error)
1303 g_propagate_error (error, inner_error);
1305 else
1306 g_set_error (error, G_MARKUP_ERROR,
1307 G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1308 _("Unexpected tag '%s' inside '%s'"),
1309 element_name,
1310 XBEL_METADATA_ELEMENT);
1311 break;
1312 default:
1313 g_warn_if_reached ();
1314 break;
1318 static void
1319 end_element_raw_cb (GMarkupParseContext *context,
1320 const gchar *element_name,
1321 gpointer user_data,
1322 GError **error)
1324 ParseData *parse_data = (ParseData *) user_data;
1326 if (IS_ELEMENT (parse_data, element_name, XBEL_ROOT_ELEMENT))
1327 parse_data->state = STATE_FINISHED;
1328 else if (IS_ELEMENT (parse_data, element_name, XBEL_BOOKMARK_ELEMENT))
1330 parse_data->current_item = NULL;
1332 parse_data->state = STATE_ROOT;
1334 else if ((IS_ELEMENT (parse_data, element_name, XBEL_INFO_ELEMENT)) ||
1335 (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT)) ||
1336 (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT)))
1338 if (parse_data->current_item)
1339 parse_data->state = STATE_BOOKMARK;
1340 else
1341 parse_data->state = STATE_ROOT;
1343 else if (IS_ELEMENT (parse_data, element_name, XBEL_METADATA_ELEMENT))
1344 parse_data->state = STATE_INFO;
1345 else if (IS_ELEMENT_NS (parse_data, element_name,
1346 BOOKMARK_NAMESPACE_URI,
1347 BOOKMARK_APPLICATION_ELEMENT))
1348 parse_data->state = STATE_APPLICATIONS;
1349 else if (IS_ELEMENT_NS (parse_data, element_name,
1350 BOOKMARK_NAMESPACE_URI,
1351 BOOKMARK_GROUP_ELEMENT))
1352 parse_data->state = STATE_GROUPS;
1353 else if ((IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATIONS_ELEMENT)) ||
1354 (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUPS_ELEMENT)) ||
1355 (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_PRIVATE_ELEMENT)) ||
1356 (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_ICON_ELEMENT)) ||
1357 (IS_ELEMENT_NS (parse_data, element_name, MIME_NAMESPACE_URI, MIME_TYPE_ELEMENT)))
1358 parse_data->state = STATE_METADATA;
1361 static void
1362 text_raw_cb (GMarkupParseContext *context,
1363 const gchar *text,
1364 gsize length,
1365 gpointer user_data,
1366 GError **error)
1368 ParseData *parse_data = (ParseData *) user_data;
1369 gchar *payload;
1371 payload = g_strndup (text, length);
1373 switch (parse_data->state)
1375 case STATE_TITLE:
1376 if (parse_data->current_item)
1378 g_free (parse_data->current_item->title);
1379 parse_data->current_item->title = g_strdup (payload);
1381 else
1383 g_free (parse_data->bookmark_file->title);
1384 parse_data->bookmark_file->title = g_strdup (payload);
1386 break;
1387 case STATE_DESC:
1388 if (parse_data->current_item)
1390 g_free (parse_data->current_item->description);
1391 parse_data->current_item->description = g_strdup (payload);
1393 else
1395 g_free (parse_data->bookmark_file->description);
1396 parse_data->bookmark_file->description = g_strdup (payload);
1398 break;
1399 case STATE_GROUP:
1401 GList *groups;
1403 g_warn_if_fail (parse_data->current_item != NULL);
1405 if (!parse_data->current_item->metadata)
1406 parse_data->current_item->metadata = bookmark_metadata_new ();
1408 groups = parse_data->current_item->metadata->groups;
1409 parse_data->current_item->metadata->groups = g_list_prepend (groups, g_strdup (payload));
1411 break;
1412 case STATE_ROOT:
1413 case STATE_BOOKMARK:
1414 case STATE_INFO:
1415 case STATE_METADATA:
1416 case STATE_APPLICATIONS:
1417 case STATE_APPLICATION:
1418 case STATE_GROUPS:
1419 case STATE_MIME:
1420 case STATE_ICON:
1421 break;
1422 default:
1423 g_warn_if_reached ();
1424 break;
1427 g_free (payload);
1430 static const GMarkupParser markup_parser =
1432 start_element_raw_cb, /* start_element */
1433 end_element_raw_cb, /* end_element */
1434 text_raw_cb, /* text */
1435 NULL, /* passthrough */
1436 NULL
1439 static gboolean
1440 g_bookmark_file_parse (GBookmarkFile *bookmark,
1441 const gchar *buffer,
1442 gsize length,
1443 GError **error)
1445 GMarkupParseContext *context;
1446 ParseData *parse_data;
1447 GError *parse_error, *end_error;
1448 gboolean retval;
1450 g_warn_if_fail (bookmark != NULL);
1452 if (!buffer)
1453 return FALSE;
1455 if (length == (gsize) -1)
1456 length = strlen (buffer);
1458 parse_data = parse_data_new ();
1459 parse_data->bookmark_file = bookmark;
1461 context = g_markup_parse_context_new (&markup_parser,
1463 parse_data,
1464 (GDestroyNotify) parse_data_free);
1466 parse_error = NULL;
1467 retval = g_markup_parse_context_parse (context,
1468 buffer,
1469 length,
1470 &parse_error);
1471 if (!retval)
1473 g_propagate_error (error, parse_error);
1475 return FALSE;
1478 end_error = NULL;
1479 retval = g_markup_parse_context_end_parse (context, &end_error);
1480 if (!retval)
1482 g_propagate_error (error, end_error);
1484 return FALSE;
1487 g_markup_parse_context_free (context);
1489 return TRUE;
1492 static gchar *
1493 g_bookmark_file_dump (GBookmarkFile *bookmark,
1494 gsize *length,
1495 GError **error)
1497 GString *retval;
1498 gchar *buffer;
1499 GList *l;
1501 retval = g_string_sized_new (4096);
1503 g_string_append (retval,
1504 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1505 #if 0
1506 /* XXX - do we really need the doctype? */
1507 "<!DOCTYPE " XBEL_DTD_NICK "\n"
1508 " PUBLIC \"" XBEL_DTD_SYSTEM "\"\n"
1509 " \"" XBEL_DTD_URI "\">\n"
1510 #endif
1511 "<" XBEL_ROOT_ELEMENT " " XBEL_VERSION_ATTRIBUTE "=\"" XBEL_VERSION "\"\n"
1512 " xmlns:" BOOKMARK_NAMESPACE_NAME "=\"" BOOKMARK_NAMESPACE_URI "\"\n"
1513 " xmlns:" MIME_NAMESPACE_NAME "=\"" MIME_NAMESPACE_URI "\"\n>");
1515 if (bookmark->title)
1517 gchar *escaped_title;
1519 escaped_title = g_markup_escape_text (bookmark->title, -1);
1521 buffer = g_strconcat (" "
1522 "<" XBEL_TITLE_ELEMENT ">",
1523 escaped_title,
1524 "</" XBEL_TITLE_ELEMENT ">\n", NULL);
1526 g_string_append (retval, buffer);
1528 g_free (buffer);
1529 g_free (escaped_title);
1532 if (bookmark->description)
1534 gchar *escaped_desc;
1536 escaped_desc = g_markup_escape_text (bookmark->description, -1);
1538 buffer = g_strconcat (" "
1539 "<" XBEL_DESC_ELEMENT ">",
1540 escaped_desc,
1541 "</" XBEL_DESC_ELEMENT ">\n", NULL);
1542 g_string_append (retval, buffer);
1544 g_free (buffer);
1545 g_free (escaped_desc);
1548 if (!bookmark->items)
1549 goto out;
1550 else
1551 retval = g_string_append (retval, "\n");
1553 /* the items are stored in reverse order */
1554 for (l = g_list_last (bookmark->items);
1555 l != NULL;
1556 l = l->prev)
1558 BookmarkItem *item = (BookmarkItem *) l->data;
1559 gchar *item_dump;
1561 item_dump = bookmark_item_dump (item);
1562 if (!item_dump)
1563 continue;
1565 retval = g_string_append (retval, item_dump);
1567 g_free (item_dump);
1570 out:
1571 g_string_append (retval, "</" XBEL_ROOT_ELEMENT ">");
1573 if (length)
1574 *length = retval->len;
1576 return g_string_free (retval, FALSE);
1579 /**************
1580 * Misc *
1581 **************/
1583 /* converts a Unix timestamp in a ISO 8601 compliant string; you
1584 * should free the returned string.
1586 static gchar *
1587 timestamp_to_iso8601 (time_t timestamp)
1589 GTimeVal stamp;
1591 if (timestamp == (time_t) -1)
1592 g_get_current_time (&stamp);
1593 else
1595 stamp.tv_sec = timestamp;
1596 stamp.tv_usec = 0;
1599 return g_time_val_to_iso8601 (&stamp);
1602 static time_t
1603 timestamp_from_iso8601 (const gchar *iso_date)
1605 GTimeVal stamp;
1607 if (!g_time_val_from_iso8601 (iso_date, &stamp))
1608 return (time_t) -1;
1610 return (time_t) stamp.tv_sec;
1615 GQuark
1616 g_bookmark_file_error_quark (void)
1618 return g_quark_from_static_string ("g-bookmark-file-error-quark");
1623 /********************
1624 * Public API *
1625 ********************/
1628 * g_bookmark_file_new:
1630 * Creates a new empty #GBookmarkFile object.
1632 * Use g_bookmark_file_load_from_file(), g_bookmark_file_load_from_data()
1633 * or g_bookmark_file_load_from_data_dirs() to read an existing bookmark
1634 * file.
1636 * Return value: an empty #GBookmarkFile
1638 * Since: 2.12
1640 GBookmarkFile *
1641 g_bookmark_file_new (void)
1643 GBookmarkFile *bookmark;
1645 bookmark = g_new (GBookmarkFile, 1);
1647 g_bookmark_file_init (bookmark);
1649 return bookmark;
1653 * g_bookmark_file_free:
1654 * @bookmark: a #GBookmarkFile
1656 * Frees a #GBookmarkFile.
1658 * Since: 2.12
1660 void
1661 g_bookmark_file_free (GBookmarkFile *bookmark)
1663 if (!bookmark)
1664 return;
1666 g_bookmark_file_clear (bookmark);
1668 g_free (bookmark);
1672 * g_bookmark_file_load_from_data:
1673 * @bookmark: an empty #GBookmarkFile struct
1674 * @data: desktop bookmarks loaded in memory
1675 * @length: the length of @data in bytes
1676 * @error: return location for a #GError, or %NULL
1678 * Loads a bookmark file from memory into an empty #GBookmarkFile
1679 * structure. If the object cannot be created then @error is set to a
1680 * #GBookmarkFileError.
1682 * Return value: %TRUE if a desktop bookmark could be loaded.
1684 * Since: 2.12
1686 gboolean
1687 g_bookmark_file_load_from_data (GBookmarkFile *bookmark,
1688 const gchar *data,
1689 gsize length,
1690 GError **error)
1692 GError *parse_error;
1693 gboolean retval;
1695 g_return_val_if_fail (bookmark != NULL, FALSE);
1697 if (length == (gsize) -1)
1698 length = strlen (data);
1700 if (bookmark->items)
1702 g_bookmark_file_clear (bookmark);
1703 g_bookmark_file_init (bookmark);
1706 parse_error = NULL;
1707 retval = g_bookmark_file_parse (bookmark, data, length, &parse_error);
1708 if (!retval)
1710 g_propagate_error (error, parse_error);
1712 return FALSE;
1715 return TRUE;
1719 * g_bookmark_file_load_from_file:
1720 * @bookmark: an empty #GBookmarkFile struct
1721 * @filename: the path of a filename to load, in the GLib file name encoding
1722 * @error: return location for a #GError, or %NULL
1724 * Loads a desktop bookmark file into an empty #GBookmarkFile structure.
1725 * If the file could not be loaded then @error is set to either a #GFileError
1726 * or #GBookmarkFileError.
1728 * Return value: %TRUE if a desktop bookmark file could be loaded
1730 * Since: 2.12
1732 gboolean
1733 g_bookmark_file_load_from_file (GBookmarkFile *bookmark,
1734 const gchar *filename,
1735 GError **error)
1737 gchar *buffer;
1738 gsize len;
1739 GError *read_error;
1740 gboolean retval;
1742 g_return_val_if_fail (bookmark != NULL, FALSE);
1743 g_return_val_if_fail (filename != NULL, FALSE);
1745 read_error = NULL;
1746 g_file_get_contents (filename, &buffer, &len, &read_error);
1747 if (read_error)
1749 g_propagate_error (error, read_error);
1751 return FALSE;
1754 read_error = NULL;
1755 retval = g_bookmark_file_load_from_data (bookmark,
1756 buffer,
1757 len,
1758 &read_error);
1759 if (read_error)
1761 g_propagate_error (error, read_error);
1763 g_free (buffer);
1765 return FALSE;
1768 g_free (buffer);
1770 return retval;
1774 /* Iterates through all the directories in *dirs trying to
1775 * find file. When it successfully locates file, returns a
1776 * string its absolute path. It also leaves the unchecked
1777 * directories in *dirs. You should free the returned string
1779 * Adapted from gkeyfile.c
1781 static gchar *
1782 find_file_in_data_dirs (const gchar *file,
1783 gchar ***dirs,
1784 GError **error)
1786 gchar **data_dirs, *data_dir, *path;
1788 path = NULL;
1790 if (dirs == NULL)
1791 return NULL;
1793 data_dirs = *dirs;
1794 path = NULL;
1795 while (data_dirs && (data_dir = *data_dirs) && !path)
1797 gchar *candidate_file, *sub_dir;
1799 candidate_file = (gchar *) file;
1800 sub_dir = g_strdup ("");
1801 while (candidate_file != NULL && !path)
1803 gchar *p;
1805 path = g_build_filename (data_dir, sub_dir,
1806 candidate_file, NULL);
1808 candidate_file = strchr (candidate_file, '-');
1810 if (candidate_file == NULL)
1811 break;
1813 candidate_file++;
1815 g_free (sub_dir);
1816 sub_dir = g_strndup (file, candidate_file - file - 1);
1818 for (p = sub_dir; *p != '\0'; p++)
1820 if (*p == '-')
1821 *p = G_DIR_SEPARATOR;
1824 g_free (sub_dir);
1825 data_dirs++;
1828 *dirs = data_dirs;
1830 if (!path)
1832 g_set_error_literal (error, G_BOOKMARK_FILE_ERROR,
1833 G_BOOKMARK_FILE_ERROR_FILE_NOT_FOUND,
1834 _("No valid bookmark file found in data dirs"));
1836 return NULL;
1839 return path;
1844 * g_bookmark_file_load_from_data_dirs:
1845 * @bookmark: a #GBookmarkFile
1846 * @file: a relative path to a filename to open and parse
1847 * @full_path: return location for a string containing the full path
1848 * of the file, or %NULL
1849 * @error: return location for a #GError, or %NULL
1851 * This function looks for a desktop bookmark file named @file in the
1852 * paths returned from g_get_user_data_dir() and g_get_system_data_dirs(),
1853 * loads the file into @bookmark and returns the file's full path in
1854 * @full_path. If the file could not be loaded then an %error is
1855 * set to either a #GFileError or #GBookmarkFileError.
1857 * Return value: %TRUE if a key file could be loaded, %FALSE othewise
1859 * Since: 2.12
1861 gboolean
1862 g_bookmark_file_load_from_data_dirs (GBookmarkFile *bookmark,
1863 const gchar *file,
1864 gchar **full_path,
1865 GError **error)
1867 GError *file_error = NULL;
1868 gchar **all_data_dirs, **data_dirs;
1869 const gchar *user_data_dir;
1870 const gchar * const * system_data_dirs;
1871 gsize i, j;
1872 gchar *output_path;
1873 gboolean found_file;
1875 g_return_val_if_fail (bookmark != NULL, FALSE);
1876 g_return_val_if_fail (!g_path_is_absolute (file), FALSE);
1878 user_data_dir = g_get_user_data_dir ();
1879 system_data_dirs = g_get_system_data_dirs ();
1880 all_data_dirs = g_new0 (gchar *, g_strv_length ((gchar **)system_data_dirs) + 2);
1882 i = 0;
1883 all_data_dirs[i++] = g_strdup (user_data_dir);
1885 j = 0;
1886 while (system_data_dirs[j] != NULL)
1887 all_data_dirs[i++] = g_strdup (system_data_dirs[j++]);
1889 found_file = FALSE;
1890 data_dirs = all_data_dirs;
1891 output_path = NULL;
1892 while (*data_dirs != NULL && !found_file)
1894 g_free (output_path);
1896 output_path = find_file_in_data_dirs (file, &data_dirs, &file_error);
1898 if (file_error)
1900 g_propagate_error (error, file_error);
1901 break;
1904 found_file = g_bookmark_file_load_from_file (bookmark,
1905 output_path,
1906 &file_error);
1907 if (file_error)
1909 g_propagate_error (error, file_error);
1910 break;
1914 if (found_file && full_path)
1915 *full_path = output_path;
1916 else
1917 g_free (output_path);
1919 g_strfreev (all_data_dirs);
1921 return found_file;
1926 * g_bookmark_file_to_data:
1927 * @bookmark: a #GBookmarkFile
1928 * @length: return location for the length of the returned string, or %NULL
1929 * @error: return location for a #GError, or %NULL
1931 * This function outputs @bookmark as a string.
1933 * Return value: a newly allocated string holding
1934 * the contents of the #GBookmarkFile
1936 * Since: 2.12
1938 gchar *
1939 g_bookmark_file_to_data (GBookmarkFile *bookmark,
1940 gsize *length,
1941 GError **error)
1943 GError *write_error = NULL;
1944 gchar *retval;
1946 g_return_val_if_fail (bookmark != NULL, NULL);
1948 retval = g_bookmark_file_dump (bookmark, length, &write_error);
1949 if (write_error)
1951 g_propagate_error (error, write_error);
1953 return NULL;
1956 return retval;
1960 * g_bookmark_file_to_file:
1961 * @bookmark: a #GBookmarkFile
1962 * @filename: path of the output file
1963 * @error: return location for a #GError, or %NULL
1965 * This function outputs @bookmark into a file. The write process is
1966 * guaranteed to be atomic by using g_file_set_contents() internally.
1968 * Return value: %TRUE if the file was successfully written.
1970 * Since: 2.12
1972 gboolean
1973 g_bookmark_file_to_file (GBookmarkFile *bookmark,
1974 const gchar *filename,
1975 GError **error)
1977 gchar *data;
1978 GError *data_error, *write_error;
1979 gsize len;
1980 gboolean retval;
1982 g_return_val_if_fail (bookmark != NULL, FALSE);
1983 g_return_val_if_fail (filename != NULL, FALSE);
1985 data_error = NULL;
1986 data = g_bookmark_file_to_data (bookmark, &len, &data_error);
1987 if (data_error)
1989 g_propagate_error (error, data_error);
1991 return FALSE;
1994 write_error = NULL;
1995 g_file_set_contents (filename, data, len, &write_error);
1996 if (write_error)
1998 g_propagate_error (error, write_error);
2000 retval = FALSE;
2002 else
2003 retval = TRUE;
2005 g_free (data);
2007 return retval;
2010 static BookmarkItem *
2011 g_bookmark_file_lookup_item (GBookmarkFile *bookmark,
2012 const gchar *uri)
2014 g_warn_if_fail (bookmark != NULL && uri != NULL);
2016 return g_hash_table_lookup (bookmark->items_by_uri, uri);
2019 /* this function adds a new item to the list */
2020 static void
2021 g_bookmark_file_add_item (GBookmarkFile *bookmark,
2022 BookmarkItem *item,
2023 GError **error)
2025 g_warn_if_fail (bookmark != NULL);
2026 g_warn_if_fail (item != NULL);
2028 /* this should never happen; and if it does, then we are
2029 * screwing up something big time.
2031 if (G_UNLIKELY (g_bookmark_file_has_item (bookmark, item->uri)))
2033 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2034 G_BOOKMARK_FILE_ERROR_INVALID_URI,
2035 _("A bookmark for URI '%s' already exists"),
2036 item->uri);
2037 return;
2040 bookmark->items = g_list_prepend (bookmark->items, item);
2042 g_hash_table_replace (bookmark->items_by_uri,
2043 item->uri,
2044 item);
2046 if (item->added == (time_t) -1)
2047 item->added = time (NULL);
2049 if (item->modified == (time_t) -1)
2050 item->modified = time (NULL);
2054 * g_bookmark_file_remove_item:
2055 * @bookmark: a #GBookmarkFile
2056 * @uri: a valid URI
2057 * @error: return location for a #GError, or %NULL
2059 * Removes the bookmark for @uri from the bookmark file @bookmark.
2061 * Return value: %TRUE if the bookmark was removed successfully.
2063 * Since: 2.12
2065 gboolean
2066 g_bookmark_file_remove_item (GBookmarkFile *bookmark,
2067 const gchar *uri,
2068 GError **error)
2070 BookmarkItem *item;
2072 g_return_val_if_fail (bookmark != NULL, FALSE);
2073 g_return_val_if_fail (uri != NULL, FALSE);
2075 item = g_bookmark_file_lookup_item (bookmark, uri);
2077 if (!item)
2079 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2080 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2081 _("No bookmark found for URI '%s'"),
2082 uri);
2083 return FALSE;
2086 bookmark->items = g_list_remove (bookmark->items, item);
2087 g_hash_table_remove (bookmark->items_by_uri, item->uri);
2089 bookmark_item_free (item);
2091 return TRUE;
2095 * g_bookmark_file_has_item:
2096 * @bookmark: a #GBookmarkFile
2097 * @uri: a valid URI
2099 * Looks whether the desktop bookmark has an item with its URI set to @uri.
2101 * Return value: %TRUE if @uri is inside @bookmark, %FALSE otherwise
2103 * Since: 2.12
2105 gboolean
2106 g_bookmark_file_has_item (GBookmarkFile *bookmark,
2107 const gchar *uri)
2109 g_return_val_if_fail (bookmark != NULL, FALSE);
2110 g_return_val_if_fail (uri != NULL, FALSE);
2112 return (NULL != g_hash_table_lookup (bookmark->items_by_uri, uri));
2116 * g_bookmark_file_get_uris:
2117 * @bookmark: a #GBookmarkFile
2118 * @length: return location for the number of returned URIs, or %NULL
2120 * Returns all URIs of the bookmarks in the bookmark file @bookmark.
2121 * The array of returned URIs will be %NULL-terminated, so @length may
2122 * optionally be %NULL.
2124 * Return value: a newly allocated %NULL-terminated array of strings.
2125 * Use g_strfreev() to free it.
2127 * Since: 2.12
2129 gchar **
2130 g_bookmark_file_get_uris (GBookmarkFile *bookmark,
2131 gsize *length)
2133 GList *l;
2134 gchar **uris;
2135 gsize i, n_items;
2137 g_return_val_if_fail (bookmark != NULL, NULL);
2139 n_items = g_list_length (bookmark->items);
2140 uris = g_new0 (gchar *, n_items + 1);
2142 /* the items are stored in reverse order, so we walk the list backward */
2143 for (l = g_list_last (bookmark->items), i = 0; l != NULL; l = l->prev)
2145 BookmarkItem *item = (BookmarkItem *) l->data;
2147 g_warn_if_fail (item != NULL);
2149 uris[i++] = g_strdup (item->uri);
2151 uris[i] = NULL;
2153 if (length)
2154 *length = i;
2156 return uris;
2160 * g_bookmark_file_set_title:
2161 * @bookmark: a #GBookmarkFile
2162 * @uri: a valid URI or %NULL
2163 * @title: a UTF-8 encoded string
2165 * Sets @title as the title of the bookmark for @uri inside the
2166 * bookmark file @bookmark.
2168 * If @uri is %NULL, the title of @bookmark is set.
2170 * If a bookmark for @uri cannot be found then it is created.
2172 * Since: 2.12
2174 void
2175 g_bookmark_file_set_title (GBookmarkFile *bookmark,
2176 const gchar *uri,
2177 const gchar *title)
2179 g_return_if_fail (bookmark != NULL);
2181 if (!uri)
2183 g_free (bookmark->title);
2184 bookmark->title = g_strdup (title);
2186 else
2188 BookmarkItem *item;
2190 item = g_bookmark_file_lookup_item (bookmark, uri);
2191 if (!item)
2193 item = bookmark_item_new (uri);
2194 g_bookmark_file_add_item (bookmark, item, NULL);
2197 g_free (item->title);
2198 item->title = g_strdup (title);
2200 item->modified = time (NULL);
2205 * g_bookmark_file_get_title:
2206 * @bookmark: a #GBookmarkFile
2207 * @uri: a valid URI or %NULL
2208 * @error: return location for a #GError, or %NULL
2210 * Returns the title of the bookmark for @uri.
2212 * If @uri is %NULL, the title of @bookmark is returned.
2214 * In the event the URI cannot be found, %NULL is returned and
2215 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2217 * Return value: a newly allocated string or %NULL if the specified
2218 * URI cannot be found.
2220 * Since: 2.12
2222 gchar *
2223 g_bookmark_file_get_title (GBookmarkFile *bookmark,
2224 const gchar *uri,
2225 GError **error)
2227 BookmarkItem *item;
2229 g_return_val_if_fail (bookmark != NULL, NULL);
2231 if (!uri)
2232 return g_strdup (bookmark->title);
2234 item = g_bookmark_file_lookup_item (bookmark, uri);
2235 if (!item)
2237 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2238 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2239 _("No bookmark found for URI '%s'"),
2240 uri);
2241 return NULL;
2244 return g_strdup (item->title);
2248 * g_bookmark_file_set_description:
2249 * @bookmark: a #GBookmarkFile
2250 * @uri: a valid URI or %NULL
2251 * @description: a string
2253 * Sets @description as the description of the bookmark for @uri.
2255 * If @uri is %NULL, the description of @bookmark is set.
2257 * If a bookmark for @uri cannot be found then it is created.
2259 * Since: 2.12
2261 void
2262 g_bookmark_file_set_description (GBookmarkFile *bookmark,
2263 const gchar *uri,
2264 const gchar *description)
2266 g_return_if_fail (bookmark != NULL);
2268 if (!uri)
2270 g_free (bookmark->description);
2271 bookmark->description = g_strdup (description);
2273 else
2275 BookmarkItem *item;
2277 item = g_bookmark_file_lookup_item (bookmark, uri);
2278 if (!item)
2280 item = bookmark_item_new (uri);
2281 g_bookmark_file_add_item (bookmark, item, NULL);
2284 g_free (item->description);
2285 item->description = g_strdup (description);
2287 item->modified = time (NULL);
2292 * g_bookmark_file_get_description:
2293 * @bookmark: a #GBookmarkFile
2294 * @uri: a valid URI
2295 * @error: return location for a #GError, or %NULL
2297 * Retrieves the description of the bookmark for @uri.
2299 * In the event the URI cannot be found, %NULL is returned and
2300 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2302 * Return value: a newly allocated string or %NULL if the specified
2303 * URI cannot be found.
2305 * Since: 2.12
2307 gchar *
2308 g_bookmark_file_get_description (GBookmarkFile *bookmark,
2309 const gchar *uri,
2310 GError **error)
2312 BookmarkItem *item;
2314 g_return_val_if_fail (bookmark != NULL, NULL);
2316 if (!uri)
2317 return g_strdup (bookmark->description);
2319 item = g_bookmark_file_lookup_item (bookmark, uri);
2320 if (!item)
2322 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2323 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2324 _("No bookmark found for URI '%s'"),
2325 uri);
2326 return NULL;
2329 return g_strdup (item->description);
2333 * g_bookmark_file_set_mime_type:
2334 * @bookmark: a #GBookmarkFile
2335 * @uri: a valid URI
2336 * @mime_type: a MIME type
2338 * Sets @mime_type as the MIME type of the bookmark for @uri.
2340 * If a bookmark for @uri cannot be found then it is created.
2342 * Since: 2.12
2344 void
2345 g_bookmark_file_set_mime_type (GBookmarkFile *bookmark,
2346 const gchar *uri,
2347 const gchar *mime_type)
2349 BookmarkItem *item;
2351 g_return_if_fail (bookmark != NULL);
2352 g_return_if_fail (uri != NULL);
2353 g_return_if_fail (mime_type != NULL);
2355 item = g_bookmark_file_lookup_item (bookmark, uri);
2356 if (!item)
2358 item = bookmark_item_new (uri);
2359 g_bookmark_file_add_item (bookmark, item, NULL);
2362 if (!item->metadata)
2363 item->metadata = bookmark_metadata_new ();
2365 g_free (item->metadata->mime_type);
2367 item->metadata->mime_type = g_strdup (mime_type);
2368 item->modified = time (NULL);
2372 * g_bookmark_file_get_mime_type:
2373 * @bookmark: a #GBookmarkFile
2374 * @uri: a valid URI
2375 * @error: return location for a #GError, or %NULL
2377 * Retrieves the MIME type of the resource pointed by @uri.
2379 * In the event the URI cannot be found, %NULL is returned and
2380 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND. In the
2381 * event that the MIME type cannot be found, %NULL is returned and
2382 * @error is set to #G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
2384 * Return value: a newly allocated string or %NULL if the specified
2385 * URI cannot be found.
2387 * Since: 2.12
2389 gchar *
2390 g_bookmark_file_get_mime_type (GBookmarkFile *bookmark,
2391 const gchar *uri,
2392 GError **error)
2394 BookmarkItem *item;
2396 g_return_val_if_fail (bookmark != NULL, NULL);
2397 g_return_val_if_fail (uri != NULL, NULL);
2399 item = g_bookmark_file_lookup_item (bookmark, uri);
2400 if (!item)
2402 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2403 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2404 _("No bookmark found for URI '%s'"),
2405 uri);
2406 return NULL;
2409 if (!item->metadata)
2411 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2412 G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
2413 _("No MIME type defined in the bookmark for URI '%s'"),
2414 uri);
2415 return NULL;
2418 return g_strdup (item->metadata->mime_type);
2422 * g_bookmark_file_set_is_private:
2423 * @bookmark: a #GBookmarkFile
2424 * @uri: a valid URI
2425 * @is_private: %TRUE if the bookmark should be marked as private
2427 * Sets the private flag of the bookmark for @uri.
2429 * If a bookmark for @uri cannot be found then it is created.
2431 * Since: 2.12
2433 void
2434 g_bookmark_file_set_is_private (GBookmarkFile *bookmark,
2435 const gchar *uri,
2436 gboolean is_private)
2438 BookmarkItem *item;
2440 g_return_if_fail (bookmark != NULL);
2441 g_return_if_fail (uri != NULL);
2443 item = g_bookmark_file_lookup_item (bookmark, uri);
2444 if (!item)
2446 item = bookmark_item_new (uri);
2447 g_bookmark_file_add_item (bookmark, item, NULL);
2450 if (!item->metadata)
2451 item->metadata = bookmark_metadata_new ();
2453 item->metadata->is_private = (is_private == TRUE);
2454 item->modified = time (NULL);
2458 * g_bookmark_file_get_is_private:
2459 * @bookmark: a #GBookmarkFile
2460 * @uri: a valid URI
2461 * @error: return location for a #GError, or %NULL
2463 * Gets whether the private flag of the bookmark for @uri is set.
2465 * In the event the URI cannot be found, %FALSE is returned and
2466 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND. In the
2467 * event that the private flag cannot be found, %FALSE is returned and
2468 * @error is set to #G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
2470 * Return value: %TRUE if the private flag is set, %FALSE otherwise.
2472 * Since: 2.12
2474 gboolean
2475 g_bookmark_file_get_is_private (GBookmarkFile *bookmark,
2476 const gchar *uri,
2477 GError **error)
2479 BookmarkItem *item;
2481 g_return_val_if_fail (bookmark != NULL, FALSE);
2482 g_return_val_if_fail (uri != NULL, FALSE);
2484 item = g_bookmark_file_lookup_item (bookmark, uri);
2485 if (!item)
2487 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2488 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2489 _("No bookmark found for URI '%s'"),
2490 uri);
2491 return FALSE;
2494 if (!item->metadata)
2496 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2497 G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
2498 _("No private flag has been defined in bookmark for URI '%s'"),
2499 uri);
2500 return FALSE;
2503 return item->metadata->is_private;
2507 * g_bookmark_file_set_added:
2508 * @bookmark: a #GBookmarkFile
2509 * @uri: a valid URI
2510 * @added: a timestamp or -1 to use the current time
2512 * Sets the time the bookmark for @uri was added into @bookmark.
2514 * If no bookmark for @uri is found then it is created.
2516 * Since: 2.12
2518 void
2519 g_bookmark_file_set_added (GBookmarkFile *bookmark,
2520 const gchar *uri,
2521 time_t added)
2523 BookmarkItem *item;
2525 g_return_if_fail (bookmark != NULL);
2526 g_return_if_fail (uri != NULL);
2528 item = g_bookmark_file_lookup_item (bookmark, uri);
2529 if (!item)
2531 item = bookmark_item_new (uri);
2532 g_bookmark_file_add_item (bookmark, item, NULL);
2535 if (added == (time_t) -1)
2536 time (&added);
2538 item->added = added;
2539 item->modified = added;
2543 * g_bookmark_file_get_added:
2544 * @bookmark: a #GBookmarkFile
2545 * @uri: a valid URI
2546 * @error: return location for a #GError, or %NULL
2548 * Gets the time the bookmark for @uri was added to @bookmark
2550 * In the event the URI cannot be found, -1 is returned and
2551 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2553 * Return value: a timestamp
2555 * Since: 2.12
2557 time_t
2558 g_bookmark_file_get_added (GBookmarkFile *bookmark,
2559 const gchar *uri,
2560 GError **error)
2562 BookmarkItem *item;
2564 g_return_val_if_fail (bookmark != NULL, (time_t) -1);
2565 g_return_val_if_fail (uri != NULL, (time_t) -1);
2567 item = g_bookmark_file_lookup_item (bookmark, uri);
2568 if (!item)
2570 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2571 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2572 _("No bookmark found for URI '%s'"),
2573 uri);
2574 return (time_t) -1;
2577 return item->added;
2581 * g_bookmark_file_set_modified:
2582 * @bookmark: a #GBookmarkFile
2583 * @uri: a valid URI
2584 * @modified: a timestamp or -1 to use the current time
2586 * Sets the last time the bookmark for @uri was last modified.
2588 * If no bookmark for @uri is found then it is created.
2590 * The "modified" time should only be set when the bookmark's meta-data
2591 * was actually changed. Every function of #GBookmarkFile that
2592 * modifies a bookmark also changes the modification time, except for
2593 * g_bookmark_file_set_visited().
2595 * Since: 2.12
2597 void
2598 g_bookmark_file_set_modified (GBookmarkFile *bookmark,
2599 const gchar *uri,
2600 time_t modified)
2602 BookmarkItem *item;
2604 g_return_if_fail (bookmark != NULL);
2605 g_return_if_fail (uri != NULL);
2607 item = g_bookmark_file_lookup_item (bookmark, uri);
2608 if (!item)
2610 item = bookmark_item_new (uri);
2611 g_bookmark_file_add_item (bookmark, item, NULL);
2614 if (modified == (time_t) -1)
2615 time (&modified);
2617 item->modified = modified;
2621 * g_bookmark_file_get_modified:
2622 * @bookmark: a #GBookmarkFile
2623 * @uri: a valid URI
2624 * @error: return location for a #GError, or %NULL
2626 * Gets the time when the bookmark for @uri was last modified.
2628 * In the event the URI cannot be found, -1 is returned and
2629 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2631 * Return value: a timestamp
2633 * Since: 2.12
2635 time_t
2636 g_bookmark_file_get_modified (GBookmarkFile *bookmark,
2637 const gchar *uri,
2638 GError **error)
2640 BookmarkItem *item;
2642 g_return_val_if_fail (bookmark != NULL, (time_t) -1);
2643 g_return_val_if_fail (uri != NULL, (time_t) -1);
2645 item = g_bookmark_file_lookup_item (bookmark, uri);
2646 if (!item)
2648 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2649 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2650 _("No bookmark found for URI '%s'"),
2651 uri);
2652 return (time_t) -1;
2655 return item->modified;
2659 * g_bookmark_file_set_visited:
2660 * @bookmark: a #GBookmarkFile
2661 * @uri: a valid URI
2662 * @visited: a timestamp or -1 to use the current time
2664 * Sets the time the bookmark for @uri was last visited.
2666 * If no bookmark for @uri is found then it is created.
2668 * The "visited" time should only be set if the bookmark was launched,
2669 * either using the command line retrieved by g_bookmark_file_get_app_info()
2670 * or by the default application for the bookmark's MIME type, retrieved
2671 * using g_bookmark_file_get_mime_type(). Changing the "visited" time
2672 * does not affect the "modified" time.
2674 * Since: 2.12
2676 void
2677 g_bookmark_file_set_visited (GBookmarkFile *bookmark,
2678 const gchar *uri,
2679 time_t visited)
2681 BookmarkItem *item;
2683 g_return_if_fail (bookmark != NULL);
2684 g_return_if_fail (uri != NULL);
2686 item = g_bookmark_file_lookup_item (bookmark, uri);
2687 if (!item)
2689 item = bookmark_item_new (uri);
2690 g_bookmark_file_add_item (bookmark, item, NULL);
2693 if (visited == (time_t) -1)
2694 time (&visited);
2696 item->visited = visited;
2700 * g_bookmark_file_get_visited:
2701 * @bookmark: a #GBookmarkFile
2702 * @uri: a valid URI
2703 * @error: return location for a #GError, or %NULL
2705 * Gets the time the bookmark for @uri was last visited.
2707 * In the event the URI cannot be found, -1 is returned and
2708 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2710 * Return value: a timestamp.
2712 * Since: 2.12
2714 time_t
2715 g_bookmark_file_get_visited (GBookmarkFile *bookmark,
2716 const gchar *uri,
2717 GError **error)
2719 BookmarkItem *item;
2721 g_return_val_if_fail (bookmark != NULL, (time_t) -1);
2722 g_return_val_if_fail (uri != NULL, (time_t) -1);
2724 item = g_bookmark_file_lookup_item (bookmark, uri);
2725 if (!item)
2727 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2728 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2729 _("No bookmark found for URI '%s'"),
2730 uri);
2731 return (time_t) -1;
2734 return item->visited;
2738 * g_bookmark_file_has_group:
2739 * @bookmark: a #GBookmarkFile
2740 * @uri: a valid URI
2741 * @group: the group name to be searched
2742 * @error: return location for a #GError, or %NULL
2744 * Checks whether @group appears in the list of groups to which
2745 * the bookmark for @uri belongs to.
2747 * In the event the URI cannot be found, %FALSE is returned and
2748 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2750 * Return value: %TRUE if @group was found.
2752 * Since: 2.12
2754 gboolean
2755 g_bookmark_file_has_group (GBookmarkFile *bookmark,
2756 const gchar *uri,
2757 const gchar *group,
2758 GError **error)
2760 BookmarkItem *item;
2761 GList *l;
2763 g_return_val_if_fail (bookmark != NULL, FALSE);
2764 g_return_val_if_fail (uri != NULL, FALSE);
2766 item = g_bookmark_file_lookup_item (bookmark, uri);
2767 if (!item)
2769 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2770 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2771 _("No bookmark found for URI '%s'"),
2772 uri);
2773 return FALSE;
2776 if (!item->metadata)
2777 return FALSE;
2779 for (l = item->metadata->groups; l != NULL; l = l->next)
2781 if (strcmp (l->data, group) == 0)
2782 return TRUE;
2785 return FALSE;
2790 * g_bookmark_file_add_group:
2791 * @bookmark: a #GBookmarkFile
2792 * @uri: a valid URI
2793 * @group: the group name to be added
2795 * Adds @group to the list of groups to which the bookmark for @uri
2796 * belongs to.
2798 * If no bookmark for @uri is found then it is created.
2800 * Since: 2.12
2802 void
2803 g_bookmark_file_add_group (GBookmarkFile *bookmark,
2804 const gchar *uri,
2805 const gchar *group)
2807 BookmarkItem *item;
2809 g_return_if_fail (bookmark != NULL);
2810 g_return_if_fail (uri != NULL);
2811 g_return_if_fail (group != NULL && group[0] != '\0');
2813 item = g_bookmark_file_lookup_item (bookmark, uri);
2814 if (!item)
2816 item = bookmark_item_new (uri);
2817 g_bookmark_file_add_item (bookmark, item, NULL);
2820 if (!item->metadata)
2821 item->metadata = bookmark_metadata_new ();
2823 if (!g_bookmark_file_has_group (bookmark, uri, group, NULL))
2825 item->metadata->groups = g_list_prepend (item->metadata->groups,
2826 g_strdup (group));
2828 item->modified = time (NULL);
2833 * g_bookmark_file_remove_group:
2834 * @bookmark: a #GBookmarkFile
2835 * @uri: a valid URI
2836 * @group: the group name to be removed
2837 * @error: return location for a #GError, or %NULL
2839 * Removes @group from the list of groups to which the bookmark
2840 * for @uri belongs to.
2842 * In the event the URI cannot be found, %FALSE is returned and
2843 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2844 * In the event no group was defined, %FALSE is returned and
2845 * @error is set to #G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
2847 * Return value: %TRUE if @group was successfully removed.
2849 * Since: 2.12
2851 gboolean
2852 g_bookmark_file_remove_group (GBookmarkFile *bookmark,
2853 const gchar *uri,
2854 const gchar *group,
2855 GError **error)
2857 BookmarkItem *item;
2858 GList *l;
2860 g_return_val_if_fail (bookmark != NULL, FALSE);
2861 g_return_val_if_fail (uri != NULL, FALSE);
2863 item = g_bookmark_file_lookup_item (bookmark, uri);
2864 if (!item)
2866 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2867 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2868 _("No bookmark found for URI '%s'"),
2869 uri);
2870 return FALSE;
2873 if (!item->metadata)
2875 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2876 G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
2877 _("No groups set in bookmark for URI '%s'"),
2878 uri);
2879 return FALSE;
2882 for (l = item->metadata->groups; l != NULL; l = l->next)
2884 if (strcmp (l->data, group) == 0)
2886 item->metadata->groups = g_list_remove_link (item->metadata->groups, l);
2887 g_free (l->data);
2888 g_list_free_1 (l);
2890 item->modified = time (NULL);
2892 return TRUE;
2896 return FALSE;
2900 * g_bookmark_file_set_groups:
2901 * @bookmark: a #GBookmarkFile
2902 * @uri: an item's URI
2903 * @groups: an array of group names, or %NULL to remove all groups
2904 * @length: number of group name values in @groups
2906 * Sets a list of group names for the item with URI @uri. Each previously
2907 * set group name list is removed.
2909 * If @uri cannot be found then an item for it is created.
2911 * Since: 2.12
2913 void
2914 g_bookmark_file_set_groups (GBookmarkFile *bookmark,
2915 const gchar *uri,
2916 const gchar **groups,
2917 gsize length)
2919 BookmarkItem *item;
2920 gsize i;
2922 g_return_if_fail (bookmark != NULL);
2923 g_return_if_fail (uri != NULL);
2924 g_return_if_fail (groups != NULL);
2926 item = g_bookmark_file_lookup_item (bookmark, uri);
2927 if (!item)
2929 item = bookmark_item_new (uri);
2930 g_bookmark_file_add_item (bookmark, item, NULL);
2933 if (!item->metadata)
2934 item->metadata = bookmark_metadata_new ();
2936 if (item->metadata->groups != NULL)
2938 g_list_foreach (item->metadata->groups,
2939 (GFunc) g_free,
2940 NULL);
2941 g_list_free (item->metadata->groups);
2942 item->metadata->groups = NULL;
2945 if (groups)
2947 for (i = 0; groups[i] != NULL && i < length; i++)
2948 item->metadata->groups = g_list_append (item->metadata->groups,
2949 g_strdup (groups[i]));
2952 item->modified = time (NULL);
2956 * g_bookmark_file_get_groups:
2957 * @bookmark: a #GBookmarkFile
2958 * @uri: a valid URI
2959 * @length: return location for the length of the returned string, or %NULL
2960 * @error: return location for a #GError, or %NULL
2962 * Retrieves the list of group names of the bookmark for @uri.
2964 * In the event the URI cannot be found, %NULL is returned and
2965 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2967 * The returned array is %NULL terminated, so @length may optionally
2968 * be %NULL.
2970 * Return value: a newly allocated %NULL-terminated array of group names.
2971 * Use g_strfreev() to free it.
2973 * Since: 2.12
2975 gchar **
2976 g_bookmark_file_get_groups (GBookmarkFile *bookmark,
2977 const gchar *uri,
2978 gsize *length,
2979 GError **error)
2981 BookmarkItem *item;
2982 GList *l;
2983 gsize len, i;
2984 gchar **retval;
2986 g_return_val_if_fail (bookmark != NULL, NULL);
2987 g_return_val_if_fail (uri != NULL, NULL);
2989 item = g_bookmark_file_lookup_item (bookmark, uri);
2990 if (!item)
2992 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2993 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2994 _("No bookmark found for URI '%s'"),
2995 uri);
2996 return NULL;
2999 if (!item->metadata)
3001 if (length)
3002 *length = 0;
3004 return NULL;
3007 len = g_list_length (item->metadata->groups);
3008 retval = g_new0 (gchar *, len + 1);
3009 for (l = g_list_last (item->metadata->groups), i = 0;
3010 l != NULL;
3011 l = l->prev)
3013 gchar *group_name = (gchar *) l->data;
3015 g_warn_if_fail (group_name != NULL);
3017 retval[i++] = g_strdup (group_name);
3019 retval[i] = NULL;
3021 if (length)
3022 *length = len;
3024 return retval;
3028 * g_bookmark_file_add_application:
3029 * @bookmark: a #GBookmarkFile
3030 * @uri: a valid URI
3031 * @name: the name of the application registering the bookmark
3032 * or %NULL
3033 * @exec: command line to be used to launch the bookmark or %NULL
3035 * Adds the application with @name and @exec to the list of
3036 * applications that have registered a bookmark for @uri into
3037 * @bookmark.
3039 * Every bookmark inside a #GBookmarkFile must have at least an
3040 * application registered. Each application must provide a name, a
3041 * command line useful for launching the bookmark, the number of times
3042 * the bookmark has been registered by the application and the last
3043 * time the application registered this bookmark.
3045 * If @name is %NULL, the name of the application will be the
3046 * same returned by g_get_application_name(); if @exec is %NULL, the
3047 * command line will be a composition of the program name as
3048 * returned by g_get_prgname() and the "%u" modifier, which will be
3049 * expanded to the bookmark's URI.
3051 * This function will automatically take care of updating the
3052 * registrations count and timestamping in case an application
3053 * with the same @name had already registered a bookmark for
3054 * @uri inside @bookmark.
3056 * If no bookmark for @uri is found, one is created.
3058 * Since: 2.12
3060 void
3061 g_bookmark_file_add_application (GBookmarkFile *bookmark,
3062 const gchar *uri,
3063 const gchar *name,
3064 const gchar *exec)
3066 BookmarkItem *item;
3067 gchar *app_name, *app_exec;
3069 g_return_if_fail (bookmark != NULL);
3070 g_return_if_fail (uri != NULL);
3072 item = g_bookmark_file_lookup_item (bookmark, uri);
3073 if (!item)
3075 item = bookmark_item_new (uri);
3076 g_bookmark_file_add_item (bookmark, item, NULL);
3079 if (name && name[0] != '\0')
3080 app_name = g_strdup (name);
3081 else
3082 app_name = g_strdup (g_get_application_name ());
3084 if (exec && exec[0] != '\0')
3085 app_exec = g_strdup (exec);
3086 else
3087 app_exec = g_strjoin (" ", g_get_prgname(), "%u", NULL);
3089 g_bookmark_file_set_app_info (bookmark, uri,
3090 app_name,
3091 app_exec,
3093 (time_t) -1,
3094 NULL);
3096 g_free (app_exec);
3097 g_free (app_name);
3101 * g_bookmark_file_remove_application:
3102 * @bookmark: a #GBookmarkFile
3103 * @uri: a valid URI
3104 * @name: the name of the application
3105 * @error: return location for a #GError or %NULL
3107 * Removes application registered with @name from the list of applications
3108 * that have registered a bookmark for @uri inside @bookmark.
3110 * In the event the URI cannot be found, %FALSE is returned and
3111 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3112 * In the event that no application with name @app_name has registered
3113 * a bookmark for @uri, %FALSE is returned and error is set to
3114 * #G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED.
3116 * Return value: %TRUE if the application was successfully removed.
3118 * Since: 2.12
3120 gboolean
3121 g_bookmark_file_remove_application (GBookmarkFile *bookmark,
3122 const gchar *uri,
3123 const gchar *name,
3124 GError **error)
3126 GError *set_error;
3127 gboolean retval;
3129 g_return_val_if_fail (bookmark != NULL, FALSE);
3130 g_return_val_if_fail (uri != NULL, FALSE);
3131 g_return_val_if_fail (name != NULL, FALSE);
3133 set_error = NULL;
3134 retval = g_bookmark_file_set_app_info (bookmark, uri,
3135 name,
3138 (time_t) -1,
3139 &set_error);
3140 if (set_error)
3142 g_propagate_error (error, set_error);
3144 return FALSE;
3147 return retval;
3151 * g_bookmark_file_has_application:
3152 * @bookmark: a #GBookmarkFile
3153 * @uri: a valid URI
3154 * @name: the name of the application
3155 * @error: return location for a #GError or %NULL
3157 * Checks whether the bookmark for @uri inside @bookmark has been
3158 * registered by application @name.
3160 * In the event the URI cannot be found, %FALSE is returned and
3161 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3163 * Return value: %TRUE if the application @name was found
3165 * Since: 2.12
3167 gboolean
3168 g_bookmark_file_has_application (GBookmarkFile *bookmark,
3169 const gchar *uri,
3170 const gchar *name,
3171 GError **error)
3173 BookmarkItem *item;
3175 g_return_val_if_fail (bookmark != NULL, FALSE);
3176 g_return_val_if_fail (uri != NULL, FALSE);
3177 g_return_val_if_fail (name != NULL, FALSE);
3179 item = g_bookmark_file_lookup_item (bookmark, uri);
3180 if (!item)
3182 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3183 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3184 _("No bookmark found for URI '%s'"),
3185 uri);
3186 return FALSE;
3189 return (NULL != bookmark_item_lookup_app_info (item, name));
3193 * g_bookmark_file_set_app_info:
3194 * @bookmark: a #GBookmarkFile
3195 * @uri: a valid URI
3196 * @name: an application's name
3197 * @exec: an application's command line
3198 * @count: the number of registrations done for this application
3199 * @stamp: the time of the last registration for this application
3200 * @error: return location for a #GError or %NULL
3202 * Sets the meta-data of application @name inside the list of
3203 * applications that have registered a bookmark for @uri inside
3204 * @bookmark.
3206 * You should rarely use this function; use g_bookmark_file_add_application()
3207 * and g_bookmark_file_remove_application() instead.
3209 * @name can be any UTF-8 encoded string used to identify an
3210 * application.
3211 * @exec can have one of these two modifiers: "%f", which will
3212 * be expanded as the local file name retrieved from the bookmark's
3213 * URI; "%u", which will be expanded as the bookmark's URI.
3214 * The expansion is done automatically when retrieving the stored
3215 * command line using the g_bookmark_file_get_app_info() function.
3216 * @count is the number of times the application has registered the
3217 * bookmark; if is < 0, the current registration count will be increased
3218 * by one, if is 0, the application with @name will be removed from
3219 * the list of registered applications.
3220 * @stamp is the Unix time of the last registration; if it is -1, the
3221 * current time will be used.
3223 * If you try to remove an application by setting its registration count to
3224 * zero, and no bookmark for @uri is found, %FALSE is returned and
3225 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND; similarly,
3226 * in the event that no application @name has registered a bookmark
3227 * for @uri, %FALSE is returned and error is set to
3228 * #G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. Otherwise, if no bookmark
3229 * for @uri is found, one is created.
3231 * Return value: %TRUE if the application's meta-data was successfully
3232 * changed.
3234 * Since: 2.12
3236 gboolean
3237 g_bookmark_file_set_app_info (GBookmarkFile *bookmark,
3238 const gchar *uri,
3239 const gchar *name,
3240 const gchar *exec,
3241 gint count,
3242 time_t stamp,
3243 GError **error)
3245 BookmarkItem *item;
3246 BookmarkAppInfo *ai;
3248 g_return_val_if_fail (bookmark != NULL, FALSE);
3249 g_return_val_if_fail (uri != NULL, FALSE);
3250 g_return_val_if_fail (name != NULL, FALSE);
3251 g_return_val_if_fail (exec != NULL, FALSE);
3253 item = g_bookmark_file_lookup_item (bookmark, uri);
3254 if (!item)
3256 if (count == 0)
3258 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3259 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3260 _("No bookmark found for URI '%s'"),
3261 uri);
3262 return FALSE;
3264 else
3266 item = bookmark_item_new (uri);
3267 g_bookmark_file_add_item (bookmark, item, NULL);
3271 ai = bookmark_item_lookup_app_info (item, name);
3272 if (!ai)
3274 if (count == 0)
3276 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3277 G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED,
3278 _("No application with name '%s' registered a bookmark for '%s'"),
3279 name,
3280 uri);
3281 return FALSE;
3283 else
3285 ai = bookmark_app_info_new (name);
3287 item->metadata->applications = g_list_prepend (item->metadata->applications, ai);
3288 g_hash_table_replace (item->metadata->apps_by_name, ai->name, ai);
3292 if (count == 0)
3294 item->metadata->applications = g_list_remove (item->metadata->applications, ai);
3295 g_hash_table_remove (item->metadata->apps_by_name, ai->name);
3296 bookmark_app_info_free (ai);
3298 item->modified = time (NULL);
3300 return TRUE;
3302 else if (count > 0)
3303 ai->count = count;
3304 else
3305 ai->count += 1;
3307 if (stamp != (time_t) -1)
3308 ai->stamp = stamp;
3309 else
3310 ai->stamp = time (NULL);
3312 if (exec && exec[0] != '\0')
3314 g_free (ai->exec);
3315 ai->exec = g_shell_quote (exec);
3318 item->modified = time (NULL);
3320 return TRUE;
3323 /* expands the application's command line */
3324 static gchar *
3325 expand_exec_line (const gchar *exec_fmt,
3326 const gchar *uri)
3328 GString *exec;
3329 gchar ch;
3331 exec = g_string_sized_new (512);
3332 while ((ch = *exec_fmt++) != '\0')
3334 if (ch != '%')
3336 exec = g_string_append_c (exec, ch);
3337 continue;
3340 ch = *exec_fmt++;
3341 switch (ch)
3343 case '\0':
3344 goto out;
3345 case 'U':
3346 case 'u':
3347 g_string_append (exec, uri);
3348 break;
3349 case 'F':
3350 case 'f':
3352 gchar *file = g_filename_from_uri (uri, NULL, NULL);
3353 if (file)
3355 g_string_append (exec, file);
3356 g_free (file);
3358 else
3360 g_string_free (exec, TRUE);
3361 return NULL;
3364 break;
3365 case '%':
3366 default:
3367 exec = g_string_append_c (exec, ch);
3368 break;
3372 out:
3373 return g_string_free (exec, FALSE);
3377 * g_bookmark_file_get_app_info:
3378 * @bookmark: a #GBookmarkFile
3379 * @uri: a valid URI
3380 * @name: an application's name
3381 * @exec: location for the command line of the application, or %NULL
3382 * @count: return location for the registration count, or %NULL
3383 * @stamp: return location for the last registration time, or %NULL
3384 * @error: return location for a #GError, or %NULL
3386 * Gets the registration informations of @app_name for the bookmark for
3387 * @uri. See g_bookmark_file_set_app_info() for more informations about
3388 * the returned data.
3390 * The string returned in @app_exec must be freed.
3392 * In the event the URI cannot be found, %FALSE is returned and
3393 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND. In the
3394 * event that no application with name @app_name has registered a bookmark
3395 * for @uri, %FALSE is returned and error is set to
3396 * #G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. In the event that unquoting
3397 * the command line fails, an error of the #G_SHELL_ERROR domain is
3398 * set and %FALSE is returned.
3400 * Return value: %TRUE on success.
3402 * Since: 2.12
3404 gboolean
3405 g_bookmark_file_get_app_info (GBookmarkFile *bookmark,
3406 const gchar *uri,
3407 const gchar *name,
3408 gchar **exec,
3409 guint *count,
3410 time_t *stamp,
3411 GError **error)
3413 BookmarkItem *item;
3414 BookmarkAppInfo *ai;
3416 g_return_val_if_fail (bookmark != NULL, FALSE);
3417 g_return_val_if_fail (uri != NULL, FALSE);
3418 g_return_val_if_fail (name != NULL, FALSE);
3420 item = g_bookmark_file_lookup_item (bookmark, uri);
3421 if (!item)
3423 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3424 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3425 _("No bookmark found for URI '%s'"),
3426 uri);
3427 return FALSE;
3430 ai = bookmark_item_lookup_app_info (item, name);
3431 if (!ai)
3433 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3434 G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED,
3435 _("No application with name '%s' registered a bookmark for '%s'"),
3436 name,
3437 uri);
3438 return FALSE;
3441 if (exec)
3443 GError *unquote_error = NULL;
3444 gchar *command_line;
3446 command_line = g_shell_unquote (ai->exec, &unquote_error);
3447 if (unquote_error)
3449 g_propagate_error (error, unquote_error);
3450 return FALSE;
3453 *exec = expand_exec_line (command_line, uri);
3454 if (!*exec)
3456 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3457 G_BOOKMARK_FILE_ERROR_INVALID_URI,
3458 _("Failed to expand exec line '%s' with URI '%s'"),
3459 ai->exec, uri);
3460 g_free (command_line);
3462 return FALSE;
3464 else
3465 g_free (command_line);
3468 if (count)
3469 *count = ai->count;
3471 if (stamp)
3472 *stamp = ai->stamp;
3474 return TRUE;
3478 * g_bookmark_file_get_applications:
3479 * @bookmark: a #GBookmarkFile
3480 * @uri: a valid URI
3481 * @length: return location of the length of the returned list, or %NULL
3482 * @error: return location for a #GError, or %NULL
3484 * Retrieves the names of the applications that have registered the
3485 * bookmark for @uri.
3487 * In the event the URI cannot be found, %NULL is returned and
3488 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3490 * Return value: a newly allocated %NULL-terminated array of strings.
3491 * Use g_strfreev() to free it.
3493 * Since: 2.12
3495 gchar **
3496 g_bookmark_file_get_applications (GBookmarkFile *bookmark,
3497 const gchar *uri,
3498 gsize *length,
3499 GError **error)
3501 BookmarkItem *item;
3502 GList *l;
3503 gchar **apps;
3504 gsize i, n_apps;
3506 g_return_val_if_fail (bookmark != NULL, NULL);
3507 g_return_val_if_fail (uri != NULL, NULL);
3509 item = g_bookmark_file_lookup_item (bookmark, uri);
3510 if (!item)
3512 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3513 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3514 _("No bookmark found for URI '%s'"),
3515 uri);
3516 return NULL;
3519 if (!item->metadata)
3521 if (length)
3522 *length = 0;
3524 return NULL;
3527 n_apps = g_list_length (item->metadata->applications);
3528 apps = g_new0 (gchar *, n_apps + 1);
3530 for (l = g_list_last (item->metadata->applications), i = 0;
3531 l != NULL;
3532 l = l->prev)
3534 BookmarkAppInfo *ai;
3536 ai = (BookmarkAppInfo *) l->data;
3538 g_warn_if_fail (ai != NULL);
3539 g_warn_if_fail (ai->name != NULL);
3541 apps[i++] = g_strdup (ai->name);
3543 apps[i] = NULL;
3545 if (length)
3546 *length = i;
3548 return apps;
3552 * g_bookmark_file_get_size:
3553 * @bookmark: a #GBookmarkFile
3555 * Gets the number of bookmarks inside @bookmark.
3557 * Return value: the number of bookmarks
3559 * Since: 2.12
3561 gint
3562 g_bookmark_file_get_size (GBookmarkFile *bookmark)
3564 g_return_val_if_fail (bookmark != NULL, 0);
3566 return g_list_length (bookmark->items);
3570 * g_bookmark_file_move_item:
3571 * @bookmark: a #GBookmarkFile
3572 * @old_uri: a valid URI
3573 * @new_uri: a valid URI, or %NULL
3574 * @error: return location for a #GError or %NULL
3576 * Changes the URI of a bookmark item from @old_uri to @new_uri. Any
3577 * existing bookmark for @new_uri will be overwritten. If @new_uri is
3578 * %NULL, then the bookmark is removed.
3580 * In the event the URI cannot be found, %FALSE is returned and
3581 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3583 * Return value: %TRUE if the URI was successfully changed
3585 * Since: 2.12
3587 gboolean
3588 g_bookmark_file_move_item (GBookmarkFile *bookmark,
3589 const gchar *old_uri,
3590 const gchar *new_uri,
3591 GError **error)
3593 BookmarkItem *item;
3594 GError *remove_error;
3596 g_return_val_if_fail (bookmark != NULL, FALSE);
3597 g_return_val_if_fail (old_uri != NULL, FALSE);
3599 item = g_bookmark_file_lookup_item (bookmark, old_uri);
3600 if (!item)
3602 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3603 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3604 _("No bookmark found for URI '%s'"),
3605 old_uri);
3606 return FALSE;
3609 if (new_uri && new_uri[0] != '\0')
3611 if (g_bookmark_file_has_item (bookmark, new_uri))
3613 remove_error = NULL;
3614 g_bookmark_file_remove_item (bookmark, new_uri, &remove_error);
3615 if (remove_error)
3617 g_propagate_error (error, remove_error);
3619 return FALSE;
3623 g_hash_table_steal (bookmark->items_by_uri, item->uri);
3625 g_free (item->uri);
3626 item->uri = g_strdup (new_uri);
3627 item->modified = time (NULL);
3629 g_hash_table_replace (bookmark->items_by_uri, item->uri, item);
3631 return TRUE;
3633 else
3635 remove_error = NULL;
3636 g_bookmark_file_remove_item (bookmark, old_uri, &remove_error);
3637 if (remove_error)
3639 g_propagate_error (error, remove_error);
3641 return FALSE;
3644 return TRUE;
3649 * g_bookmark_file_set_icon:
3650 * @bookmark: a #GBookmarkFile
3651 * @uri: a valid URI
3652 * @href: the URI of the icon for the bookmark, or %NULL
3653 * @mime_type: the MIME type of the icon for the bookmark
3655 * Sets the icon for the bookmark for @uri. If @href is %NULL, unsets
3656 * the currently set icon. @href can either be a full URL for the icon
3657 * file or the icon name following the Icon Naming specification.
3659 * If no bookmark for @uri is found one is created.
3661 * Since: 2.12
3663 void
3664 g_bookmark_file_set_icon (GBookmarkFile *bookmark,
3665 const gchar *uri,
3666 const gchar *href,
3667 const gchar *mime_type)
3669 BookmarkItem *item;
3671 g_return_if_fail (bookmark != NULL);
3672 g_return_if_fail (uri != NULL);
3674 item = g_bookmark_file_lookup_item (bookmark, uri);
3675 if (!item)
3677 item = bookmark_item_new (uri);
3678 g_bookmark_file_add_item (bookmark, item, NULL);
3681 if (!item->metadata)
3682 item->metadata = bookmark_metadata_new ();
3684 g_free (item->metadata->icon_href);
3685 g_free (item->metadata->icon_mime);
3687 item->metadata->icon_href = g_strdup (href);
3689 if (mime_type && mime_type[0] != '\0')
3690 item->metadata->icon_mime = g_strdup (mime_type);
3691 else
3692 item->metadata->icon_mime = g_strdup ("application/octet-stream");
3694 item->modified = time (NULL);
3698 * g_bookmark_file_get_icon:
3699 * @bookmark: a #GBookmarkFile
3700 * @uri: a valid URI
3701 * @href: return location for the icon's location or %NULL
3702 * @mime_type: return location for the icon's MIME type or %NULL
3703 * @error: return location for a #GError or %NULL
3705 * Gets the icon of the bookmark for @uri.
3707 * In the event the URI cannot be found, %FALSE is returned and
3708 * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3710 * Return value: %TRUE if the icon for the bookmark for the URI was found.
3711 * You should free the returned strings.
3713 * Since: 2.12
3715 gboolean
3716 g_bookmark_file_get_icon (GBookmarkFile *bookmark,
3717 const gchar *uri,
3718 gchar **href,
3719 gchar **mime_type,
3720 GError **error)
3722 BookmarkItem *item;
3724 g_return_val_if_fail (bookmark != NULL, FALSE);
3725 g_return_val_if_fail (uri != NULL, FALSE);
3727 item = g_bookmark_file_lookup_item (bookmark, uri);
3728 if (!item)
3730 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3731 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3732 _("No bookmark found for URI '%s'"),
3733 uri);
3734 return FALSE;
3737 if ((!item->metadata) || (!item->metadata->icon_href))
3738 return FALSE;
3740 if (href)
3741 *href = g_strdup (item->metadata->icon_href);
3743 if (mime_type)
3744 *mime_type = g_strdup (item->metadata->icon_mime);
3746 return TRUE;