1 /* gbookmarkfile.c: parsing and building desktop bookmarks
3 * Copyright (C) 2005-2006 Emmanuele Bassi
5 * SPDX-License-Identifier: LGPL-2.1-or-later
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, see <http://www.gnu.org/licenses/>.
23 #include "gbookmarkfile.h"
36 #include "gdatetime.h"
38 #include "gfileutils.h"
45 #include "gmessages.h"
50 #include "gstrfuncs.h"
56 * SECTION:bookmarkfile
57 * @title: Bookmark file parser
58 * @short_description: parses files containing bookmarks
60 * GBookmarkFile lets you parse, edit or create files containing bookmarks
61 * to URI, along with some meta-data about the resource pointed by the URI
62 * like its MIME type, the application that is registering the bookmark and
63 * the icon that should be used to represent the bookmark. The data is stored
65 * [Desktop Bookmark Specification](http://www.gnome.org/~ebassi/bookmark-spec).
67 * The syntax of the bookmark files is described in detail inside the
68 * Desktop Bookmark Specification, here is a quick summary: bookmark
69 * files use a sub-class of the XML Bookmark Exchange Language
70 * specification, consisting of valid UTF-8 encoded XML, under the
71 * <xbel> root element; each bookmark is stored inside a
72 * <bookmark> element, using its URI: no relative paths can
73 * be used inside a bookmark file. The bookmark may have a user defined
74 * title and description, to be used instead of the URI. Under the
75 * <metadata> element, with its owner attribute set to
76 * `http://freedesktop.org`, is stored the meta-data about a resource
77 * pointed by its URI. The meta-data consists of the resource's MIME
78 * type; the applications that have registered a bookmark; the groups
79 * to which a bookmark belongs to; a visibility flag, used to set the
80 * bookmark as "private" to the applications and groups that has it
81 * registered; the URI and MIME type of an icon, to be used when
82 * displaying the bookmark inside a GUI.
84 * Here is an example of a bookmark file:
85 * [bookmarks.xbel](https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/glib/tests/bookmarks.xbel)
87 * A bookmark file might contain more than one bookmark; each bookmark
88 * is accessed through its URI.
90 * The important caveat of bookmark files is that when you add a new
91 * bookmark you must also add the application that is registering it, using
92 * g_bookmark_file_add_application() or g_bookmark_file_set_application_info().
93 * If a bookmark has no applications then it won't be dumped when creating
94 * the on disk representation, using g_bookmark_file_to_data() or
95 * g_bookmark_file_to_file().
97 * The #GBookmarkFile parser was added in GLib 2.12.
100 /* XBEL 1.0 standard entities */
101 #define XBEL_VERSION "1.0"
102 #define XBEL_DTD_NICK "xbel"
103 #define XBEL_DTD_SYSTEM "+//IDN python.org//DTD XML Bookmark " \
104 "Exchange Language 1.0//EN//XML"
106 #define XBEL_DTD_URI "http://www.python.org/topics/xml/dtds/xbel-1.0.dtd"
108 #define XBEL_ROOT_ELEMENT "xbel"
109 #define XBEL_FOLDER_ELEMENT "folder" /* unused */
110 #define XBEL_BOOKMARK_ELEMENT "bookmark"
111 #define XBEL_ALIAS_ELEMENT "alias" /* unused */
112 #define XBEL_SEPARATOR_ELEMENT "separator" /* unused */
113 #define XBEL_TITLE_ELEMENT "title"
114 #define XBEL_DESC_ELEMENT "desc"
115 #define XBEL_INFO_ELEMENT "info"
116 #define XBEL_METADATA_ELEMENT "metadata"
118 #define XBEL_VERSION_ATTRIBUTE "version"
119 #define XBEL_FOLDED_ATTRIBUTE "folded" /* unused */
120 #define XBEL_OWNER_ATTRIBUTE "owner"
121 #define XBEL_ADDED_ATTRIBUTE "added"
122 #define XBEL_VISITED_ATTRIBUTE "visited"
123 #define XBEL_MODIFIED_ATTRIBUTE "modified"
124 #define XBEL_ID_ATTRIBUTE "id"
125 #define XBEL_HREF_ATTRIBUTE "href"
126 #define XBEL_REF_ATTRIBUTE "ref" /* unused */
128 #define XBEL_YES_VALUE "yes"
129 #define XBEL_NO_VALUE "no"
131 /* Desktop bookmark spec entities */
132 #define BOOKMARK_METADATA_OWNER "http://freedesktop.org"
134 #define BOOKMARK_NAMESPACE_NAME "bookmark"
135 #define BOOKMARK_NAMESPACE_URI "http://www.freedesktop.org/standards/desktop-bookmarks"
137 #define BOOKMARK_GROUPS_ELEMENT "groups"
138 #define BOOKMARK_GROUP_ELEMENT "group"
139 #define BOOKMARK_APPLICATIONS_ELEMENT "applications"
140 #define BOOKMARK_APPLICATION_ELEMENT "application"
141 #define BOOKMARK_ICON_ELEMENT "icon"
142 #define BOOKMARK_PRIVATE_ELEMENT "private"
144 #define BOOKMARK_NAME_ATTRIBUTE "name"
145 #define BOOKMARK_EXEC_ATTRIBUTE "exec"
146 #define BOOKMARK_COUNT_ATTRIBUTE "count"
147 #define BOOKMARK_TIMESTAMP_ATTRIBUTE "timestamp" /* deprecated by "modified" */
148 #define BOOKMARK_MODIFIED_ATTRIBUTE "modified"
149 #define BOOKMARK_HREF_ATTRIBUTE "href"
150 #define BOOKMARK_TYPE_ATTRIBUTE "type"
152 /* Shared MIME Info entities */
153 #define MIME_NAMESPACE_NAME "mime"
154 #define MIME_NAMESPACE_URI "http://www.freedesktop.org/standards/shared-mime-info"
155 #define MIME_TYPE_ELEMENT "mime-type"
156 #define MIME_TYPE_ATTRIBUTE "type"
159 typedef struct _BookmarkAppInfo BookmarkAppInfo;
160 typedef struct _BookmarkMetadata BookmarkMetadata;
161 typedef struct _BookmarkItem BookmarkItem;
162 typedef struct _ParseData ParseData;
164 struct _BookmarkAppInfo
171 GDateTime *stamp; /* (owned) */
174 struct _BookmarkMetadata
181 GHashTable *apps_by_name;
186 guint is_private : 1;
196 GDateTime *added; /* (owned) */
197 GDateTime *modified; /* (owned) */
198 GDateTime *visited; /* (owned) */
200 BookmarkMetadata *metadata;
203 struct _GBookmarkFile
208 /* we store our items in a list and keep a copy inside
209 * a hash table for faster lookup performances
212 GHashTable *items_by_uri;
215 /* parser state machine */
236 static void g_bookmark_file_init (GBookmarkFile *bookmark);
237 static void g_bookmark_file_clear (GBookmarkFile *bookmark);
238 static gboolean g_bookmark_file_parse (GBookmarkFile *bookmark,
242 static gchar * g_bookmark_file_dump (GBookmarkFile *bookmark,
245 static BookmarkItem *g_bookmark_file_lookup_item (GBookmarkFile *bookmark,
247 static void g_bookmark_file_add_item (GBookmarkFile *bookmark,
251 static gboolean timestamp_from_iso8601 (const gchar *iso_date,
252 GDateTime **out_date_time,
255 /********************************
258 * Application metadata storage *
259 ********************************/
260 static BookmarkAppInfo *
261 bookmark_app_info_new (const gchar *name)
263 BookmarkAppInfo *retval;
265 g_warn_if_fail (name != NULL);
267 retval = g_slice_new (BookmarkAppInfo);
269 retval->name = g_strdup (name);
272 retval->stamp = NULL;
278 bookmark_app_info_free (BookmarkAppInfo *app_info)
283 g_free (app_info->name);
284 g_free (app_info->exec);
285 g_clear_pointer (&app_info->stamp, g_date_time_unref);
287 g_slice_free (BookmarkAppInfo, app_info);
291 bookmark_app_info_dump (BookmarkAppInfo *app_info)
294 gchar *name, *exec, *modified, *count;
296 g_warn_if_fail (app_info != NULL);
298 if (app_info->count == 0)
301 name = g_markup_escape_text (app_info->name, -1);
302 exec = g_markup_escape_text (app_info->exec, -1);
303 modified = g_date_time_format_iso8601 (app_info->stamp);
304 count = g_strdup_printf ("%u", app_info->count);
306 retval = g_strconcat (" "
307 "<" BOOKMARK_NAMESPACE_NAME ":" BOOKMARK_APPLICATION_ELEMENT
308 " " BOOKMARK_NAME_ATTRIBUTE "=\"", name, "\""
309 " " BOOKMARK_EXEC_ATTRIBUTE "=\"", exec, "\""
310 " " BOOKMARK_MODIFIED_ATTRIBUTE "=\"", modified, "\""
311 " " BOOKMARK_COUNT_ATTRIBUTE "=\"", count, "\"/>\n",
323 /***********************
327 ***********************/
328 static BookmarkMetadata *
329 bookmark_metadata_new (void)
331 BookmarkMetadata *retval;
333 retval = g_slice_new (BookmarkMetadata);
335 retval->mime_type = NULL;
337 retval->groups = NULL;
339 retval->applications = NULL;
340 retval->apps_by_name = g_hash_table_new_full (g_str_hash,
345 retval->is_private = FALSE;
347 retval->icon_href = NULL;
348 retval->icon_mime = NULL;
354 bookmark_metadata_free (BookmarkMetadata *metadata)
359 g_free (metadata->mime_type);
361 g_list_free_full (metadata->groups, g_free);
362 g_list_free_full (metadata->applications, (GDestroyNotify) bookmark_app_info_free);
364 g_hash_table_destroy (metadata->apps_by_name);
366 g_free (metadata->icon_href);
367 g_free (metadata->icon_mime);
369 g_slice_free (BookmarkMetadata, metadata);
373 bookmark_metadata_dump (BookmarkMetadata *metadata)
378 if (!metadata->applications)
381 retval = g_string_sized_new (1024);
383 /* metadata container */
384 g_string_append (retval,
386 "<" XBEL_METADATA_ELEMENT
387 " " XBEL_OWNER_ATTRIBUTE "=\"" BOOKMARK_METADATA_OWNER
391 if (metadata->mime_type) {
392 buffer = g_strconcat (" "
393 "<" MIME_NAMESPACE_NAME ":" MIME_TYPE_ELEMENT " "
394 MIME_TYPE_ATTRIBUTE "=\"", metadata->mime_type, "\"/>\n",
396 g_string_append (retval, buffer);
400 if (metadata->groups)
404 /* open groups container */
405 g_string_append (retval,
407 "<" BOOKMARK_NAMESPACE_NAME
408 ":" BOOKMARK_GROUPS_ELEMENT ">\n");
410 for (l = g_list_last (metadata->groups); l != NULL; l = l->prev)
414 group_name = g_markup_escape_text ((gchar *) l->data, -1);
415 buffer = g_strconcat (" "
416 "<" BOOKMARK_NAMESPACE_NAME
417 ":" BOOKMARK_GROUP_ELEMENT ">",
419 "</" BOOKMARK_NAMESPACE_NAME
420 ":" BOOKMARK_GROUP_ELEMENT ">\n", NULL);
421 g_string_append (retval, buffer);
427 /* close groups container */
428 g_string_append (retval,
430 "</" BOOKMARK_NAMESPACE_NAME
431 ":" BOOKMARK_GROUPS_ELEMENT ">\n");
434 if (metadata->applications)
438 /* open applications container */
439 g_string_append (retval,
441 "<" BOOKMARK_NAMESPACE_NAME
442 ":" BOOKMARK_APPLICATIONS_ELEMENT ">\n");
444 for (l = g_list_last (metadata->applications); l != NULL; l = l->prev)
446 BookmarkAppInfo *app_info = (BookmarkAppInfo *) l->data;
449 g_warn_if_fail (app_info != NULL);
451 app_data = bookmark_app_info_dump (app_info);
455 retval = g_string_append (retval, app_data);
461 /* close applications container */
462 g_string_append (retval,
464 "</" BOOKMARK_NAMESPACE_NAME
465 ":" BOOKMARK_APPLICATIONS_ELEMENT ">\n");
469 if (metadata->icon_href)
471 if (!metadata->icon_mime)
472 metadata->icon_mime = g_strdup ("application/octet-stream");
474 buffer = g_strconcat (" "
475 "<" BOOKMARK_NAMESPACE_NAME
476 ":" BOOKMARK_ICON_ELEMENT
477 " " BOOKMARK_HREF_ATTRIBUTE "=\"", metadata->icon_href,
478 "\" " BOOKMARK_TYPE_ATTRIBUTE "=\"", metadata->icon_mime, "\"/>\n", NULL);
479 g_string_append (retval, buffer);
485 if (metadata->is_private)
486 g_string_append (retval,
488 "<" BOOKMARK_NAMESPACE_NAME
489 ":" BOOKMARK_PRIVATE_ELEMENT "/>\n");
491 /* close metadata container */
492 g_string_append (retval,
494 "</" XBEL_METADATA_ELEMENT ">\n");
496 return g_string_free (retval, FALSE);
499 /******************************************************
502 * Storage for a single bookmark item inside the list *
503 ******************************************************/
504 static BookmarkItem *
505 bookmark_item_new (const gchar *uri)
509 g_warn_if_fail (uri != NULL);
511 item = g_slice_new (BookmarkItem);
512 item->uri = g_strdup (uri);
515 item->description = NULL;
518 item->modified = NULL;
519 item->visited = NULL;
521 item->metadata = NULL;
527 bookmark_item_free (BookmarkItem *item)
533 g_free (item->title);
534 g_free (item->description);
537 bookmark_metadata_free (item->metadata);
539 g_clear_pointer (&item->added, g_date_time_unref);
540 g_clear_pointer (&item->modified, g_date_time_unref);
541 g_clear_pointer (&item->visited, g_date_time_unref);
543 g_slice_free (BookmarkItem, item);
547 bookmark_item_touch_modified (BookmarkItem *item)
549 g_clear_pointer (&item->modified, g_date_time_unref);
550 item->modified = g_date_time_new_now_utc ();
554 bookmark_item_dump (BookmarkItem *item)
559 /* at this point, we must have at least a registered application; if we don't
560 * we don't screw up the bookmark file, and just skip this item
562 if (!item->metadata || !item->metadata->applications)
564 g_warning ("Item for URI '%s' has no registered applications: skipping.", item->uri);
568 retval = g_string_sized_new (4096);
570 g_string_append (retval, " <" XBEL_BOOKMARK_ELEMENT " ");
572 escaped_uri = g_markup_escape_text (item->uri, -1);
574 g_string_append (retval, XBEL_HREF_ATTRIBUTE "=\"");
575 g_string_append (retval, escaped_uri);
576 g_string_append (retval , "\" ");
578 g_free (escaped_uri);
584 added = g_date_time_format_iso8601 (item->added);
585 g_string_append (retval, XBEL_ADDED_ATTRIBUTE "=\"");
586 g_string_append (retval, added);
587 g_string_append (retval, "\" ");
595 modified = g_date_time_format_iso8601 (item->modified);
596 g_string_append (retval, XBEL_MODIFIED_ATTRIBUTE "=\"");
597 g_string_append (retval, modified);
598 g_string_append (retval, "\" ");
606 visited = g_date_time_format_iso8601 (item->visited);
607 g_string_append (retval, XBEL_VISITED_ATTRIBUTE "=\"");
608 g_string_append (retval, visited);
609 g_string_append (retval, "\" ");
613 if (retval->str[retval->len - 1] == ' ')
614 g_string_truncate (retval, retval->len - 1);
615 g_string_append (retval, ">\n");
619 gchar *escaped_title;
621 escaped_title = g_markup_escape_text (item->title, -1);
622 g_string_append (retval, " " "<" XBEL_TITLE_ELEMENT ">");
623 g_string_append (retval, escaped_title);
624 g_string_append (retval, "</" XBEL_TITLE_ELEMENT ">\n");
626 g_free (escaped_title);
629 if (item->description)
633 escaped_desc = g_markup_escape_text (item->description, -1);
634 g_string_append (retval, " " "<" XBEL_DESC_ELEMENT ">");
635 g_string_append (retval, escaped_desc);
636 g_string_append (retval, "</" XBEL_DESC_ELEMENT ">\n");
638 g_free (escaped_desc);
645 metadata = bookmark_metadata_dump (item->metadata);
648 g_string_append (retval, " " "<" XBEL_INFO_ELEMENT ">\n");
649 g_string_append (retval, metadata);
650 g_string_append (retval, " " "</" XBEL_INFO_ELEMENT ">\n");
656 g_string_append (retval, " </" XBEL_BOOKMARK_ELEMENT ">\n");
658 return g_string_free (retval, FALSE);
661 static BookmarkAppInfo *
662 bookmark_item_lookup_app_info (BookmarkItem *item,
663 const gchar *app_name)
665 g_warn_if_fail (item != NULL && app_name != NULL);
670 return g_hash_table_lookup (item->metadata->apps_by_name, app_name);
673 /*************************
675 *************************/
678 g_bookmark_file_init (GBookmarkFile *bookmark)
680 bookmark->title = NULL;
681 bookmark->description = NULL;
683 bookmark->items = NULL;
684 bookmark->items_by_uri = g_hash_table_new_full (g_str_hash,
691 g_bookmark_file_clear (GBookmarkFile *bookmark)
693 g_free (bookmark->title);
694 g_free (bookmark->description);
696 g_list_free_full (bookmark->items, (GDestroyNotify) bookmark_item_free);
697 bookmark->items = NULL;
699 if (bookmark->items_by_uri)
701 g_hash_table_destroy (bookmark->items_by_uri);
703 bookmark->items_by_uri = NULL;
711 GHashTable *namespaces;
713 GBookmarkFile *bookmark_file;
714 BookmarkItem *current_item;
718 parse_data_new (void)
722 retval = g_new (ParseData, 1);
724 retval->state = STATE_STARTED;
725 retval->namespaces = g_hash_table_new_full (g_str_hash, g_str_equal,
726 (GDestroyNotify) g_free,
727 (GDestroyNotify) g_free);
728 retval->bookmark_file = NULL;
729 retval->current_item = NULL;
735 parse_data_free (ParseData *parse_data)
737 g_hash_table_destroy (parse_data->namespaces);
742 #define IS_ATTRIBUTE(s,a) ((0 == strcmp ((s), (a))))
745 parse_bookmark_element (GMarkupParseContext *context,
746 ParseData *parse_data,
747 const gchar **attribute_names,
748 const gchar **attribute_values,
751 const gchar *uri, *added, *modified, *visited;
757 g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_BOOKMARK));
760 uri = added = modified = visited = NULL;
761 for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
763 if (IS_ATTRIBUTE (attr, XBEL_HREF_ATTRIBUTE))
764 uri = attribute_values[i];
765 else if (IS_ATTRIBUTE (attr, XBEL_ADDED_ATTRIBUTE))
766 added = attribute_values[i];
767 else if (IS_ATTRIBUTE (attr, XBEL_MODIFIED_ATTRIBUTE))
768 modified = attribute_values[i];
769 else if (IS_ATTRIBUTE (attr, XBEL_VISITED_ATTRIBUTE))
770 visited = attribute_values[i];
773 /* bookmark is defined by the XBEL spec, so we need
774 * to error out if the element has different or
777 g_set_error (error, G_MARKUP_ERROR,
778 G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
779 _("Unexpected attribute “%s” for element “%s”"),
781 XBEL_BOOKMARK_ELEMENT);
788 g_set_error (error, G_MARKUP_ERROR,
789 G_MARKUP_ERROR_INVALID_CONTENT,
790 _("Attribute “%s” of element “%s” not found"),
792 XBEL_BOOKMARK_ELEMENT);
796 g_warn_if_fail (parse_data->current_item == NULL);
798 item = bookmark_item_new (uri);
800 if (added != NULL && !timestamp_from_iso8601 (added, &item->added, error))
802 bookmark_item_free (item);
806 if (modified != NULL && !timestamp_from_iso8601 (modified, &item->modified, error))
808 bookmark_item_free (item);
812 if (visited != NULL && !timestamp_from_iso8601 (visited, &item->visited, error))
814 bookmark_item_free (item);
819 g_bookmark_file_add_item (parse_data->bookmark_file,
824 bookmark_item_free (item);
826 g_propagate_error (error, add_error);
831 parse_data->current_item = item;
835 parse_application_element (GMarkupParseContext *context,
836 ParseData *parse_data,
837 const gchar **attribute_names,
838 const gchar **attribute_values,
841 const gchar *name, *exec, *count, *stamp, *modified;
847 g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_APPLICATION));
850 name = exec = count = stamp = modified = NULL;
851 for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
853 if (IS_ATTRIBUTE (attr, BOOKMARK_NAME_ATTRIBUTE))
854 name = attribute_values[i];
855 else if (IS_ATTRIBUTE (attr, BOOKMARK_EXEC_ATTRIBUTE))
856 exec = attribute_values[i];
857 else if (IS_ATTRIBUTE (attr, BOOKMARK_COUNT_ATTRIBUTE))
858 count = attribute_values[i];
859 else if (IS_ATTRIBUTE (attr, BOOKMARK_TIMESTAMP_ATTRIBUTE))
860 stamp = attribute_values[i];
861 else if (IS_ATTRIBUTE (attr, BOOKMARK_MODIFIED_ATTRIBUTE))
862 modified = attribute_values[i];
865 /* the "name" and "exec" attributes are mandatory */
868 g_set_error (error, G_MARKUP_ERROR,
869 G_MARKUP_ERROR_INVALID_CONTENT,
870 _("Attribute “%s” of element “%s” not found"),
871 BOOKMARK_NAME_ATTRIBUTE,
872 BOOKMARK_APPLICATION_ELEMENT);
878 g_set_error (error, G_MARKUP_ERROR,
879 G_MARKUP_ERROR_INVALID_CONTENT,
880 _("Attribute “%s” of element “%s” not found"),
881 BOOKMARK_EXEC_ATTRIBUTE,
882 BOOKMARK_APPLICATION_ELEMENT);
886 g_warn_if_fail (parse_data->current_item != NULL);
887 item = parse_data->current_item;
889 ai = bookmark_item_lookup_app_info (item, name);
892 ai = bookmark_app_info_new (name);
895 item->metadata = bookmark_metadata_new ();
897 item->metadata->applications = g_list_prepend (item->metadata->applications, ai);
898 g_hash_table_replace (item->metadata->apps_by_name, ai->name, ai);
902 ai->exec = g_strdup (exec);
905 ai->count = atoi (count);
909 g_clear_pointer (&ai->stamp, g_date_time_unref);
910 if (modified != NULL)
912 if (!timestamp_from_iso8601 (modified, &ai->stamp, error))
917 /* the timestamp attribute has been deprecated but we still parse
918 * it for backward compatibility
921 ai->stamp = g_date_time_new_from_unix_utc (atol (stamp));
923 ai->stamp = g_date_time_new_now_utc ();
928 parse_mime_type_element (GMarkupParseContext *context,
929 ParseData *parse_data,
930 const gchar **attribute_names,
931 const gchar **attribute_values,
939 g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_MIME));
943 for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
945 if (IS_ATTRIBUTE (attr, MIME_TYPE_ATTRIBUTE))
946 type = attribute_values[i];
950 type = "application/octet-stream";
952 g_warn_if_fail (parse_data->current_item != NULL);
953 item = parse_data->current_item;
956 item->metadata = bookmark_metadata_new ();
958 g_free (item->metadata->mime_type);
959 item->metadata->mime_type = g_strdup (type);
963 parse_icon_element (GMarkupParseContext *context,
964 ParseData *parse_data,
965 const gchar **attribute_names,
966 const gchar **attribute_values,
975 g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_ICON));
980 for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
982 if (IS_ATTRIBUTE (attr, BOOKMARK_HREF_ATTRIBUTE))
983 href = attribute_values[i];
984 else if (IS_ATTRIBUTE (attr, BOOKMARK_TYPE_ATTRIBUTE))
985 type = attribute_values[i];
988 /* the "href" attribute is mandatory */
991 g_set_error (error, G_MARKUP_ERROR,
992 G_MARKUP_ERROR_INVALID_CONTENT,
993 _("Attribute “%s” of element “%s” not found"),
994 BOOKMARK_HREF_ATTRIBUTE,
995 BOOKMARK_ICON_ELEMENT);
1000 type = "application/octet-stream";
1002 g_warn_if_fail (parse_data->current_item != NULL);
1003 item = parse_data->current_item;
1005 if (!item->metadata)
1006 item->metadata = bookmark_metadata_new ();
1008 g_free (item->metadata->icon_href);
1009 g_free (item->metadata->icon_mime);
1010 item->metadata->icon_href = g_strdup (href);
1011 item->metadata->icon_mime = g_strdup (type);
1014 /* scans through the attributes of an element for the "xmlns" pragma, and
1015 * adds any resulting namespace declaration to a per-parser hashtable, using
1016 * the namespace name as a key for the namespace URI; if no key was found,
1017 * the namespace is considered as default, and stored under the "default" key.
1019 * FIXME: this works on the assumption that the generator of the XBEL file
1020 * is either this code or is smart enough to place the namespace declarations
1021 * inside the main root node or inside the metadata node and does not redefine
1022 * a namespace inside an inner node; this does *not* conform to the
1023 * XML-NS standard, although is a close approximation. In order to make this
1024 * conformant to the XML-NS specification we should use a per-element
1025 * namespace table inside GMarkup and ask it to resolve the namespaces for us.
1028 map_namespace_to_name (ParseData *parse_data,
1029 const gchar **attribute_names,
1030 const gchar **attribute_values)
1035 g_warn_if_fail (parse_data != NULL);
1037 if (!attribute_names || !attribute_names[0])
1041 for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
1043 if (g_str_has_prefix (attr, "xmlns"))
1045 gchar *namespace_name, *namespace_uri;
1048 p = g_utf8_strchr (attr, -1, ':');
1050 p = g_utf8_next_char (p);
1054 namespace_name = g_strdup (p);
1055 namespace_uri = g_strdup (attribute_values[i]);
1057 g_hash_table_replace (parse_data->namespaces,
1064 /* checks whether @element_full is equal to @element.
1066 * if @namespace is set, it tries to resolve the namespace to a known URI,
1067 * and if found is prepended to the element name, from which is separated
1068 * using the character specified in the @sep parameter.
1071 is_element_full (ParseData *parse_data,
1072 const gchar *element_full,
1073 const gchar *namespace,
1074 const gchar *element,
1077 gchar *ns_uri, *ns_name;
1078 const gchar *p, *element_name;
1081 g_warn_if_fail (parse_data != NULL);
1082 g_warn_if_fail (element_full != NULL);
1087 /* no namespace requested: dumb element compare */
1089 return (0 == strcmp (element_full, element));
1091 /* search for namespace separator; if none found, assume we are under the
1092 * default namespace, and set ns_name to our "default" marker; if no default
1093 * namespace has been set, just do a plain comparison between @full_element
1096 p = g_utf8_strchr (element_full, -1, ':');
1099 ns_name = g_strndup (element_full, p - element_full);
1100 element_name = g_utf8_next_char (p);
1104 ns_name = g_strdup ("default");
1105 element_name = element_full;
1108 ns_uri = g_hash_table_lookup (parse_data->namespaces, ns_name);
1111 /* no default namespace found */
1114 return (0 == strcmp (element_full, element));
1117 retval = (0 == strcmp (ns_uri, namespace) &&
1118 0 == strcmp (element_name, element));
1125 #define IS_ELEMENT(p,s,e) (is_element_full ((p), (s), NULL, (e), '\0'))
1126 #define IS_ELEMENT_NS(p,s,n,e) (is_element_full ((p), (s), (n), (e), '|'))
1128 static const gchar *
1129 parser_state_to_element_name (ParserState state)
1134 case STATE_FINISHED:
1135 return "(top-level)";
1137 return XBEL_ROOT_ELEMENT;
1138 case STATE_BOOKMARK:
1139 return XBEL_BOOKMARK_ELEMENT;
1141 return XBEL_TITLE_ELEMENT;
1143 return XBEL_DESC_ELEMENT;
1145 return XBEL_INFO_ELEMENT;
1146 case STATE_METADATA:
1147 return XBEL_METADATA_ELEMENT;
1148 case STATE_APPLICATIONS:
1149 return BOOKMARK_APPLICATIONS_ELEMENT;
1150 case STATE_APPLICATION:
1151 return BOOKMARK_APPLICATION_ELEMENT;
1153 return BOOKMARK_GROUPS_ELEMENT;
1155 return BOOKMARK_GROUP_ELEMENT;
1157 return MIME_TYPE_ELEMENT;
1159 return BOOKMARK_ICON_ELEMENT;
1161 g_assert_not_reached ();
1166 start_element_raw_cb (GMarkupParseContext *context,
1167 const gchar *element_name,
1168 const gchar **attribute_names,
1169 const gchar **attribute_values,
1173 ParseData *parse_data = (ParseData *) user_data;
1175 /* we must check for namespace declarations first
1177 * XXX - we could speed up things by checking for namespace declarations
1178 * only on the root node, where they usually are; this would probably break
1179 * on streams not produced by us or by "smart" generators
1181 map_namespace_to_name (parse_data, attribute_names, attribute_values);
1183 switch (parse_data->state)
1186 if (IS_ELEMENT (parse_data, element_name, XBEL_ROOT_ELEMENT))
1192 for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
1194 if ((IS_ATTRIBUTE (attr, XBEL_VERSION_ATTRIBUTE)) &&
1195 (0 == strcmp (attribute_values[i], XBEL_VERSION)))
1196 parse_data->state = STATE_ROOT;
1200 g_set_error (error, G_MARKUP_ERROR,
1201 G_MARKUP_ERROR_INVALID_CONTENT,
1202 _("Unexpected tag “%s”, tag “%s” expected"),
1203 element_name, XBEL_ROOT_ELEMENT);
1206 if (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT))
1207 parse_data->state = STATE_TITLE;
1208 else if (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT))
1209 parse_data->state = STATE_DESC;
1210 else if (IS_ELEMENT (parse_data, element_name, XBEL_BOOKMARK_ELEMENT))
1212 GError *inner_error = NULL;
1214 parse_data->state = STATE_BOOKMARK;
1216 parse_bookmark_element (context,
1222 g_propagate_error (error, inner_error);
1225 g_set_error (error, G_MARKUP_ERROR,
1226 G_MARKUP_ERROR_INVALID_CONTENT,
1227 _("Unexpected tag “%s” inside “%s”"),
1231 case STATE_BOOKMARK:
1232 if (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT))
1233 parse_data->state = STATE_TITLE;
1234 else if (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT))
1235 parse_data->state = STATE_DESC;
1236 else if (IS_ELEMENT (parse_data, element_name, XBEL_INFO_ELEMENT))
1237 parse_data->state = STATE_INFO;
1239 g_set_error (error, G_MARKUP_ERROR,
1240 G_MARKUP_ERROR_INVALID_CONTENT,
1241 _("Unexpected tag “%s” inside “%s”"),
1243 XBEL_BOOKMARK_ELEMENT);
1246 if (IS_ELEMENT (parse_data, element_name, XBEL_METADATA_ELEMENT))
1252 for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
1254 if ((IS_ATTRIBUTE (attr, XBEL_OWNER_ATTRIBUTE)) &&
1255 (0 == strcmp (attribute_values[i], BOOKMARK_METADATA_OWNER)))
1257 parse_data->state = STATE_METADATA;
1259 if (!parse_data->current_item->metadata)
1260 parse_data->current_item->metadata = bookmark_metadata_new ();
1265 g_set_error (error, G_MARKUP_ERROR,
1266 G_MARKUP_ERROR_INVALID_CONTENT,
1267 _("Unexpected tag “%s”, tag “%s” expected"),
1269 XBEL_METADATA_ELEMENT);
1271 case STATE_METADATA:
1272 if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATIONS_ELEMENT))
1273 parse_data->state = STATE_APPLICATIONS;
1274 else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUPS_ELEMENT))
1275 parse_data->state = STATE_GROUPS;
1276 else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_PRIVATE_ELEMENT))
1277 parse_data->current_item->metadata->is_private = TRUE;
1278 else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_ICON_ELEMENT))
1280 GError *inner_error = NULL;
1282 parse_data->state = STATE_ICON;
1284 parse_icon_element (context,
1290 g_propagate_error (error, inner_error);
1292 else if (IS_ELEMENT_NS (parse_data, element_name, MIME_NAMESPACE_URI, MIME_TYPE_ELEMENT))
1294 GError *inner_error = NULL;
1296 parse_data->state = STATE_MIME;
1298 parse_mime_type_element (context,
1304 g_propagate_error (error, inner_error);
1307 g_set_error (error, G_MARKUP_ERROR,
1308 G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1309 _("Unexpected tag “%s” inside “%s”"),
1311 XBEL_METADATA_ELEMENT);
1313 case STATE_APPLICATIONS:
1314 if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATION_ELEMENT))
1316 GError *inner_error = NULL;
1318 parse_data->state = STATE_APPLICATION;
1320 parse_application_element (context,
1326 g_propagate_error (error, inner_error);
1329 g_set_error (error, G_MARKUP_ERROR,
1330 G_MARKUP_ERROR_INVALID_CONTENT,
1331 _("Unexpected tag “%s”, tag “%s” expected"),
1333 BOOKMARK_APPLICATION_ELEMENT);
1336 if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUP_ELEMENT))
1337 parse_data->state = STATE_GROUP;
1339 g_set_error (error, G_MARKUP_ERROR,
1340 G_MARKUP_ERROR_INVALID_CONTENT,
1341 _("Unexpected tag “%s”, tag “%s” expected"),
1343 BOOKMARK_GROUP_ELEMENT);
1348 case STATE_APPLICATION:
1352 case STATE_FINISHED:
1353 g_set_error (error, G_MARKUP_ERROR,
1354 G_MARKUP_ERROR_INVALID_CONTENT,
1355 _("Unexpected tag “%s” inside “%s”"),
1357 parser_state_to_element_name (parse_data->state));
1361 g_assert_not_reached ();
1367 end_element_raw_cb (GMarkupParseContext *context,
1368 const gchar *element_name,
1372 ParseData *parse_data = (ParseData *) user_data;
1374 if (IS_ELEMENT (parse_data, element_name, XBEL_ROOT_ELEMENT))
1375 parse_data->state = STATE_FINISHED;
1376 else if (IS_ELEMENT (parse_data, element_name, XBEL_BOOKMARK_ELEMENT))
1378 parse_data->current_item = NULL;
1380 parse_data->state = STATE_ROOT;
1382 else if ((IS_ELEMENT (parse_data, element_name, XBEL_INFO_ELEMENT)) ||
1383 (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT)) ||
1384 (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT)))
1386 if (parse_data->current_item)
1387 parse_data->state = STATE_BOOKMARK;
1389 parse_data->state = STATE_ROOT;
1391 else if (IS_ELEMENT (parse_data, element_name, XBEL_METADATA_ELEMENT))
1392 parse_data->state = STATE_INFO;
1393 else if (IS_ELEMENT_NS (parse_data, element_name,
1394 BOOKMARK_NAMESPACE_URI,
1395 BOOKMARK_APPLICATION_ELEMENT))
1396 parse_data->state = STATE_APPLICATIONS;
1397 else if (IS_ELEMENT_NS (parse_data, element_name,
1398 BOOKMARK_NAMESPACE_URI,
1399 BOOKMARK_GROUP_ELEMENT))
1400 parse_data->state = STATE_GROUPS;
1401 else if ((IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATIONS_ELEMENT)) ||
1402 (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUPS_ELEMENT)) ||
1403 (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_PRIVATE_ELEMENT)) ||
1404 (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_ICON_ELEMENT)) ||
1405 (IS_ELEMENT_NS (parse_data, element_name, MIME_NAMESPACE_URI, MIME_TYPE_ELEMENT)))
1406 parse_data->state = STATE_METADATA;
1410 text_raw_cb (GMarkupParseContext *context,
1416 ParseData *parse_data = (ParseData *) user_data;
1419 payload = g_strndup (text, length);
1421 switch (parse_data->state)
1424 if (parse_data->current_item)
1426 g_free (parse_data->current_item->title);
1427 parse_data->current_item->title = g_strdup (payload);
1431 g_free (parse_data->bookmark_file->title);
1432 parse_data->bookmark_file->title = g_strdup (payload);
1436 if (parse_data->current_item)
1438 g_free (parse_data->current_item->description);
1439 parse_data->current_item->description = g_strdup (payload);
1443 g_free (parse_data->bookmark_file->description);
1444 parse_data->bookmark_file->description = g_strdup (payload);
1451 g_warn_if_fail (parse_data->current_item != NULL);
1453 if (!parse_data->current_item->metadata)
1454 parse_data->current_item->metadata = bookmark_metadata_new ();
1456 groups = parse_data->current_item->metadata->groups;
1457 parse_data->current_item->metadata->groups = g_list_prepend (groups, g_strdup (payload));
1461 case STATE_BOOKMARK:
1463 case STATE_METADATA:
1464 case STATE_APPLICATIONS:
1465 case STATE_APPLICATION:
1471 g_warn_if_reached ();
1478 static const GMarkupParser markup_parser =
1480 start_element_raw_cb, /* start_element */
1481 end_element_raw_cb, /* end_element */
1482 text_raw_cb, /* text */
1483 NULL, /* passthrough */
1488 g_bookmark_file_parse (GBookmarkFile *bookmark,
1489 const gchar *buffer,
1493 GMarkupParseContext *context;
1494 ParseData *parse_data;
1495 GError *parse_error, *end_error;
1498 g_warn_if_fail (bookmark != NULL);
1506 if (length == (gsize) -1)
1507 length = strlen (buffer);
1509 parse_data = parse_data_new ();
1510 parse_data->bookmark_file = bookmark;
1512 context = g_markup_parse_context_new (&markup_parser,
1513 G_MARKUP_DEFAULT_FLAGS,
1515 (GDestroyNotify) parse_data_free);
1517 retval = g_markup_parse_context_parse (context,
1522 g_propagate_error (error, parse_error);
1525 retval = g_markup_parse_context_end_parse (context, &end_error);
1527 g_propagate_error (error, end_error);
1530 g_markup_parse_context_free (context);
1536 g_bookmark_file_dump (GBookmarkFile *bookmark,
1544 retval = g_string_sized_new (4096);
1546 g_string_append (retval,
1547 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1549 /* XXX - do we really need the doctype? */
1550 "<!DOCTYPE " XBEL_DTD_NICK "\n"
1551 " PUBLIC \"" XBEL_DTD_SYSTEM "\"\n"
1552 " \"" XBEL_DTD_URI "\">\n"
1554 "<" XBEL_ROOT_ELEMENT " " XBEL_VERSION_ATTRIBUTE "=\"" XBEL_VERSION "\"\n"
1555 " xmlns:" BOOKMARK_NAMESPACE_NAME "=\"" BOOKMARK_NAMESPACE_URI "\"\n"
1556 " xmlns:" MIME_NAMESPACE_NAME "=\"" MIME_NAMESPACE_URI "\"\n>");
1558 if (bookmark->title)
1560 gchar *escaped_title;
1562 escaped_title = g_markup_escape_text (bookmark->title, -1);
1564 buffer = g_strconcat (" "
1565 "<" XBEL_TITLE_ELEMENT ">",
1567 "</" XBEL_TITLE_ELEMENT ">\n", NULL);
1569 g_string_append (retval, buffer);
1572 g_free (escaped_title);
1575 if (bookmark->description)
1577 gchar *escaped_desc;
1579 escaped_desc = g_markup_escape_text (bookmark->description, -1);
1581 buffer = g_strconcat (" "
1582 "<" XBEL_DESC_ELEMENT ">",
1584 "</" XBEL_DESC_ELEMENT ">\n", NULL);
1585 g_string_append (retval, buffer);
1588 g_free (escaped_desc);
1591 if (!bookmark->items)
1594 retval = g_string_append (retval, "\n");
1596 /* the items are stored in reverse order */
1597 for (l = g_list_last (bookmark->items);
1601 BookmarkItem *item = (BookmarkItem *) l->data;
1604 item_dump = bookmark_item_dump (item);
1608 retval = g_string_append (retval, item_dump);
1614 g_string_append (retval, "</" XBEL_ROOT_ELEMENT ">");
1617 *length = retval->len;
1619 return g_string_free (retval, FALSE);
1627 timestamp_from_iso8601 (const gchar *iso_date,
1628 GDateTime **out_date_time,
1631 GDateTime *dt = g_date_time_new_from_iso8601 (iso_date, NULL);
1634 g_set_error (error, G_BOOKMARK_FILE_ERROR, G_BOOKMARK_FILE_ERROR_READ,
1635 _("Invalid date/time ‘%s’ in bookmark file"), iso_date);
1639 *out_date_time = g_steal_pointer (&dt);
1643 G_DEFINE_QUARK (g-bookmark-file-error-quark, g_bookmark_file_error)
1645 /********************
1647 ********************/
1650 * g_bookmark_file_new: (constructor)
1652 * Creates a new empty #GBookmarkFile object.
1654 * Use g_bookmark_file_load_from_file(), g_bookmark_file_load_from_data()
1655 * or g_bookmark_file_load_from_data_dirs() to read an existing bookmark
1658 * Returns: an empty #GBookmarkFile
1663 g_bookmark_file_new (void)
1665 GBookmarkFile *bookmark;
1667 bookmark = g_new (GBookmarkFile, 1);
1669 g_bookmark_file_init (bookmark);
1675 * g_bookmark_file_free:
1676 * @bookmark: a #GBookmarkFile
1678 * Frees a #GBookmarkFile.
1683 g_bookmark_file_free (GBookmarkFile *bookmark)
1688 g_bookmark_file_clear (bookmark);
1694 * g_bookmark_file_load_from_data:
1695 * @bookmark: an empty #GBookmarkFile struct
1696 * @data: (array length=length) (element-type guint8): desktop bookmarks
1698 * @length: the length of @data in bytes
1699 * @error: return location for a #GError, or %NULL
1701 * Loads a bookmark file from memory into an empty #GBookmarkFile
1702 * structure. If the object cannot be created then @error is set to a
1703 * #GBookmarkFileError.
1705 * Returns: %TRUE if a desktop bookmark could be loaded.
1710 g_bookmark_file_load_from_data (GBookmarkFile *bookmark,
1715 GError *parse_error;
1718 g_return_val_if_fail (bookmark != NULL, FALSE);
1720 if (length == (gsize) -1)
1721 length = strlen (data);
1723 if (bookmark->items)
1725 g_bookmark_file_clear (bookmark);
1726 g_bookmark_file_init (bookmark);
1730 retval = g_bookmark_file_parse (bookmark, data, length, &parse_error);
1733 g_propagate_error (error, parse_error);
1739 * g_bookmark_file_load_from_file:
1740 * @bookmark: an empty #GBookmarkFile struct
1741 * @filename: (type filename): the path of a filename to load, in the
1742 * GLib file name encoding
1743 * @error: return location for a #GError, or %NULL
1745 * Loads a desktop bookmark file into an empty #GBookmarkFile structure.
1746 * If the file could not be loaded then @error is set to either a #GFileError
1747 * or #GBookmarkFileError.
1749 * Returns: %TRUE if a desktop bookmark file could be loaded
1754 g_bookmark_file_load_from_file (GBookmarkFile *bookmark,
1755 const gchar *filename,
1758 gboolean ret = FALSE;
1759 gchar *buffer = NULL;
1762 g_return_val_if_fail (bookmark != NULL, FALSE);
1763 g_return_val_if_fail (filename != NULL, FALSE);
1765 if (!g_file_get_contents (filename, &buffer, &len, error))
1768 if (!g_bookmark_file_load_from_data (bookmark, buffer, len, error))
1778 /* Iterates through all the directories in *dirs trying to
1779 * find file. When it successfully locates file, returns a
1780 * string its absolute path. It also leaves the unchecked
1781 * directories in *dirs. You should free the returned string
1783 * Adapted from gkeyfile.c
1786 find_file_in_data_dirs (const gchar *file,
1790 gchar **data_dirs, *data_dir, *path;
1799 while (data_dirs && (data_dir = *data_dirs) && !path)
1801 gchar *candidate_file, *sub_dir;
1803 candidate_file = (gchar *) file;
1804 sub_dir = g_strdup ("");
1805 while (candidate_file != NULL && !path)
1809 path = g_build_filename (data_dir, sub_dir,
1810 candidate_file, NULL);
1812 candidate_file = strchr (candidate_file, '-');
1814 if (candidate_file == NULL)
1820 sub_dir = g_strndup (file, candidate_file - file - 1);
1822 for (p = sub_dir; *p != '\0'; p++)
1825 *p = G_DIR_SEPARATOR;
1836 g_set_error_literal (error, G_BOOKMARK_FILE_ERROR,
1837 G_BOOKMARK_FILE_ERROR_FILE_NOT_FOUND,
1838 _("No valid bookmark file found in data dirs"));
1848 * g_bookmark_file_load_from_data_dirs:
1849 * @bookmark: a #GBookmarkFile
1850 * @file: (type filename): a relative path to a filename to open and parse
1851 * @full_path: (out) (optional) (type filename): return location for a string
1852 * containing the full path of the file, or %NULL
1853 * @error: return location for a #GError, or %NULL
1855 * This function looks for a desktop bookmark file named @file in the
1856 * paths returned from g_get_user_data_dir() and g_get_system_data_dirs(),
1857 * loads the file into @bookmark and returns the file's full path in
1858 * @full_path. If the file could not be loaded then @error is
1859 * set to either a #GFileError or #GBookmarkFileError.
1861 * Returns: %TRUE if a key file could be loaded, %FALSE otherwise
1866 g_bookmark_file_load_from_data_dirs (GBookmarkFile *bookmark,
1871 GError *file_error = NULL;
1872 gchar **all_data_dirs, **data_dirs;
1873 const gchar *user_data_dir;
1874 const gchar * const * system_data_dirs;
1877 gboolean found_file;
1879 g_return_val_if_fail (bookmark != NULL, FALSE);
1880 g_return_val_if_fail (!g_path_is_absolute (file), FALSE);
1882 user_data_dir = g_get_user_data_dir ();
1883 system_data_dirs = g_get_system_data_dirs ();
1884 all_data_dirs = g_new0 (gchar *, g_strv_length ((gchar **)system_data_dirs) + 2);
1887 all_data_dirs[i++] = g_strdup (user_data_dir);
1890 while (system_data_dirs[j] != NULL)
1891 all_data_dirs[i++] = g_strdup (system_data_dirs[j++]);
1894 data_dirs = all_data_dirs;
1896 while (*data_dirs != NULL && !found_file)
1898 g_free (output_path);
1900 output_path = find_file_in_data_dirs (file, &data_dirs, &file_error);
1904 g_propagate_error (error, file_error);
1908 found_file = g_bookmark_file_load_from_file (bookmark,
1913 g_propagate_error (error, file_error);
1918 if (found_file && full_path)
1919 *full_path = output_path;
1921 g_free (output_path);
1923 g_strfreev (all_data_dirs);
1930 * g_bookmark_file_to_data:
1931 * @bookmark: a #GBookmarkFile
1932 * @length: (out) (optional): return location for the length of the returned string, or %NULL
1933 * @error: return location for a #GError, or %NULL
1935 * This function outputs @bookmark as a string.
1937 * Returns: (transfer full) (array length=length) (element-type guint8):
1938 * a newly allocated string holding the contents of the #GBookmarkFile
1943 g_bookmark_file_to_data (GBookmarkFile *bookmark,
1947 GError *write_error = NULL;
1950 g_return_val_if_fail (bookmark != NULL, NULL);
1952 retval = g_bookmark_file_dump (bookmark, length, &write_error);
1955 g_propagate_error (error, write_error);
1964 * g_bookmark_file_to_file:
1965 * @bookmark: a #GBookmarkFile
1966 * @filename: (type filename): path of the output file
1967 * @error: return location for a #GError, or %NULL
1969 * This function outputs @bookmark into a file. The write process is
1970 * guaranteed to be atomic by using g_file_set_contents() internally.
1972 * Returns: %TRUE if the file was successfully written.
1977 g_bookmark_file_to_file (GBookmarkFile *bookmark,
1978 const gchar *filename,
1982 GError *data_error, *write_error;
1986 g_return_val_if_fail (bookmark != NULL, FALSE);
1987 g_return_val_if_fail (filename != NULL, FALSE);
1990 data = g_bookmark_file_to_data (bookmark, &len, &data_error);
1993 g_propagate_error (error, data_error);
1999 g_file_set_contents (filename, data, len, &write_error);
2002 g_propagate_error (error, write_error);
2014 static BookmarkItem *
2015 g_bookmark_file_lookup_item (GBookmarkFile *bookmark,
2018 g_warn_if_fail (bookmark != NULL && uri != NULL);
2020 return g_hash_table_lookup (bookmark->items_by_uri, uri);
2023 /* this function adds a new item to the list */
2025 g_bookmark_file_add_item (GBookmarkFile *bookmark,
2029 g_warn_if_fail (bookmark != NULL);
2030 g_warn_if_fail (item != NULL);
2032 /* this should never happen; and if it does, then we are
2033 * screwing up something big time.
2035 if (G_UNLIKELY (g_bookmark_file_has_item (bookmark, item->uri)))
2037 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2038 G_BOOKMARK_FILE_ERROR_INVALID_URI,
2039 _("A bookmark for URI “%s” already exists"),
2044 bookmark->items = g_list_prepend (bookmark->items, item);
2046 g_hash_table_replace (bookmark->items_by_uri,
2050 if (item->added == NULL)
2051 item->added = g_date_time_new_now_utc ();
2053 if (item->modified == NULL)
2054 item->modified = g_date_time_new_now_utc ();
2056 if (item->visited == NULL)
2057 item->visited = g_date_time_new_now_utc ();
2061 * g_bookmark_file_remove_item:
2062 * @bookmark: a #GBookmarkFile
2064 * @error: return location for a #GError, or %NULL
2066 * Removes the bookmark for @uri from the bookmark file @bookmark.
2068 * Returns: %TRUE if the bookmark was removed successfully.
2073 g_bookmark_file_remove_item (GBookmarkFile *bookmark,
2079 g_return_val_if_fail (bookmark != NULL, FALSE);
2080 g_return_val_if_fail (uri != NULL, FALSE);
2082 item = g_bookmark_file_lookup_item (bookmark, uri);
2086 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2087 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2088 _("No bookmark found for URI “%s”"),
2093 bookmark->items = g_list_remove (bookmark->items, item);
2094 g_hash_table_remove (bookmark->items_by_uri, item->uri);
2096 bookmark_item_free (item);
2102 * g_bookmark_file_has_item:
2103 * @bookmark: a #GBookmarkFile
2106 * Looks whether the desktop bookmark has an item with its URI set to @uri.
2108 * Returns: %TRUE if @uri is inside @bookmark, %FALSE otherwise
2113 g_bookmark_file_has_item (GBookmarkFile *bookmark,
2116 g_return_val_if_fail (bookmark != NULL, FALSE);
2117 g_return_val_if_fail (uri != NULL, FALSE);
2119 return (NULL != g_hash_table_lookup (bookmark->items_by_uri, uri));
2123 * g_bookmark_file_get_uris:
2124 * @bookmark: a #GBookmarkFile
2125 * @length: (out) (optional): return location for the number of returned URIs, or %NULL
2127 * Returns all URIs of the bookmarks in the bookmark file @bookmark.
2128 * The array of returned URIs will be %NULL-terminated, so @length may
2129 * optionally be %NULL.
2131 * Returns: (array length=length) (transfer full): a newly allocated %NULL-terminated array of strings.
2132 * Use g_strfreev() to free it.
2137 g_bookmark_file_get_uris (GBookmarkFile *bookmark,
2144 g_return_val_if_fail (bookmark != NULL, NULL);
2146 n_items = g_list_length (bookmark->items);
2147 uris = g_new0 (gchar *, n_items + 1);
2149 /* the items are stored in reverse order, so we walk the list backward */
2150 for (l = g_list_last (bookmark->items), i = 0; l != NULL; l = l->prev)
2152 BookmarkItem *item = (BookmarkItem *) l->data;
2154 g_warn_if_fail (item != NULL);
2156 uris[i++] = g_strdup (item->uri);
2167 * g_bookmark_file_set_title:
2168 * @bookmark: a #GBookmarkFile
2169 * @uri: (nullable): a valid URI or %NULL
2170 * @title: a UTF-8 encoded string
2172 * Sets @title as the title of the bookmark for @uri inside the
2173 * bookmark file @bookmark.
2175 * If @uri is %NULL, the title of @bookmark is set.
2177 * If a bookmark for @uri cannot be found then it is created.
2182 g_bookmark_file_set_title (GBookmarkFile *bookmark,
2186 g_return_if_fail (bookmark != NULL);
2190 g_free (bookmark->title);
2191 bookmark->title = g_strdup (title);
2197 item = g_bookmark_file_lookup_item (bookmark, uri);
2200 item = bookmark_item_new (uri);
2201 g_bookmark_file_add_item (bookmark, item, NULL);
2204 g_free (item->title);
2205 item->title = g_strdup (title);
2207 bookmark_item_touch_modified (item);
2212 * g_bookmark_file_get_title:
2213 * @bookmark: a #GBookmarkFile
2214 * @uri: (nullable): a valid URI or %NULL
2215 * @error: return location for a #GError, or %NULL
2217 * Returns the title of the bookmark for @uri.
2219 * If @uri is %NULL, the title of @bookmark is returned.
2221 * In the event the URI cannot be found, %NULL is returned and
2222 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2224 * Returns: (transfer full): a newly allocated string or %NULL if the specified
2225 * URI cannot be found.
2230 g_bookmark_file_get_title (GBookmarkFile *bookmark,
2236 g_return_val_if_fail (bookmark != NULL, NULL);
2239 return g_strdup (bookmark->title);
2241 item = g_bookmark_file_lookup_item (bookmark, uri);
2244 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2245 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2246 _("No bookmark found for URI “%s”"),
2251 return g_strdup (item->title);
2255 * g_bookmark_file_set_description:
2256 * @bookmark: a #GBookmarkFile
2257 * @uri: (nullable): a valid URI or %NULL
2258 * @description: a string
2260 * Sets @description as the description of the bookmark for @uri.
2262 * If @uri is %NULL, the description of @bookmark is set.
2264 * If a bookmark for @uri cannot be found then it is created.
2269 g_bookmark_file_set_description (GBookmarkFile *bookmark,
2271 const gchar *description)
2273 g_return_if_fail (bookmark != NULL);
2277 g_free (bookmark->description);
2278 bookmark->description = g_strdup (description);
2284 item = g_bookmark_file_lookup_item (bookmark, uri);
2287 item = bookmark_item_new (uri);
2288 g_bookmark_file_add_item (bookmark, item, NULL);
2291 g_free (item->description);
2292 item->description = g_strdup (description);
2294 bookmark_item_touch_modified (item);
2299 * g_bookmark_file_get_description:
2300 * @bookmark: a #GBookmarkFile
2302 * @error: return location for a #GError, or %NULL
2304 * Retrieves the description of the bookmark for @uri.
2306 * In the event the URI cannot be found, %NULL is returned and
2307 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2309 * Returns: (transfer full): a newly allocated string or %NULL if the specified
2310 * URI cannot be found.
2315 g_bookmark_file_get_description (GBookmarkFile *bookmark,
2321 g_return_val_if_fail (bookmark != NULL, NULL);
2324 return g_strdup (bookmark->description);
2326 item = g_bookmark_file_lookup_item (bookmark, uri);
2329 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2330 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2331 _("No bookmark found for URI “%s”"),
2336 return g_strdup (item->description);
2340 * g_bookmark_file_set_mime_type:
2341 * @bookmark: a #GBookmarkFile
2343 * @mime_type: a MIME type
2345 * Sets @mime_type as the MIME type of the bookmark for @uri.
2347 * If a bookmark for @uri cannot be found then it is created.
2352 g_bookmark_file_set_mime_type (GBookmarkFile *bookmark,
2354 const gchar *mime_type)
2358 g_return_if_fail (bookmark != NULL);
2359 g_return_if_fail (uri != NULL);
2360 g_return_if_fail (mime_type != NULL);
2362 item = g_bookmark_file_lookup_item (bookmark, uri);
2365 item = bookmark_item_new (uri);
2366 g_bookmark_file_add_item (bookmark, item, NULL);
2369 if (!item->metadata)
2370 item->metadata = bookmark_metadata_new ();
2372 g_free (item->metadata->mime_type);
2374 item->metadata->mime_type = g_strdup (mime_type);
2375 bookmark_item_touch_modified (item);
2379 * g_bookmark_file_get_mime_type:
2380 * @bookmark: a #GBookmarkFile
2382 * @error: return location for a #GError, or %NULL
2384 * Retrieves the MIME type of the resource pointed by @uri.
2386 * In the event the URI cannot be found, %NULL is returned and
2387 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND. In the
2388 * event that the MIME type cannot be found, %NULL is returned and
2389 * @error is set to %G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
2391 * Returns: (transfer full): a newly allocated string or %NULL if the specified
2392 * URI cannot be found.
2397 g_bookmark_file_get_mime_type (GBookmarkFile *bookmark,
2403 g_return_val_if_fail (bookmark != NULL, NULL);
2404 g_return_val_if_fail (uri != NULL, NULL);
2406 item = g_bookmark_file_lookup_item (bookmark, uri);
2409 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2410 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2411 _("No bookmark found for URI “%s”"),
2416 if (!item->metadata)
2418 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2419 G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
2420 _("No MIME type defined in the bookmark for URI “%s”"),
2425 return g_strdup (item->metadata->mime_type);
2429 * g_bookmark_file_set_is_private:
2430 * @bookmark: a #GBookmarkFile
2432 * @is_private: %TRUE if the bookmark should be marked as private
2434 * Sets the private flag of the bookmark for @uri.
2436 * If a bookmark for @uri cannot be found then it is created.
2441 g_bookmark_file_set_is_private (GBookmarkFile *bookmark,
2443 gboolean is_private)
2447 g_return_if_fail (bookmark != NULL);
2448 g_return_if_fail (uri != NULL);
2450 item = g_bookmark_file_lookup_item (bookmark, uri);
2453 item = bookmark_item_new (uri);
2454 g_bookmark_file_add_item (bookmark, item, NULL);
2457 if (!item->metadata)
2458 item->metadata = bookmark_metadata_new ();
2460 item->metadata->is_private = (is_private == TRUE);
2461 bookmark_item_touch_modified (item);
2465 * g_bookmark_file_get_is_private:
2466 * @bookmark: a #GBookmarkFile
2468 * @error: return location for a #GError, or %NULL
2470 * Gets whether the private flag of the bookmark for @uri is set.
2472 * In the event the URI cannot be found, %FALSE is returned and
2473 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND. In the
2474 * event that the private flag cannot be found, %FALSE is returned and
2475 * @error is set to %G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
2477 * Returns: %TRUE if the private flag is set, %FALSE otherwise.
2482 g_bookmark_file_get_is_private (GBookmarkFile *bookmark,
2488 g_return_val_if_fail (bookmark != NULL, FALSE);
2489 g_return_val_if_fail (uri != NULL, FALSE);
2491 item = g_bookmark_file_lookup_item (bookmark, uri);
2494 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2495 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2496 _("No bookmark found for URI “%s”"),
2501 if (!item->metadata)
2503 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2504 G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
2505 _("No private flag has been defined in bookmark for URI “%s”"),
2510 return item->metadata->is_private;
2514 * g_bookmark_file_set_added:
2515 * @bookmark: a #GBookmarkFile
2517 * @added: a timestamp or -1 to use the current time
2519 * Sets the time the bookmark for @uri was added into @bookmark.
2521 * If no bookmark for @uri is found then it is created.
2524 * Deprecated: 2.66: Use g_bookmark_file_set_added_date_time() instead, as
2525 * `time_t` is deprecated due to the year 2038 problem.
2528 g_bookmark_file_set_added (GBookmarkFile *bookmark,
2532 GDateTime *added_dt = (added != (time_t) -1) ? g_date_time_new_from_unix_utc (added) : g_date_time_new_now_utc ();
2533 g_bookmark_file_set_added_date_time (bookmark, uri, added_dt);
2534 g_date_time_unref (added_dt);
2538 * g_bookmark_file_set_added_date_time:
2539 * @bookmark: a #GBookmarkFile
2541 * @added: a #GDateTime
2543 * Sets the time the bookmark for @uri was added into @bookmark.
2545 * If no bookmark for @uri is found then it is created.
2550 g_bookmark_file_set_added_date_time (GBookmarkFile *bookmark,
2556 g_return_if_fail (bookmark != NULL);
2557 g_return_if_fail (uri != NULL);
2558 g_return_if_fail (added != NULL);
2560 item = g_bookmark_file_lookup_item (bookmark, uri);
2563 item = bookmark_item_new (uri);
2564 g_bookmark_file_add_item (bookmark, item, NULL);
2567 g_clear_pointer (&item->added, g_date_time_unref);
2568 item->added = g_date_time_ref (added);
2569 g_clear_pointer (&item->modified, g_date_time_unref);
2570 item->modified = g_date_time_ref (added);
2574 * g_bookmark_file_get_added:
2575 * @bookmark: a #GBookmarkFile
2577 * @error: return location for a #GError, or %NULL
2579 * Gets the time the bookmark for @uri was added to @bookmark
2581 * In the event the URI cannot be found, -1 is returned and
2582 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2584 * Returns: a timestamp
2587 * Deprecated: 2.66: Use g_bookmark_file_get_added_date_time() instead, as
2588 * `time_t` is deprecated due to the year 2038 problem.
2591 g_bookmark_file_get_added (GBookmarkFile *bookmark,
2595 GDateTime *added = g_bookmark_file_get_added_date_time (bookmark, uri, error);
2596 return (added != NULL) ? g_date_time_to_unix (added) : (time_t) -1;
2600 * g_bookmark_file_get_added_date_time:
2601 * @bookmark: a #GBookmarkFile
2603 * @error: return location for a #GError, or %NULL
2605 * Gets the time the bookmark for @uri was added to @bookmark
2607 * In the event the URI cannot be found, %NULL is returned and
2608 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2610 * Returns: (transfer none): a #GDateTime
2615 g_bookmark_file_get_added_date_time (GBookmarkFile *bookmark,
2621 g_return_val_if_fail (bookmark != NULL, NULL);
2622 g_return_val_if_fail (uri != NULL, NULL);
2623 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2625 item = g_bookmark_file_lookup_item (bookmark, uri);
2628 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2629 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2630 _("No bookmark found for URI “%s”"),
2639 * g_bookmark_file_set_modified:
2640 * @bookmark: a #GBookmarkFile
2642 * @modified: a timestamp or -1 to use the current time
2644 * Sets the last time the bookmark for @uri was last modified.
2646 * If no bookmark for @uri is found then it is created.
2648 * The "modified" time should only be set when the bookmark's meta-data
2649 * was actually changed. Every function of #GBookmarkFile that
2650 * modifies a bookmark also changes the modification time, except for
2651 * g_bookmark_file_set_visited_date_time().
2654 * Deprecated: 2.66: Use g_bookmark_file_set_modified_date_time() instead, as
2655 * `time_t` is deprecated due to the year 2038 problem.
2658 g_bookmark_file_set_modified (GBookmarkFile *bookmark,
2662 GDateTime *modified_dt = (modified != (time_t) -1) ? g_date_time_new_from_unix_utc (modified) : g_date_time_new_now_utc ();
2663 g_bookmark_file_set_modified_date_time (bookmark, uri, modified_dt);
2664 g_date_time_unref (modified_dt);
2668 * g_bookmark_file_set_modified_date_time:
2669 * @bookmark: a #GBookmarkFile
2671 * @modified: a #GDateTime
2673 * Sets the last time the bookmark for @uri was last modified.
2675 * If no bookmark for @uri is found then it is created.
2677 * The "modified" time should only be set when the bookmark's meta-data
2678 * was actually changed. Every function of #GBookmarkFile that
2679 * modifies a bookmark also changes the modification time, except for
2680 * g_bookmark_file_set_visited_date_time().
2685 g_bookmark_file_set_modified_date_time (GBookmarkFile *bookmark,
2687 GDateTime *modified)
2691 g_return_if_fail (bookmark != NULL);
2692 g_return_if_fail (uri != NULL);
2693 g_return_if_fail (modified != NULL);
2695 item = g_bookmark_file_lookup_item (bookmark, uri);
2698 item = bookmark_item_new (uri);
2699 g_bookmark_file_add_item (bookmark, item, NULL);
2702 g_clear_pointer (&item->modified, g_date_time_unref);
2703 item->modified = g_date_time_ref (modified);
2707 * g_bookmark_file_get_modified:
2708 * @bookmark: a #GBookmarkFile
2710 * @error: return location for a #GError, or %NULL
2712 * Gets the time when the bookmark for @uri was last modified.
2714 * In the event the URI cannot be found, -1 is returned and
2715 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2717 * Returns: a timestamp
2720 * Deprecated: 2.66: Use g_bookmark_file_get_modified_date_time() instead, as
2721 * `time_t` is deprecated due to the year 2038 problem.
2724 g_bookmark_file_get_modified (GBookmarkFile *bookmark,
2728 GDateTime *modified = g_bookmark_file_get_modified_date_time (bookmark, uri, error);
2729 return (modified != NULL) ? g_date_time_to_unix (modified) : (time_t) -1;
2733 * g_bookmark_file_get_modified_date_time:
2734 * @bookmark: a #GBookmarkFile
2736 * @error: return location for a #GError, or %NULL
2738 * Gets the time when the bookmark for @uri was last modified.
2740 * In the event the URI cannot be found, %NULL is returned and
2741 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2743 * Returns: (transfer none): a #GDateTime
2748 g_bookmark_file_get_modified_date_time (GBookmarkFile *bookmark,
2754 g_return_val_if_fail (bookmark != NULL, NULL);
2755 g_return_val_if_fail (uri != NULL, NULL);
2756 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2758 item = g_bookmark_file_lookup_item (bookmark, uri);
2761 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2762 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2763 _("No bookmark found for URI “%s”"),
2768 return item->modified;
2772 * g_bookmark_file_set_visited:
2773 * @bookmark: a #GBookmarkFile
2775 * @visited: a timestamp or -1 to use the current time
2777 * Sets the time the bookmark for @uri was last visited.
2779 * If no bookmark for @uri is found then it is created.
2781 * The "visited" time should only be set if the bookmark was launched,
2782 * either using the command line retrieved by g_bookmark_file_get_application_info()
2783 * or by the default application for the bookmark's MIME type, retrieved
2784 * using g_bookmark_file_get_mime_type(). Changing the "visited" time
2785 * does not affect the "modified" time.
2788 * Deprecated: 2.66: Use g_bookmark_file_set_visited_date_time() instead, as
2789 * `time_t` is deprecated due to the year 2038 problem.
2792 g_bookmark_file_set_visited (GBookmarkFile *bookmark,
2796 GDateTime *visited_dt = (visited != (time_t) -1) ? g_date_time_new_from_unix_utc (visited) : g_date_time_new_now_utc ();
2797 g_bookmark_file_set_visited_date_time (bookmark, uri, visited_dt);
2798 g_date_time_unref (visited_dt);
2802 * g_bookmark_file_set_visited_date_time:
2803 * @bookmark: a #GBookmarkFile
2805 * @visited: a #GDateTime
2807 * Sets the time the bookmark for @uri was last visited.
2809 * If no bookmark for @uri is found then it is created.
2811 * The "visited" time should only be set if the bookmark was launched,
2812 * either using the command line retrieved by g_bookmark_file_get_application_info()
2813 * or by the default application for the bookmark's MIME type, retrieved
2814 * using g_bookmark_file_get_mime_type(). Changing the "visited" time
2815 * does not affect the "modified" time.
2820 g_bookmark_file_set_visited_date_time (GBookmarkFile *bookmark,
2826 g_return_if_fail (bookmark != NULL);
2827 g_return_if_fail (uri != NULL);
2828 g_return_if_fail (visited != NULL);
2830 item = g_bookmark_file_lookup_item (bookmark, uri);
2833 item = bookmark_item_new (uri);
2834 g_bookmark_file_add_item (bookmark, item, NULL);
2837 g_clear_pointer (&item->visited, g_date_time_unref);
2838 item->visited = g_date_time_ref (visited);
2842 * g_bookmark_file_get_visited:
2843 * @bookmark: a #GBookmarkFile
2845 * @error: return location for a #GError, or %NULL
2847 * Gets the time the bookmark for @uri was last visited.
2849 * In the event the URI cannot be found, -1 is returned and
2850 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2852 * Returns: a timestamp.
2855 * Deprecated: 2.66: Use g_bookmark_file_get_visited_date_time() instead, as
2856 * `time_t` is deprecated due to the year 2038 problem.
2859 g_bookmark_file_get_visited (GBookmarkFile *bookmark,
2863 GDateTime *visited = g_bookmark_file_get_visited_date_time (bookmark, uri, error);
2864 return (visited != NULL) ? g_date_time_to_unix (visited) : (time_t) -1;
2868 * g_bookmark_file_get_visited_date_time:
2869 * @bookmark: a #GBookmarkFile
2871 * @error: return location for a #GError, or %NULL
2873 * Gets the time the bookmark for @uri was last visited.
2875 * In the event the URI cannot be found, %NULL is returned and
2876 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2878 * Returns: (transfer none): a #GDateTime
2883 g_bookmark_file_get_visited_date_time (GBookmarkFile *bookmark,
2889 g_return_val_if_fail (bookmark != NULL, NULL);
2890 g_return_val_if_fail (uri != NULL, NULL);
2891 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2893 item = g_bookmark_file_lookup_item (bookmark, uri);
2896 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2897 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2898 _("No bookmark found for URI “%s”"),
2903 return item->visited;
2907 * g_bookmark_file_has_group:
2908 * @bookmark: a #GBookmarkFile
2910 * @group: the group name to be searched
2911 * @error: return location for a #GError, or %NULL
2913 * Checks whether @group appears in the list of groups to which
2914 * the bookmark for @uri belongs to.
2916 * In the event the URI cannot be found, %FALSE is returned and
2917 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
2919 * Returns: %TRUE if @group was found.
2924 g_bookmark_file_has_group (GBookmarkFile *bookmark,
2932 g_return_val_if_fail (bookmark != NULL, FALSE);
2933 g_return_val_if_fail (uri != NULL, FALSE);
2935 item = g_bookmark_file_lookup_item (bookmark, uri);
2938 g_set_error (error, G_BOOKMARK_FILE_ERROR,
2939 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
2940 _("No bookmark found for URI “%s”"),
2945 if (!item->metadata)
2948 for (l = item->metadata->groups; l != NULL; l = l->next)
2950 if (strcmp (l->data, group) == 0)
2959 * g_bookmark_file_add_group:
2960 * @bookmark: a #GBookmarkFile
2962 * @group: the group name to be added
2964 * Adds @group to the list of groups to which the bookmark for @uri
2967 * If no bookmark for @uri is found then it is created.
2972 g_bookmark_file_add_group (GBookmarkFile *bookmark,
2978 g_return_if_fail (bookmark != NULL);
2979 g_return_if_fail (uri != NULL);
2980 g_return_if_fail (group != NULL && group[0] != '\0');
2982 item = g_bookmark_file_lookup_item (bookmark, uri);
2985 item = bookmark_item_new (uri);
2986 g_bookmark_file_add_item (bookmark, item, NULL);
2989 if (!item->metadata)
2990 item->metadata = bookmark_metadata_new ();
2992 if (!g_bookmark_file_has_group (bookmark, uri, group, NULL))
2994 item->metadata->groups = g_list_prepend (item->metadata->groups,
2997 bookmark_item_touch_modified (item);
3002 * g_bookmark_file_remove_group:
3003 * @bookmark: a #GBookmarkFile
3005 * @group: the group name to be removed
3006 * @error: return location for a #GError, or %NULL
3008 * Removes @group from the list of groups to which the bookmark
3009 * for @uri belongs to.
3011 * In the event the URI cannot be found, %FALSE is returned and
3012 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3013 * In the event no group was defined, %FALSE is returned and
3014 * @error is set to %G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
3016 * Returns: %TRUE if @group was successfully removed.
3021 g_bookmark_file_remove_group (GBookmarkFile *bookmark,
3029 g_return_val_if_fail (bookmark != NULL, FALSE);
3030 g_return_val_if_fail (uri != NULL, FALSE);
3032 item = g_bookmark_file_lookup_item (bookmark, uri);
3035 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3036 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3037 _("No bookmark found for URI “%s”"),
3042 if (!item->metadata)
3044 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3045 G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
3046 _("No groups set in bookmark for URI “%s”"),
3051 for (l = item->metadata->groups; l != NULL; l = l->next)
3053 if (strcmp (l->data, group) == 0)
3055 item->metadata->groups = g_list_remove_link (item->metadata->groups, l);
3059 bookmark_item_touch_modified (item);
3069 * g_bookmark_file_set_groups:
3070 * @bookmark: a #GBookmarkFile
3071 * @uri: an item's URI
3072 * @groups: (nullable) (array length=length) (element-type utf8): an array of
3073 * group names, or %NULL to remove all groups
3074 * @length: number of group name values in @groups
3076 * Sets a list of group names for the item with URI @uri. Each previously
3077 * set group name list is removed.
3079 * If @uri cannot be found then an item for it is created.
3084 g_bookmark_file_set_groups (GBookmarkFile *bookmark,
3086 const gchar **groups,
3092 g_return_if_fail (bookmark != NULL);
3093 g_return_if_fail (uri != NULL);
3094 g_return_if_fail (groups != NULL);
3096 item = g_bookmark_file_lookup_item (bookmark, uri);
3099 item = bookmark_item_new (uri);
3100 g_bookmark_file_add_item (bookmark, item, NULL);
3103 if (!item->metadata)
3104 item->metadata = bookmark_metadata_new ();
3106 g_list_free_full (item->metadata->groups, g_free);
3107 item->metadata->groups = NULL;
3111 for (i = 0; i < length && groups[i] != NULL; i++)
3112 item->metadata->groups = g_list_append (item->metadata->groups,
3113 g_strdup (groups[i]));
3116 bookmark_item_touch_modified (item);
3120 * g_bookmark_file_get_groups:
3121 * @bookmark: a #GBookmarkFile
3123 * @length: (out) (optional): return location for the length of the returned string, or %NULL
3124 * @error: return location for a #GError, or %NULL
3126 * Retrieves the list of group names of the bookmark for @uri.
3128 * In the event the URI cannot be found, %NULL is returned and
3129 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3131 * The returned array is %NULL terminated, so @length may optionally
3134 * Returns: (array length=length) (transfer full): a newly allocated %NULL-terminated array of group names.
3135 * Use g_strfreev() to free it.
3140 g_bookmark_file_get_groups (GBookmarkFile *bookmark,
3150 g_return_val_if_fail (bookmark != NULL, NULL);
3151 g_return_val_if_fail (uri != NULL, NULL);
3153 item = g_bookmark_file_lookup_item (bookmark, uri);
3156 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3157 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3158 _("No bookmark found for URI “%s”"),
3163 if (!item->metadata)
3171 len = g_list_length (item->metadata->groups);
3172 retval = g_new0 (gchar *, len + 1);
3173 for (l = g_list_last (item->metadata->groups), i = 0;
3177 gchar *group_name = (gchar *) l->data;
3179 g_warn_if_fail (group_name != NULL);
3181 retval[i++] = g_strdup (group_name);
3192 * g_bookmark_file_add_application:
3193 * @bookmark: a #GBookmarkFile
3195 * @name: (nullable): the name of the application registering the bookmark
3197 * @exec: (nullable): command line to be used to launch the bookmark or %NULL
3199 * Adds the application with @name and @exec to the list of
3200 * applications that have registered a bookmark for @uri into
3203 * Every bookmark inside a #GBookmarkFile must have at least an
3204 * application registered. Each application must provide a name, a
3205 * command line useful for launching the bookmark, the number of times
3206 * the bookmark has been registered by the application and the last
3207 * time the application registered this bookmark.
3209 * If @name is %NULL, the name of the application will be the
3210 * same returned by g_get_application_name(); if @exec is %NULL, the
3211 * command line will be a composition of the program name as
3212 * returned by g_get_prgname() and the "\%u" modifier, which will be
3213 * expanded to the bookmark's URI.
3215 * This function will automatically take care of updating the
3216 * registrations count and timestamping in case an application
3217 * with the same @name had already registered a bookmark for
3218 * @uri inside @bookmark.
3220 * If no bookmark for @uri is found, one is created.
3225 g_bookmark_file_add_application (GBookmarkFile *bookmark,
3231 gchar *app_name, *app_exec;
3234 g_return_if_fail (bookmark != NULL);
3235 g_return_if_fail (uri != NULL);
3237 item = g_bookmark_file_lookup_item (bookmark, uri);
3240 item = bookmark_item_new (uri);
3241 g_bookmark_file_add_item (bookmark, item, NULL);
3244 if (name && name[0] != '\0')
3245 app_name = g_strdup (name);
3247 app_name = g_strdup (g_get_application_name ());
3249 if (exec && exec[0] != '\0')
3250 app_exec = g_strdup (exec);
3252 app_exec = g_strjoin (" ", g_get_prgname(), "%u", NULL);
3254 stamp = g_date_time_new_now_utc ();
3256 g_bookmark_file_set_application_info (bookmark, uri,
3263 g_date_time_unref (stamp);
3269 * g_bookmark_file_remove_application:
3270 * @bookmark: a #GBookmarkFile
3272 * @name: the name of the application
3273 * @error: return location for a #GError or %NULL
3275 * Removes application registered with @name from the list of applications
3276 * that have registered a bookmark for @uri inside @bookmark.
3278 * In the event the URI cannot be found, %FALSE is returned and
3279 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3280 * In the event that no application with name @app_name has registered
3281 * a bookmark for @uri, %FALSE is returned and error is set to
3282 * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED.
3284 * Returns: %TRUE if the application was successfully removed.
3289 g_bookmark_file_remove_application (GBookmarkFile *bookmark,
3297 g_return_val_if_fail (bookmark != NULL, FALSE);
3298 g_return_val_if_fail (uri != NULL, FALSE);
3299 g_return_val_if_fail (name != NULL, FALSE);
3302 retval = g_bookmark_file_set_application_info (bookmark, uri,
3310 g_propagate_error (error, set_error);
3319 * g_bookmark_file_has_application:
3320 * @bookmark: a #GBookmarkFile
3322 * @name: the name of the application
3323 * @error: return location for a #GError or %NULL
3325 * Checks whether the bookmark for @uri inside @bookmark has been
3326 * registered by application @name.
3328 * In the event the URI cannot be found, %FALSE is returned and
3329 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3331 * Returns: %TRUE if the application @name was found
3336 g_bookmark_file_has_application (GBookmarkFile *bookmark,
3343 g_return_val_if_fail (bookmark != NULL, FALSE);
3344 g_return_val_if_fail (uri != NULL, FALSE);
3345 g_return_val_if_fail (name != NULL, FALSE);
3347 item = g_bookmark_file_lookup_item (bookmark, uri);
3350 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3351 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3352 _("No bookmark found for URI “%s”"),
3357 return (NULL != bookmark_item_lookup_app_info (item, name));
3361 * g_bookmark_file_set_app_info:
3362 * @bookmark: a #GBookmarkFile
3364 * @name: an application's name
3365 * @exec: an application's command line
3366 * @count: the number of registrations done for this application
3367 * @stamp: the time of the last registration for this application
3368 * @error: return location for a #GError or %NULL
3370 * Sets the meta-data of application @name inside the list of
3371 * applications that have registered a bookmark for @uri inside
3374 * You should rarely use this function; use g_bookmark_file_add_application()
3375 * and g_bookmark_file_remove_application() instead.
3377 * @name can be any UTF-8 encoded string used to identify an
3379 * @exec can have one of these two modifiers: "\%f", which will
3380 * be expanded as the local file name retrieved from the bookmark's
3381 * URI; "\%u", which will be expanded as the bookmark's URI.
3382 * The expansion is done automatically when retrieving the stored
3383 * command line using the g_bookmark_file_get_application_info() function.
3384 * @count is the number of times the application has registered the
3385 * bookmark; if is < 0, the current registration count will be increased
3386 * by one, if is 0, the application with @name will be removed from
3387 * the list of registered applications.
3388 * @stamp is the Unix time of the last registration; if it is -1, the
3389 * current time will be used.
3391 * If you try to remove an application by setting its registration count to
3392 * zero, and no bookmark for @uri is found, %FALSE is returned and
3393 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND; similarly,
3394 * in the event that no application @name has registered a bookmark
3395 * for @uri, %FALSE is returned and error is set to
3396 * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. Otherwise, if no bookmark
3397 * for @uri is found, one is created.
3399 * Returns: %TRUE if the application's meta-data was successfully
3403 * Deprecated: 2.66: Use g_bookmark_file_set_application_info() instead, as
3404 * `time_t` is deprecated due to the year 2038 problem.
3407 g_bookmark_file_set_app_info (GBookmarkFile *bookmark,
3415 GDateTime *stamp_dt = (stamp != (time_t) -1) ? g_date_time_new_from_unix_utc (stamp) : g_date_time_new_now_utc ();
3417 retval = g_bookmark_file_set_application_info (bookmark, uri, name, exec, count,
3419 g_date_time_unref (stamp_dt);
3424 * g_bookmark_file_set_application_info:
3425 * @bookmark: a #GBookmarkFile
3427 * @name: an application's name
3428 * @exec: an application's command line
3429 * @count: the number of registrations done for this application
3430 * @stamp: (nullable): the time of the last registration for this application,
3431 * which may be %NULL if @count is 0
3432 * @error: return location for a #GError or %NULL
3434 * Sets the meta-data of application @name inside the list of
3435 * applications that have registered a bookmark for @uri inside
3438 * You should rarely use this function; use g_bookmark_file_add_application()
3439 * and g_bookmark_file_remove_application() instead.
3441 * @name can be any UTF-8 encoded string used to identify an
3443 * @exec can have one of these two modifiers: "\%f", which will
3444 * be expanded as the local file name retrieved from the bookmark's
3445 * URI; "\%u", which will be expanded as the bookmark's URI.
3446 * The expansion is done automatically when retrieving the stored
3447 * command line using the g_bookmark_file_get_application_info() function.
3448 * @count is the number of times the application has registered the
3449 * bookmark; if is < 0, the current registration count will be increased
3450 * by one, if is 0, the application with @name will be removed from
3451 * the list of registered applications.
3452 * @stamp is the Unix time of the last registration.
3454 * If you try to remove an application by setting its registration count to
3455 * zero, and no bookmark for @uri is found, %FALSE is returned and
3456 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND; similarly,
3457 * in the event that no application @name has registered a bookmark
3458 * for @uri, %FALSE is returned and error is set to
3459 * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. Otherwise, if no bookmark
3460 * for @uri is found, one is created.
3462 * Returns: %TRUE if the application's meta-data was successfully
3468 g_bookmark_file_set_application_info (GBookmarkFile *bookmark,
3477 BookmarkAppInfo *ai;
3479 g_return_val_if_fail (bookmark != NULL, FALSE);
3480 g_return_val_if_fail (uri != NULL, FALSE);
3481 g_return_val_if_fail (name != NULL, FALSE);
3482 g_return_val_if_fail (exec != NULL, FALSE);
3483 g_return_val_if_fail (count == 0 || stamp != NULL, FALSE);
3484 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3486 item = g_bookmark_file_lookup_item (bookmark, uri);
3491 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3492 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3493 _("No bookmark found for URI “%s”"),
3499 item = bookmark_item_new (uri);
3500 g_bookmark_file_add_item (bookmark, item, NULL);
3504 if (!item->metadata)
3505 item->metadata = bookmark_metadata_new ();
3507 ai = bookmark_item_lookup_app_info (item, name);
3512 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3513 G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED,
3514 _("No application with name “%s” registered a bookmark for “%s”"),
3521 ai = bookmark_app_info_new (name);
3523 item->metadata->applications = g_list_prepend (item->metadata->applications, ai);
3524 g_hash_table_replace (item->metadata->apps_by_name, ai->name, ai);
3530 item->metadata->applications = g_list_remove (item->metadata->applications, ai);
3531 g_hash_table_remove (item->metadata->apps_by_name, ai->name);
3532 bookmark_app_info_free (ai);
3534 bookmark_item_touch_modified (item);
3543 g_clear_pointer (&ai->stamp, g_date_time_unref);
3544 ai->stamp = g_date_time_ref (stamp);
3546 if (exec && exec[0] != '\0')
3549 ai->exec = g_shell_quote (exec);
3552 bookmark_item_touch_modified (item);
3557 /* expands the application's command line */
3559 expand_exec_line (const gchar *exec_fmt,
3565 exec = g_string_sized_new (512);
3566 while ((ch = *exec_fmt++) != '\0')
3570 exec = g_string_append_c (exec, ch);
3581 g_string_append (exec, uri);
3586 gchar *file = g_filename_from_uri (uri, NULL, NULL);
3589 g_string_append (exec, file);
3594 g_string_free (exec, TRUE);
3601 exec = g_string_append_c (exec, ch);
3607 return g_string_free (exec, FALSE);
3611 * g_bookmark_file_get_app_info:
3612 * @bookmark: a #GBookmarkFile
3614 * @name: an application's name
3615 * @exec: (out) (optional): return location for the command line of the application, or %NULL
3616 * @count: (out) (optional): return location for the registration count, or %NULL
3617 * @stamp: (out) (optional): return location for the last registration time, or %NULL
3618 * @error: return location for a #GError, or %NULL
3620 * Gets the registration information of @app_name for the bookmark for
3621 * @uri. See g_bookmark_file_set_application_info() for more information about
3622 * the returned data.
3624 * The string returned in @app_exec must be freed.
3626 * In the event the URI cannot be found, %FALSE is returned and
3627 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND. In the
3628 * event that no application with name @app_name has registered a bookmark
3629 * for @uri, %FALSE is returned and error is set to
3630 * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. In the event that unquoting
3631 * the command line fails, an error of the %G_SHELL_ERROR domain is
3632 * set and %FALSE is returned.
3634 * Returns: %TRUE on success.
3637 * Deprecated: 2.66: Use g_bookmark_file_get_application_info() instead, as
3638 * `time_t` is deprecated due to the year 2038 problem.
3641 g_bookmark_file_get_app_info (GBookmarkFile *bookmark,
3649 GDateTime *stamp_dt = NULL;
3652 retval = g_bookmark_file_get_application_info (bookmark, uri, name, exec, count, &stamp_dt, error);
3657 *stamp = g_date_time_to_unix (stamp_dt);
3663 * g_bookmark_file_get_application_info:
3664 * @bookmark: a #GBookmarkFile
3666 * @name: an application's name
3667 * @exec: (out) (optional): return location for the command line of the application, or %NULL
3668 * @count: (out) (optional): return location for the registration count, or %NULL
3669 * @stamp: (out) (optional) (transfer none): return location for the last registration time, or %NULL
3670 * @error: return location for a #GError, or %NULL
3672 * Gets the registration information of @app_name for the bookmark for
3673 * @uri. See g_bookmark_file_set_application_info() for more information about
3674 * the returned data.
3676 * The string returned in @app_exec must be freed.
3678 * In the event the URI cannot be found, %FALSE is returned and
3679 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND. In the
3680 * event that no application with name @app_name has registered a bookmark
3681 * for @uri, %FALSE is returned and error is set to
3682 * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. In the event that unquoting
3683 * the command line fails, an error of the %G_SHELL_ERROR domain is
3684 * set and %FALSE is returned.
3686 * Returns: %TRUE on success.
3691 g_bookmark_file_get_application_info (GBookmarkFile *bookmark,
3695 unsigned int *count,
3700 BookmarkAppInfo *ai;
3702 g_return_val_if_fail (bookmark != NULL, FALSE);
3703 g_return_val_if_fail (uri != NULL, FALSE);
3704 g_return_val_if_fail (name != NULL, FALSE);
3705 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3707 item = g_bookmark_file_lookup_item (bookmark, uri);
3710 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3711 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3712 _("No bookmark found for URI “%s”"),
3717 ai = bookmark_item_lookup_app_info (item, name);
3720 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3721 G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED,
3722 _("No application with name “%s” registered a bookmark for “%s”"),
3730 GError *unquote_error = NULL;
3731 gchar *command_line;
3733 command_line = g_shell_unquote (ai->exec, &unquote_error);
3736 g_propagate_error (error, unquote_error);
3740 *exec = expand_exec_line (command_line, uri);
3743 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3744 G_BOOKMARK_FILE_ERROR_INVALID_URI,
3745 _("Failed to expand exec line “%s” with URI “%s”"),
3747 g_free (command_line);
3752 g_free (command_line);
3765 * g_bookmark_file_get_applications:
3766 * @bookmark: a #GBookmarkFile
3768 * @length: (out) (optional): return location of the length of the returned list, or %NULL
3769 * @error: return location for a #GError, or %NULL
3771 * Retrieves the names of the applications that have registered the
3772 * bookmark for @uri.
3774 * In the event the URI cannot be found, %NULL is returned and
3775 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3777 * Returns: (array length=length) (transfer full): a newly allocated %NULL-terminated array of strings.
3778 * Use g_strfreev() to free it.
3783 g_bookmark_file_get_applications (GBookmarkFile *bookmark,
3793 g_return_val_if_fail (bookmark != NULL, NULL);
3794 g_return_val_if_fail (uri != NULL, NULL);
3796 item = g_bookmark_file_lookup_item (bookmark, uri);
3799 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3800 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3801 _("No bookmark found for URI “%s”"),
3806 if (!item->metadata)
3814 n_apps = g_list_length (item->metadata->applications);
3815 apps = g_new0 (gchar *, n_apps + 1);
3817 for (l = g_list_last (item->metadata->applications), i = 0;
3821 BookmarkAppInfo *ai;
3823 ai = (BookmarkAppInfo *) l->data;
3825 g_warn_if_fail (ai != NULL);
3826 g_warn_if_fail (ai->name != NULL);
3828 apps[i++] = g_strdup (ai->name);
3839 * g_bookmark_file_get_size:
3840 * @bookmark: a #GBookmarkFile
3842 * Gets the number of bookmarks inside @bookmark.
3844 * Returns: the number of bookmarks
3849 g_bookmark_file_get_size (GBookmarkFile *bookmark)
3851 g_return_val_if_fail (bookmark != NULL, 0);
3853 return g_list_length (bookmark->items);
3857 * g_bookmark_file_move_item:
3858 * @bookmark: a #GBookmarkFile
3859 * @old_uri: a valid URI
3860 * @new_uri: (nullable): a valid URI, or %NULL
3861 * @error: return location for a #GError or %NULL
3863 * Changes the URI of a bookmark item from @old_uri to @new_uri. Any
3864 * existing bookmark for @new_uri will be overwritten. If @new_uri is
3865 * %NULL, then the bookmark is removed.
3867 * In the event the URI cannot be found, %FALSE is returned and
3868 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3870 * Returns: %TRUE if the URI was successfully changed
3875 g_bookmark_file_move_item (GBookmarkFile *bookmark,
3876 const gchar *old_uri,
3877 const gchar *new_uri,
3882 g_return_val_if_fail (bookmark != NULL, FALSE);
3883 g_return_val_if_fail (old_uri != NULL, FALSE);
3885 item = g_bookmark_file_lookup_item (bookmark, old_uri);
3888 g_set_error (error, G_BOOKMARK_FILE_ERROR,
3889 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
3890 _("No bookmark found for URI “%s”"),
3895 if (new_uri && new_uri[0] != '\0')
3897 if (g_strcmp0 (old_uri, new_uri) == 0)
3900 if (g_bookmark_file_has_item (bookmark, new_uri))
3902 if (!g_bookmark_file_remove_item (bookmark, new_uri, error))
3906 g_hash_table_steal (bookmark->items_by_uri, item->uri);
3909 item->uri = g_strdup (new_uri);
3910 bookmark_item_touch_modified (item);
3912 g_hash_table_replace (bookmark->items_by_uri, item->uri, item);
3918 if (!g_bookmark_file_remove_item (bookmark, old_uri, error))
3926 * g_bookmark_file_set_icon:
3927 * @bookmark: a #GBookmarkFile
3929 * @href: (nullable): the URI of the icon for the bookmark, or %NULL
3930 * @mime_type: the MIME type of the icon for the bookmark
3932 * Sets the icon for the bookmark for @uri. If @href is %NULL, unsets
3933 * the currently set icon. @href can either be a full URL for the icon
3934 * file or the icon name following the Icon Naming specification.
3936 * If no bookmark for @uri is found one is created.
3941 g_bookmark_file_set_icon (GBookmarkFile *bookmark,
3944 const gchar *mime_type)
3948 g_return_if_fail (bookmark != NULL);
3949 g_return_if_fail (uri != NULL);
3951 item = g_bookmark_file_lookup_item (bookmark, uri);
3954 item = bookmark_item_new (uri);
3955 g_bookmark_file_add_item (bookmark, item, NULL);
3958 if (!item->metadata)
3959 item->metadata = bookmark_metadata_new ();
3961 g_free (item->metadata->icon_href);
3962 g_free (item->metadata->icon_mime);
3964 item->metadata->icon_href = g_strdup (href);
3966 if (mime_type && mime_type[0] != '\0')
3967 item->metadata->icon_mime = g_strdup (mime_type);
3969 item->metadata->icon_mime = g_strdup ("application/octet-stream");
3971 bookmark_item_touch_modified (item);
3975 * g_bookmark_file_get_icon:
3976 * @bookmark: a #GBookmarkFile
3978 * @href: (out) (optional): return location for the icon's location or %NULL
3979 * @mime_type: (out) (optional): return location for the icon's MIME type or %NULL
3980 * @error: return location for a #GError or %NULL
3982 * Gets the icon of the bookmark for @uri.
3984 * In the event the URI cannot be found, %FALSE is returned and
3985 * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
3987 * Returns: %TRUE if the icon for the bookmark for the URI was found.
3988 * You should free the returned strings.
3993 g_bookmark_file_get_icon (GBookmarkFile *bookmark,
4001 g_return_val_if_fail (bookmark != NULL, FALSE);
4002 g_return_val_if_fail (uri != NULL, FALSE);
4004 item = g_bookmark_file_lookup_item (bookmark, uri);
4007 g_set_error (error, G_BOOKMARK_FILE_ERROR,
4008 G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
4009 _("No bookmark found for URI “%s”"),
4014 if ((!item->metadata) || (!item->metadata->icon_href))
4018 *href = g_strdup (item->metadata->icon_href);
4021 *mime_type = g_strdup (item->metadata->icon_mime);