LCOV - code coverage report
Current view: top level - glib/glib - gbookmarkfile.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 1156 1261 91.7 %
Date: 2024-04-16 05:15:53 Functions: 84 84 100.0 %
Branches: 527 716 73.6 %

           Branch data     Line data    Source code
       1                 :            : /* gbookmarkfile.c: parsing and building desktop bookmarks
       2                 :            :  *
       3                 :            :  * Copyright (C) 2005-2006 Emmanuele Bassi
       4                 :            :  *
       5                 :            :  * SPDX-License-Identifier: LGPL-2.1-or-later
       6                 :            :  *
       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.
      11                 :            :  *
      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.
      16                 :            :  *
      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/>.
      19                 :            :  */
      20                 :            : 
      21                 :            : #include "config.h"
      22                 :            : 
      23                 :            : #include "gbookmarkfile.h"
      24                 :            : 
      25                 :            : #include <stdio.h>
      26                 :            : #include <stdlib.h>
      27                 :            : #include <string.h>
      28                 :            : #include <errno.h>
      29                 :            : #include <fcntl.h>
      30                 :            : #include <locale.h>
      31                 :            : #include <time.h>
      32                 :            : #include <stdarg.h>
      33                 :            : 
      34                 :            : #include "gconvert.h"
      35                 :            : #include "gdataset.h"
      36                 :            : #include "gdatetime.h"
      37                 :            : #include "gerror.h"
      38                 :            : #include "gfileutils.h"
      39                 :            : #include "ghash.h"
      40                 :            : #include "glibintl.h"
      41                 :            : #include "glist.h"
      42                 :            : #include "gmain.h"
      43                 :            : #include "gmarkup.h"
      44                 :            : #include "gmem.h"
      45                 :            : #include "gmessages.h"
      46                 :            : #include "gshell.h"
      47                 :            : #include "gslice.h"
      48                 :            : #include "gstdio.h"
      49                 :            : #include "gstring.h"
      50                 :            : #include "gstrfuncs.h"
      51                 :            : #include "gtimer.h"
      52                 :            : #include "gutils.h"
      53                 :            : 
      54                 :            : 
      55                 :            : /* XBEL 1.0 standard entities */
      56                 :            : #define XBEL_VERSION            "1.0"
      57                 :            : #define XBEL_DTD_NICK           "xbel"
      58                 :            : #define XBEL_DTD_SYSTEM         "+//IDN python.org//DTD XML Bookmark " \
      59                 :            :                                 "Exchange Language 1.0//EN//XML"
      60                 :            : 
      61                 :            : #define XBEL_DTD_URI            "http://www.python.org/topics/xml/dtds/xbel-1.0.dtd"
      62                 :            : 
      63                 :            : #define XBEL_ROOT_ELEMENT       "xbel"
      64                 :            : #define XBEL_FOLDER_ELEMENT     "folder"      /* unused */
      65                 :            : #define XBEL_BOOKMARK_ELEMENT   "bookmark"
      66                 :            : #define XBEL_ALIAS_ELEMENT      "alias"               /* unused */
      67                 :            : #define XBEL_SEPARATOR_ELEMENT  "separator"   /* unused */
      68                 :            : #define XBEL_TITLE_ELEMENT      "title"
      69                 :            : #define XBEL_DESC_ELEMENT       "desc"
      70                 :            : #define XBEL_INFO_ELEMENT       "info"
      71                 :            : #define XBEL_METADATA_ELEMENT   "metadata"
      72                 :            : 
      73                 :            : #define XBEL_VERSION_ATTRIBUTE  "version"
      74                 :            : #define XBEL_FOLDED_ATTRIBUTE   "folded"      /* unused */
      75                 :            : #define XBEL_OWNER_ATTRIBUTE    "owner"
      76                 :            : #define XBEL_ADDED_ATTRIBUTE    "added"
      77                 :            : #define XBEL_VISITED_ATTRIBUTE  "visited"
      78                 :            : #define XBEL_MODIFIED_ATTRIBUTE "modified"
      79                 :            : #define XBEL_ID_ATTRIBUTE       "id"
      80                 :            : #define XBEL_HREF_ATTRIBUTE     "href"
      81                 :            : #define XBEL_REF_ATTRIBUTE      "ref"                 /* unused */
      82                 :            : 
      83                 :            : #define XBEL_YES_VALUE          "yes"
      84                 :            : #define XBEL_NO_VALUE           "no"
      85                 :            : 
      86                 :            : /* Desktop bookmark spec entities */
      87                 :            : #define BOOKMARK_METADATA_OWNER         "http://freedesktop.org"
      88                 :            : 
      89                 :            : #define BOOKMARK_NAMESPACE_NAME         "bookmark"
      90                 :            : #define BOOKMARK_NAMESPACE_URI          "http://www.freedesktop.org/standards/desktop-bookmarks"
      91                 :            : 
      92                 :            : #define BOOKMARK_GROUPS_ELEMENT         "groups"
      93                 :            : #define BOOKMARK_GROUP_ELEMENT          "group"
      94                 :            : #define BOOKMARK_APPLICATIONS_ELEMENT   "applications"
      95                 :            : #define BOOKMARK_APPLICATION_ELEMENT    "application"
      96                 :            : #define BOOKMARK_ICON_ELEMENT           "icon"
      97                 :            : #define BOOKMARK_PRIVATE_ELEMENT        "private"
      98                 :            : 
      99                 :            : #define BOOKMARK_NAME_ATTRIBUTE         "name"
     100                 :            : #define BOOKMARK_EXEC_ATTRIBUTE         "exec"
     101                 :            : #define BOOKMARK_COUNT_ATTRIBUTE        "count"
     102                 :            : #define BOOKMARK_TIMESTAMP_ATTRIBUTE    "timestamp"     /* deprecated by "modified" */
     103                 :            : #define BOOKMARK_MODIFIED_ATTRIBUTE     "modified"
     104                 :            : #define BOOKMARK_HREF_ATTRIBUTE         "href"
     105                 :            : #define BOOKMARK_TYPE_ATTRIBUTE         "type"
     106                 :            : 
     107                 :            : /* Shared MIME Info entities */
     108                 :            : #define MIME_NAMESPACE_NAME             "mime"
     109                 :            : #define MIME_NAMESPACE_URI              "http://www.freedesktop.org/standards/shared-mime-info"
     110                 :            : #define MIME_TYPE_ELEMENT               "mime-type"
     111                 :            : #define MIME_TYPE_ATTRIBUTE             "type"
     112                 :            : 
     113                 :            : 
     114                 :            : typedef struct _BookmarkAppInfo  BookmarkAppInfo;
     115                 :            : typedef struct _BookmarkMetadata BookmarkMetadata;
     116                 :            : typedef struct _BookmarkItem     BookmarkItem;
     117                 :            : typedef struct _ParseData        ParseData;
     118                 :            : 
     119                 :            : struct _BookmarkAppInfo
     120                 :            : {
     121                 :            :   gchar *name;
     122                 :            :   gchar *exec;
     123                 :            : 
     124                 :            :   guint count;
     125                 :            : 
     126                 :            :   GDateTime *stamp;  /* (owned) */
     127                 :            : };
     128                 :            : 
     129                 :            : struct _BookmarkMetadata
     130                 :            : {
     131                 :            :   gchar *mime_type;
     132                 :            : 
     133                 :            :   GList *groups;
     134                 :            : 
     135                 :            :   GList *applications;
     136                 :            :   GHashTable *apps_by_name;
     137                 :            : 
     138                 :            :   gchar *icon_href;
     139                 :            :   gchar *icon_mime;
     140                 :            : 
     141                 :            :   guint is_private : 1;
     142                 :            : };
     143                 :            : 
     144                 :            : struct _BookmarkItem
     145                 :            : {
     146                 :            :   gchar *uri;
     147                 :            : 
     148                 :            :   gchar *title;
     149                 :            :   gchar *description;
     150                 :            : 
     151                 :            :   GDateTime *added;  /* (owned) */
     152                 :            :   GDateTime *modified;  /* (owned) */
     153                 :            :   GDateTime *visited;  /* (owned) */
     154                 :            : 
     155                 :            :   BookmarkMetadata *metadata;
     156                 :            : };
     157                 :            : 
     158                 :            : struct _GBookmarkFile
     159                 :            : {
     160                 :            :   gchar *title;
     161                 :            :   gchar *description;
     162                 :            : 
     163                 :            :   /* we store our items in a list and keep a copy inside
     164                 :            :    * a hash table for faster lookup performances
     165                 :            :    */
     166                 :            :   GList *items;
     167                 :            :   GHashTable *items_by_uri;
     168                 :            : };
     169                 :            : 
     170                 :            : /* parser state machine */
     171                 :            : typedef enum
     172                 :            : {
     173                 :            :   STATE_STARTED        = 0,
     174                 :            : 
     175                 :            :   STATE_ROOT,
     176                 :            :   STATE_BOOKMARK,
     177                 :            :   STATE_TITLE,
     178                 :            :   STATE_DESC,
     179                 :            :   STATE_INFO,
     180                 :            :   STATE_METADATA,
     181                 :            :   STATE_APPLICATIONS,
     182                 :            :   STATE_APPLICATION,
     183                 :            :   STATE_GROUPS,
     184                 :            :   STATE_GROUP,
     185                 :            :   STATE_MIME,
     186                 :            :   STATE_ICON,
     187                 :            : 
     188                 :            :   STATE_FINISHED
     189                 :            : } ParserState;
     190                 :            : 
     191                 :            : static void          g_bookmark_file_init        (GBookmarkFile  *bookmark);
     192                 :            : static void          g_bookmark_file_clear       (GBookmarkFile  *bookmark);
     193                 :            : static gboolean      g_bookmark_file_parse       (GBookmarkFile  *bookmark,
     194                 :            :                                                   const gchar    *buffer,
     195                 :            :                                                   gsize           length,
     196                 :            :                                                   GError        **error);
     197                 :            : static gchar *       g_bookmark_file_dump        (GBookmarkFile  *bookmark,
     198                 :            :                                                   gsize          *length,
     199                 :            :                                                   GError        **error);
     200                 :            : static BookmarkItem *g_bookmark_file_lookup_item (GBookmarkFile  *bookmark,
     201                 :            :                                                   const gchar    *uri);
     202                 :            : static void          g_bookmark_file_add_item    (GBookmarkFile  *bookmark,
     203                 :            :                                                   BookmarkItem   *item,
     204                 :            :                                                   GError        **error);
     205                 :            : 
     206                 :            : static gboolean timestamp_from_iso8601 (const gchar  *iso_date,
     207                 :            :                                         GDateTime   **out_date_time,
     208                 :            :                                         GError      **error);
     209                 :            : 
     210                 :            : /********************************
     211                 :            :  * BookmarkAppInfo              *
     212                 :            :  *                              *
     213                 :            :  * Application metadata storage *
     214                 :            :  ********************************/
     215                 :            : static BookmarkAppInfo *
     216                 :         66 : bookmark_app_info_new (const gchar *name)
     217                 :            : {
     218                 :            :   BookmarkAppInfo *retval;
     219                 :            : 
     220         [ -  + ]:         66 :   g_warn_if_fail (name != NULL);
     221                 :            : 
     222                 :         66 :   retval = g_slice_new (BookmarkAppInfo);
     223                 :            : 
     224                 :         66 :   retval->name = g_strdup (name);
     225                 :         66 :   retval->exec = NULL;
     226                 :         66 :   retval->count = 0;
     227                 :         66 :   retval->stamp = NULL;
     228                 :            : 
     229                 :         66 :   return retval;
     230                 :            : }
     231                 :            : 
     232                 :            : static void
     233                 :         66 : bookmark_app_info_free (BookmarkAppInfo *app_info)
     234                 :            : {
     235         [ -  + ]:         66 :   if (!app_info)
     236                 :          0 :     return;
     237                 :            : 
     238                 :         66 :   g_free (app_info->name);
     239                 :         66 :   g_free (app_info->exec);
     240                 :         66 :   g_clear_pointer (&app_info->stamp, g_date_time_unref);
     241                 :            : 
     242                 :         66 :   g_slice_free (BookmarkAppInfo, app_info);
     243                 :            : }
     244                 :            : 
     245                 :            : static BookmarkAppInfo *
     246                 :         17 : bookmark_app_info_copy (BookmarkAppInfo *app_info)
     247                 :            : {
     248                 :            :   BookmarkAppInfo *copy;
     249                 :            : 
     250         [ -  + ]:         17 :   if (!app_info)
     251                 :          0 :     return NULL;
     252                 :            : 
     253                 :         17 :   copy = bookmark_app_info_new (app_info->name);
     254                 :         17 :   copy->count = app_info->count;
     255                 :         17 :   copy->exec = g_strdup (app_info->exec);
     256                 :            : 
     257         [ +  + ]:         17 :   if (app_info->stamp)
     258                 :         16 :     copy->stamp = g_date_time_ref (app_info->stamp);
     259                 :            : 
     260                 :         17 :   return copy;
     261                 :            : }
     262                 :            : 
     263                 :            : static gchar *
     264                 :         48 : bookmark_app_info_dump (BookmarkAppInfo *app_info)
     265                 :            : {
     266                 :            :   gchar *retval;
     267                 :            :   gchar *name, *exec, *modified, *count;
     268                 :            : 
     269         [ -  + ]:         48 :   g_warn_if_fail (app_info != NULL);
     270                 :            : 
     271         [ -  + ]:         48 :   if (app_info->count == 0)
     272                 :          0 :     return NULL;
     273                 :            : 
     274                 :         48 :   name = g_markup_escape_text (app_info->name, -1);
     275                 :         48 :   exec = g_markup_escape_text (app_info->exec, -1);
     276                 :         48 :   count = g_strdup_printf ("%u", app_info->count);
     277                 :            : 
     278         [ +  + ]:         48 :   if (app_info->stamp)
     279                 :            :     {
     280                 :            :       char *tmp;
     281                 :            : 
     282                 :         46 :       tmp = g_date_time_format_iso8601 (app_info->stamp);
     283                 :         46 :       modified = g_strconcat (" " BOOKMARK_MODIFIED_ATTRIBUTE "=\"", tmp, "\"",
     284                 :            :                               NULL);
     285                 :         46 :       g_free (tmp);
     286                 :            :     }
     287                 :            :   else
     288                 :            :     {
     289                 :          2 :       modified = g_strdup ("");
     290                 :            :     }
     291                 :            : 
     292                 :         48 :   retval = g_strconcat ("          "
     293                 :            :                         "<" BOOKMARK_NAMESPACE_NAME ":" BOOKMARK_APPLICATION_ELEMENT
     294                 :            :                         " " BOOKMARK_NAME_ATTRIBUTE "=\"", name, "\""
     295                 :            :                         " " BOOKMARK_EXEC_ATTRIBUTE "=\"", exec, "\"",
     296                 :            :                         modified,
     297                 :            :                         " " BOOKMARK_COUNT_ATTRIBUTE "=\"", count, "\"/>\n",
     298                 :            :                         NULL);
     299                 :            : 
     300                 :         48 :   g_free (name);
     301                 :         48 :   g_free (exec);
     302                 :         48 :   g_free (modified);
     303                 :         48 :   g_free (count);
     304                 :            : 
     305                 :         48 :   return retval;
     306                 :            : }
     307                 :            : 
     308                 :            : 
     309                 :            : /***********************
     310                 :            :  * BookmarkMetadata    *
     311                 :            :  *                     *
     312                 :            :  * Metadata storage    *
     313                 :            :  ***********************/
     314                 :            : static BookmarkMetadata *
     315                 :         80 : bookmark_metadata_new (void)
     316                 :            : {
     317                 :            :   BookmarkMetadata *retval;
     318                 :            : 
     319                 :         80 :   retval = g_slice_new (BookmarkMetadata);
     320                 :            : 
     321                 :         80 :   retval->mime_type = NULL;
     322                 :            : 
     323                 :         80 :   retval->groups = NULL;
     324                 :            : 
     325                 :         80 :   retval->applications = NULL;
     326                 :         80 :   retval->apps_by_name = g_hash_table_new_full (g_str_hash,
     327                 :            :                                                 g_str_equal,
     328                 :            :                                                 NULL,
     329                 :            :                                                 NULL);
     330                 :            : 
     331                 :         80 :   retval->is_private = FALSE;
     332                 :            : 
     333                 :         80 :   retval->icon_href = NULL;
     334                 :         80 :   retval->icon_mime = NULL;
     335                 :            : 
     336                 :         80 :   return retval;
     337                 :            : }
     338                 :            : 
     339                 :            : static void
     340                 :         80 : bookmark_metadata_free (BookmarkMetadata *metadata)
     341                 :            : {
     342         [ -  + ]:         80 :   if (!metadata)
     343                 :          0 :     return;
     344                 :            : 
     345                 :         80 :   g_free (metadata->mime_type);
     346                 :            : 
     347                 :         80 :   g_list_free_full (metadata->groups, g_free);
     348                 :         80 :   g_list_free_full (metadata->applications, (GDestroyNotify) bookmark_app_info_free);
     349                 :            : 
     350                 :         80 :   g_hash_table_destroy (metadata->apps_by_name);
     351                 :            : 
     352                 :         80 :   g_free (metadata->icon_href);
     353                 :         80 :   g_free (metadata->icon_mime);
     354                 :            : 
     355                 :         80 :   g_slice_free (BookmarkMetadata, metadata);
     356                 :            : }
     357                 :            : 
     358                 :            : static BookmarkMetadata *
     359                 :         24 : bookmark_metadata_copy (BookmarkMetadata *metadata)
     360                 :            : {
     361                 :            :   BookmarkMetadata *copy;
     362                 :            :   GList *l;
     363                 :            : 
     364         [ +  + ]:         24 :   if (!metadata)
     365                 :          3 :     return NULL;
     366                 :            : 
     367                 :         21 :   copy = bookmark_metadata_new ();
     368                 :         21 :   copy->is_private = metadata->is_private;
     369                 :         21 :   copy->mime_type = g_strdup (metadata->mime_type);
     370                 :         21 :   copy->icon_href = g_strdup (metadata->icon_href);
     371                 :         21 :   copy->icon_mime = g_strdup (metadata->icon_mime);
     372                 :            : 
     373                 :         21 :   copy->groups = g_list_copy_deep (metadata->groups, (GCopyFunc) g_strdup, NULL);
     374                 :         21 :   copy->applications =
     375                 :         21 :     g_list_copy_deep (metadata->applications, (GCopyFunc) bookmark_app_info_copy, NULL);
     376                 :            : 
     377         [ +  + ]:         38 :   for (l = copy->applications; l; l = l->next)
     378                 :            :     {
     379                 :         17 :       BookmarkAppInfo *app_info = l->data;
     380                 :         17 :       g_hash_table_insert (copy->apps_by_name, app_info->name, app_info);
     381                 :            :     }
     382                 :            : 
     383                 :         21 :   g_assert (g_hash_table_size (copy->apps_by_name) ==
     384                 :            :             g_hash_table_size (metadata->apps_by_name));
     385                 :            : 
     386                 :         21 :   return copy;
     387                 :            : }
     388                 :            : 
     389                 :            : static gchar *
     390                 :         48 : bookmark_metadata_dump (BookmarkMetadata *metadata)
     391                 :            : {
     392                 :            :   GString *retval;
     393                 :            :   gchar *buffer;
     394                 :            : 
     395         [ -  + ]:         48 :   if (!metadata->applications)
     396                 :          0 :     return NULL;
     397                 :            : 
     398                 :         48 :   retval = g_string_sized_new (1024);
     399                 :            : 
     400                 :            :   /* metadata container */
     401         [ +  - ]:         48 :   g_string_append (retval,
     402                 :            :                    "      "
     403                 :            :                    "<" XBEL_METADATA_ELEMENT
     404                 :            :                    " " XBEL_OWNER_ATTRIBUTE "=\"" BOOKMARK_METADATA_OWNER
     405                 :            :                    "\">\n");
     406                 :            : 
     407                 :            :   /* mime type */
     408         [ +  + ]:         48 :   if (metadata->mime_type) {
     409                 :         44 :     buffer = g_strconcat ("        "
     410                 :            :                           "<" MIME_NAMESPACE_NAME ":" MIME_TYPE_ELEMENT " "
     411                 :            :                           MIME_TYPE_ATTRIBUTE "=\"", metadata->mime_type, "\"/>\n",
     412                 :            :                           NULL);
     413                 :            :     g_string_append (retval, buffer);
     414                 :         44 :     g_free (buffer);
     415                 :            :   }
     416                 :            : 
     417         [ +  + ]:         48 :   if (metadata->groups)
     418                 :            :     {
     419                 :            :       GList *l;
     420                 :            : 
     421                 :            :       /* open groups container */
     422         [ +  - ]:         11 :       g_string_append (retval,
     423                 :            :                        "        "
     424                 :            :                        "<" BOOKMARK_NAMESPACE_NAME
     425                 :            :                        ":" BOOKMARK_GROUPS_ELEMENT ">\n");
     426                 :            : 
     427         [ +  + ]:         29 :       for (l = g_list_last (metadata->groups); l != NULL; l = l->prev)
     428                 :            :         {
     429                 :            :           gchar *group_name;
     430                 :            : 
     431                 :         18 :           group_name = g_markup_escape_text ((gchar *) l->data, -1);
     432                 :         18 :           buffer = g_strconcat ("          "
     433                 :            :                                 "<" BOOKMARK_NAMESPACE_NAME
     434                 :            :                                 ":" BOOKMARK_GROUP_ELEMENT ">",
     435                 :            :                                 group_name,
     436                 :            :                                 "</" BOOKMARK_NAMESPACE_NAME
     437                 :            :                                 ":"  BOOKMARK_GROUP_ELEMENT ">\n", NULL);
     438                 :            :           g_string_append (retval, buffer);
     439                 :            : 
     440                 :         18 :           g_free (buffer);
     441                 :         18 :           g_free (group_name);
     442                 :            :         }
     443                 :            : 
     444                 :            :       /* close groups container */
     445         [ +  - ]:         22 :       g_string_append (retval,
     446                 :            :                        "        "
     447                 :            :                        "</" BOOKMARK_NAMESPACE_NAME
     448                 :            :                        ":" BOOKMARK_GROUPS_ELEMENT ">\n");
     449                 :            :     }
     450                 :            : 
     451         [ +  - ]:         48 :   if (metadata->applications)
     452                 :            :     {
     453                 :            :       GList *l;
     454                 :            : 
     455                 :            :       /* open applications container */
     456         [ +  - ]:         48 :       g_string_append (retval,
     457                 :            :                        "        "
     458                 :            :                        "<" BOOKMARK_NAMESPACE_NAME
     459                 :            :                        ":" BOOKMARK_APPLICATIONS_ELEMENT ">\n");
     460                 :            : 
     461         [ +  + ]:         96 :       for (l = g_list_last (metadata->applications); l != NULL; l = l->prev)
     462                 :            :         {
     463                 :         48 :           BookmarkAppInfo *app_info = (BookmarkAppInfo *) l->data;
     464                 :            :           gchar *app_data;
     465                 :            : 
     466         [ -  + ]:         48 :           g_warn_if_fail (app_info != NULL);
     467                 :            : 
     468                 :         48 :           app_data = bookmark_app_info_dump (app_info);
     469                 :            : 
     470         [ +  - ]:         48 :           if (app_data)
     471                 :            :             {
     472                 :         48 :               retval = g_string_append (retval, app_data);
     473                 :            : 
     474                 :         48 :               g_free (app_data);
     475                 :            :             }
     476                 :            :         }
     477                 :            : 
     478                 :            :       /* close applications container */
     479         [ +  - ]:         96 :       g_string_append (retval,
     480                 :            :                        "        "
     481                 :            :                        "</" BOOKMARK_NAMESPACE_NAME
     482                 :            :                        ":" BOOKMARK_APPLICATIONS_ELEMENT ">\n");
     483                 :            :     }
     484                 :            : 
     485                 :            :   /* icon */
     486         [ +  + ]:         48 :   if (metadata->icon_href)
     487                 :            :     {
     488         [ -  + ]:         18 :       if (!metadata->icon_mime)
     489                 :          0 :         metadata->icon_mime = g_strdup ("application/octet-stream");
     490                 :            : 
     491                 :         18 :       buffer = g_strconcat ("       "
     492                 :            :                             "<" BOOKMARK_NAMESPACE_NAME
     493                 :            :                             ":" BOOKMARK_ICON_ELEMENT
     494                 :            :                             " " BOOKMARK_HREF_ATTRIBUTE "=\"", metadata->icon_href,
     495                 :            :                             "\" " BOOKMARK_TYPE_ATTRIBUTE "=\"", metadata->icon_mime, "\"/>\n", NULL);
     496                 :            :       g_string_append (retval, buffer);
     497                 :            : 
     498                 :         18 :       g_free (buffer);
     499                 :            :     }
     500                 :            : 
     501                 :            :   /* private hint */
     502         [ +  + ]:         48 :   if (metadata->is_private)
     503         [ +  - ]:         24 :     g_string_append (retval,
     504                 :            :                      "        "
     505                 :            :                      "<" BOOKMARK_NAMESPACE_NAME
     506                 :            :                      ":" BOOKMARK_PRIVATE_ELEMENT "/>\n");
     507                 :            : 
     508                 :            :   /* close metadata container */
     509         [ +  - ]:         48 :   g_string_append (retval,
     510                 :            :                    "      "
     511                 :            :                    "</" XBEL_METADATA_ELEMENT ">\n");
     512                 :            : 
     513                 :         48 :   return g_string_free (retval, FALSE);
     514                 :            : }
     515                 :            : 
     516                 :            : /******************************************************
     517                 :            :  * BookmarkItem                                       *
     518                 :            :  *                                                    *
     519                 :            :  * Storage for a single bookmark item inside the list *
     520                 :            :  ******************************************************/
     521                 :            : static BookmarkItem *
     522                 :         95 : bookmark_item_new (const gchar *uri)
     523                 :            : {
     524                 :            :   BookmarkItem *item;
     525                 :            : 
     526         [ -  + ]:         95 :   g_warn_if_fail (uri != NULL);
     527                 :            : 
     528                 :         95 :   item = g_slice_new (BookmarkItem);
     529                 :         95 :   item->uri = g_strdup (uri);
     530                 :            : 
     531                 :         95 :   item->title = NULL;
     532                 :         95 :   item->description = NULL;
     533                 :            : 
     534                 :         95 :   item->added = NULL;
     535                 :         95 :   item->modified = NULL;
     536                 :         95 :   item->visited = NULL;
     537                 :            : 
     538                 :         95 :   item->metadata = NULL;
     539                 :            : 
     540                 :         95 :   return item;
     541                 :            : }
     542                 :            : 
     543                 :            : static void
     544                 :         95 : bookmark_item_free (BookmarkItem *item)
     545                 :            : {
     546         [ -  + ]:         95 :   if (!item)
     547                 :          0 :     return;
     548                 :            : 
     549                 :         95 :   g_free (item->uri);
     550                 :         95 :   g_free (item->title);
     551                 :         95 :   g_free (item->description);
     552                 :            : 
     553         [ +  + ]:         95 :   if (item->metadata)
     554                 :         80 :     bookmark_metadata_free (item->metadata);
     555                 :            : 
     556                 :         95 :   g_clear_pointer (&item->added, g_date_time_unref);
     557                 :         95 :   g_clear_pointer (&item->modified, g_date_time_unref);
     558                 :         95 :   g_clear_pointer (&item->visited, g_date_time_unref);
     559                 :            : 
     560                 :         95 :   g_slice_free (BookmarkItem, item);
     561                 :            : }
     562                 :            : 
     563                 :            : static BookmarkItem *
     564                 :         24 : bookmark_item_copy (BookmarkItem *item)
     565                 :            : {
     566                 :            :   BookmarkItem* copy;
     567                 :            : 
     568         [ -  + ]:         24 :   if (!item)
     569                 :          0 :     return NULL;
     570                 :            : 
     571                 :         24 :   copy = bookmark_item_new (item->uri);
     572                 :            : 
     573                 :         24 :   copy->title = g_strdup (item->title);
     574                 :         24 :   copy->description = g_strdup (item->description);
     575                 :            : 
     576                 :         24 :   copy->metadata = bookmark_metadata_copy (item->metadata);
     577                 :            : 
     578         [ +  - ]:         24 :   if (item->added)
     579                 :         24 :     copy->added = g_date_time_ref (item->added);
     580         [ +  - ]:         24 :   if (item->modified)
     581                 :         24 :     copy->modified = g_date_time_ref (item->modified);
     582         [ +  - ]:         24 :   if (item->visited)
     583                 :         24 :     copy->visited = g_date_time_ref (item->visited);
     584                 :            : 
     585                 :         24 :   return copy;
     586                 :            : }
     587                 :            : 
     588                 :            : static void
     589                 :        115 : bookmark_item_touch_modified (BookmarkItem *item)
     590                 :            : {
     591                 :        115 :   g_clear_pointer (&item->modified, g_date_time_unref);
     592                 :        115 :   item->modified = g_date_time_new_now_utc ();
     593                 :        115 : }
     594                 :            : 
     595                 :            : static gchar *
     596                 :         62 : bookmark_item_dump (BookmarkItem *item)
     597                 :            : {
     598                 :            :   GString *retval;
     599                 :            :   gchar *escaped_uri;
     600                 :            : 
     601                 :            :   /* at this point, we must have at least a registered application; if we don't
     602                 :            :    * we don't screw up the bookmark file, and just skip this item
     603                 :            :    */
     604   [ +  +  +  + ]:         62 :   if (!item->metadata || !item->metadata->applications)
     605                 :            :     {
     606                 :         14 :       g_warning ("Item for URI '%s' has no registered applications: skipping.", item->uri);
     607                 :         14 :       return NULL;
     608                 :            :     }
     609                 :            : 
     610                 :         48 :   retval = g_string_sized_new (4096);
     611                 :            : 
     612         [ +  - ]:         48 :   g_string_append (retval, "  <" XBEL_BOOKMARK_ELEMENT " ");
     613                 :            : 
     614                 :         48 :   escaped_uri = g_markup_escape_text (item->uri, -1);
     615                 :            : 
     616   [ +  -  -  + ]:         96 :   g_string_append (retval, XBEL_HREF_ATTRIBUTE "=\"");
     617                 :            :   g_string_append (retval, escaped_uri);
     618         [ +  - ]:         48 :   g_string_append (retval , "\" ");
     619                 :            : 
     620                 :         48 :   g_free (escaped_uri);
     621                 :            : 
     622         [ +  - ]:         48 :   if (item->added)
     623                 :            :     {
     624                 :            :       char *added;
     625                 :            : 
     626                 :         48 :       added = g_date_time_format_iso8601 (item->added);
     627   [ +  -  -  + ]:         96 :       g_string_append (retval, XBEL_ADDED_ATTRIBUTE "=\"");
     628                 :            :       g_string_append (retval, added);
     629         [ +  - ]:         48 :       g_string_append (retval, "\" ");
     630                 :         48 :       g_free (added);
     631                 :            :     }
     632                 :            : 
     633         [ +  - ]:         48 :   if (item->modified)
     634                 :            :     {
     635                 :            :       char *modified;
     636                 :            : 
     637                 :         48 :       modified = g_date_time_format_iso8601 (item->modified);
     638   [ +  -  -  + ]:         96 :       g_string_append (retval, XBEL_MODIFIED_ATTRIBUTE "=\"");
     639                 :            :       g_string_append (retval, modified);
     640         [ +  - ]:         48 :       g_string_append (retval, "\" ");
     641                 :         48 :       g_free (modified);
     642                 :            :     }
     643                 :            : 
     644         [ +  - ]:         48 :   if (item->visited)
     645                 :            :     {
     646                 :            :       char *visited;
     647                 :            : 
     648                 :         48 :       visited = g_date_time_format_iso8601 (item->visited);
     649   [ +  -  -  + ]:         96 :       g_string_append (retval, XBEL_VISITED_ATTRIBUTE "=\"");
     650                 :            :       g_string_append (retval, visited);
     651         [ +  - ]:         48 :       g_string_append (retval, "\" ");
     652                 :         48 :       g_free (visited);
     653                 :            :     }
     654                 :            : 
     655         [ +  - ]:         48 :   if (retval->str[retval->len - 1] == ' ')
     656                 :         48 :     g_string_truncate (retval, retval->len - 1);
     657         [ +  - ]:         48 :   g_string_append (retval, ">\n");
     658                 :            : 
     659         [ +  + ]:         48 :   if (item->title)
     660                 :            :     {
     661                 :            :       gchar *escaped_title;
     662                 :            : 
     663                 :         19 :       escaped_title = g_markup_escape_text (item->title, -1);
     664   [ +  -  -  + ]:         38 :       g_string_append (retval, "    " "<" XBEL_TITLE_ELEMENT ">");
     665                 :            :       g_string_append (retval, escaped_title);
     666         [ +  - ]:         19 :       g_string_append (retval, "</" XBEL_TITLE_ELEMENT ">\n");
     667                 :            : 
     668                 :         19 :       g_free (escaped_title);
     669                 :            :     }
     670                 :            : 
     671         [ +  + ]:         48 :   if (item->description)
     672                 :            :     {
     673                 :            :       gchar *escaped_desc;
     674                 :            : 
     675                 :         18 :       escaped_desc = g_markup_escape_text (item->description, -1);
     676   [ +  -  -  + ]:         36 :       g_string_append (retval, "    " "<" XBEL_DESC_ELEMENT ">");
     677                 :            :       g_string_append (retval, escaped_desc);
     678         [ +  - ]:         18 :       g_string_append (retval, "</" XBEL_DESC_ELEMENT ">\n");
     679                 :            : 
     680                 :         18 :       g_free (escaped_desc);
     681                 :            :     }
     682                 :            : 
     683         [ +  - ]:         48 :   if (item->metadata)
     684                 :            :     {
     685                 :            :       gchar *metadata;
     686                 :            : 
     687                 :         48 :       metadata = bookmark_metadata_dump (item->metadata);
     688         [ +  - ]:         48 :       if (metadata)
     689                 :            :         {
     690   [ +  -  -  + ]:         96 :           g_string_append (retval, "    " "<" XBEL_INFO_ELEMENT ">\n");
     691                 :            :           g_string_append (retval, metadata);
     692         [ +  - ]:         48 :           g_string_append (retval, "    " "</" XBEL_INFO_ELEMENT ">\n");
     693                 :            : 
     694                 :         48 :           g_free (metadata);
     695                 :            :         }
     696                 :            :     }
     697                 :            : 
     698         [ +  - ]:         48 :   g_string_append (retval, "  </" XBEL_BOOKMARK_ELEMENT ">\n");
     699                 :            : 
     700                 :         48 :   return g_string_free (retval, FALSE);
     701                 :            : }
     702                 :            : 
     703                 :            : static BookmarkAppInfo *
     704                 :        106 : bookmark_item_lookup_app_info (BookmarkItem *item,
     705                 :            :                                const gchar  *app_name)
     706                 :            : {
     707   [ +  -  +  - ]:        106 :   g_warn_if_fail (item != NULL && app_name != NULL);
     708                 :            : 
     709         [ -  + ]:        106 :   if (!item->metadata)
     710                 :          0 :     return NULL;
     711                 :            : 
     712                 :        106 :   return g_hash_table_lookup (item->metadata->apps_by_name, app_name);
     713                 :            : }
     714                 :            : 
     715                 :            : /*************************
     716                 :            :  *    GBookmarkFile    *
     717                 :            :  *************************/
     718                 :            : 
     719                 :            : static void
     720                 :        148 : g_bookmark_file_init (GBookmarkFile *bookmark)
     721                 :            : {
     722                 :        148 :   bookmark->title = NULL;
     723                 :        148 :   bookmark->description = NULL;
     724                 :            : 
     725                 :        148 :   bookmark->items = NULL;
     726                 :        148 :   bookmark->items_by_uri = g_hash_table_new_full (g_str_hash,
     727                 :            :                                                   g_str_equal,
     728                 :            :                                                   NULL,
     729                 :            :                                                   NULL);
     730                 :        148 : }
     731                 :            : 
     732                 :            : static void
     733                 :        148 : g_bookmark_file_clear (GBookmarkFile *bookmark)
     734                 :            : {
     735                 :        148 :   g_free (bookmark->title);
     736                 :        148 :   g_free (bookmark->description);
     737                 :            : 
     738                 :        148 :   g_list_free_full (bookmark->items, (GDestroyNotify) bookmark_item_free);
     739                 :        148 :   bookmark->items = NULL;
     740                 :            : 
     741                 :        148 :   g_clear_pointer (&bookmark->items_by_uri, g_hash_table_unref);
     742                 :        148 : }
     743                 :            : 
     744                 :            : struct _ParseData
     745                 :            : {
     746                 :            :   ParserState state;
     747                 :            : 
     748                 :            :   GHashTable *namespaces;
     749                 :            : 
     750                 :            :   GBookmarkFile *bookmark_file;
     751                 :            :   BookmarkItem *current_item;
     752                 :            : };
     753                 :            : 
     754                 :            : static ParseData *
     755                 :         96 : parse_data_new (void)
     756                 :            : {
     757                 :            :   ParseData *retval;
     758                 :            : 
     759                 :         96 :   retval = g_new (ParseData, 1);
     760                 :            : 
     761                 :         96 :   retval->state = STATE_STARTED;
     762                 :         96 :   retval->namespaces = g_hash_table_new_full (g_str_hash, g_str_equal,
     763                 :            :                                               (GDestroyNotify) g_free,
     764                 :            :                                               (GDestroyNotify) g_free);
     765                 :         96 :   retval->bookmark_file = NULL;
     766                 :         96 :   retval->current_item = NULL;
     767                 :            : 
     768                 :         96 :   return retval;
     769                 :            : }
     770                 :            : 
     771                 :            : static void
     772                 :         96 : parse_data_free (ParseData *parse_data)
     773                 :            : {
     774                 :         96 :   g_hash_table_destroy (parse_data->namespaces);
     775                 :            : 
     776                 :         96 :   g_free (parse_data);
     777                 :         96 : }
     778                 :            : 
     779                 :            : #define IS_ATTRIBUTE(s,a)       ((0 == strcmp ((s), (a))))
     780                 :            : 
     781                 :            : static void
     782                 :         46 : parse_bookmark_element (GMarkupParseContext  *context,
     783                 :            :                         ParseData            *parse_data,
     784                 :            :                         const gchar         **attribute_names,
     785                 :            :                         const gchar         **attribute_values,
     786                 :            :                         GError              **error)
     787                 :            : {
     788                 :            :   const gchar *uri, *added, *modified, *visited;
     789                 :            :   const gchar *attr;
     790                 :            :   gint i;
     791                 :            :   BookmarkItem *item;
     792                 :            :   GError *add_error;
     793                 :            : 
     794   [ +  -  +  - ]:         46 :   g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_BOOKMARK));
     795                 :            : 
     796                 :         46 :   i = 0;
     797                 :         46 :   uri = added = modified = visited = NULL;
     798         [ +  + ]:        194 :   for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
     799                 :            :     {
     800         [ +  + ]:        150 :       if (IS_ATTRIBUTE (attr, XBEL_HREF_ATTRIBUTE))
     801                 :         44 :         uri = attribute_values[i];
     802         [ +  + ]:        106 :       else if (IS_ATTRIBUTE (attr, XBEL_ADDED_ATTRIBUTE))
     803                 :         36 :         added = attribute_values[i];
     804         [ +  + ]:         70 :       else if (IS_ATTRIBUTE (attr, XBEL_MODIFIED_ATTRIBUTE))
     805                 :         34 :         modified = attribute_values[i];
     806         [ +  + ]:         36 :       else if (IS_ATTRIBUTE (attr, XBEL_VISITED_ATTRIBUTE))
     807                 :         34 :         visited = attribute_values[i];
     808                 :            :       else
     809                 :            :         {
     810                 :            :           /* bookmark is defined by the XBEL spec, so we need
     811                 :            :            * to error out if the element has different or
     812                 :            :            * missing attributes
     813                 :            :            */
     814                 :          2 :           g_set_error (error, G_MARKUP_ERROR,
     815                 :            :                        G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
     816                 :            :                        _("Unexpected attribute “%s” for element “%s”"),
     817                 :            :                        attr,
     818                 :            :                        XBEL_BOOKMARK_ELEMENT);
     819                 :          6 :           return;
     820                 :            :         }
     821                 :            :     }
     822                 :            : 
     823         [ +  + ]:         44 :   if (!uri)
     824                 :            :     {
     825                 :          2 :       g_set_error (error, G_MARKUP_ERROR,
     826                 :            :                    G_MARKUP_ERROR_INVALID_CONTENT,
     827                 :            :                    _("Attribute “%s” of element “%s” not found"),
     828                 :            :                    XBEL_HREF_ATTRIBUTE,
     829                 :            :                    XBEL_BOOKMARK_ELEMENT);
     830                 :          2 :       return;
     831                 :            :     }
     832                 :            : 
     833         [ -  + ]:         42 :   g_warn_if_fail (parse_data->current_item == NULL);
     834                 :            : 
     835                 :         42 :   item = bookmark_item_new (uri);
     836                 :            : 
     837   [ +  +  +  + ]:         42 :   if (added != NULL && !timestamp_from_iso8601 (added, &item->added, error))
     838                 :            :     {
     839                 :          2 :       bookmark_item_free (item);
     840                 :          2 :       return;
     841                 :            :     }
     842                 :            : 
     843   [ +  +  -  + ]:         40 :   if (modified != NULL && !timestamp_from_iso8601 (modified, &item->modified, error))
     844                 :            :     {
     845                 :          0 :       bookmark_item_free (item);
     846                 :          0 :       return;
     847                 :            :     }
     848                 :            : 
     849   [ +  +  -  + ]:         40 :   if (visited != NULL && !timestamp_from_iso8601 (visited, &item->visited, error))
     850                 :            :     {
     851                 :          0 :       bookmark_item_free (item);
     852                 :          0 :       return;
     853                 :            :     }
     854                 :            : 
     855                 :         40 :   add_error = NULL;
     856                 :         40 :   g_bookmark_file_add_item (parse_data->bookmark_file,
     857                 :            :                             item,
     858                 :            :                             &add_error);
     859         [ -  + ]:         40 :   if (add_error)
     860                 :            :     {
     861                 :          0 :       bookmark_item_free (item);
     862                 :            : 
     863                 :          0 :       g_propagate_error (error, add_error);
     864                 :            : 
     865                 :          0 :       return;
     866                 :            :     }
     867                 :            : 
     868                 :         40 :   parse_data->current_item = item;
     869                 :            : }
     870                 :            : 
     871                 :            : static void
     872                 :         32 : parse_application_element (GMarkupParseContext  *context,
     873                 :            :                            ParseData            *parse_data,
     874                 :            :                            const gchar         **attribute_names,
     875                 :            :                            const gchar         **attribute_values,
     876                 :            :                            GError              **error)
     877                 :            : {
     878                 :            :   const gchar *name, *exec, *count, *stamp, *modified;
     879                 :            :   const gchar *attr;
     880                 :            :   gint i;
     881                 :            :   BookmarkItem *item;
     882                 :            :   BookmarkAppInfo *ai;
     883                 :            : 
     884   [ +  -  +  - ]:         32 :   g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_APPLICATION));
     885                 :            : 
     886                 :         32 :   i = 0;
     887                 :         32 :   name = exec = count = stamp = modified = NULL;
     888         [ +  + ]:        140 :   for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
     889                 :            :     {
     890         [ +  + ]:        108 :       if (IS_ATTRIBUTE (attr, BOOKMARK_NAME_ATTRIBUTE))
     891                 :         28 :         name = attribute_values[i];
     892         [ +  + ]:         80 :       else if (IS_ATTRIBUTE (attr, BOOKMARK_EXEC_ATTRIBUTE))
     893                 :         30 :         exec = attribute_values[i];
     894         [ +  + ]:         50 :       else if (IS_ATTRIBUTE (attr, BOOKMARK_COUNT_ATTRIBUTE))
     895                 :         24 :         count = attribute_values[i];
     896         [ +  + ]:         26 :       else if (IS_ATTRIBUTE (attr, BOOKMARK_TIMESTAMP_ATTRIBUTE))
     897                 :         18 :         stamp = attribute_values[i];
     898         [ +  - ]:          8 :       else if (IS_ATTRIBUTE (attr, BOOKMARK_MODIFIED_ATTRIBUTE))
     899                 :          8 :         modified = attribute_values[i];
     900                 :            :     }
     901                 :            : 
     902                 :            :   /* the "name" and "exec" attributes are mandatory */
     903         [ +  + ]:         32 :   if (!name)
     904                 :            :     {
     905                 :          4 :       g_set_error (error, G_MARKUP_ERROR,
     906                 :            :                    G_MARKUP_ERROR_INVALID_CONTENT,
     907                 :            :                    _("Attribute “%s” of element “%s” not found"),
     908                 :            :                    BOOKMARK_NAME_ATTRIBUTE,
     909                 :            :                    BOOKMARK_APPLICATION_ELEMENT);
     910                 :          4 :       return;
     911                 :            :     }
     912                 :            : 
     913         [ -  + ]:         28 :   if (!exec)
     914                 :            :     {
     915                 :          0 :       g_set_error (error, G_MARKUP_ERROR,
     916                 :            :                    G_MARKUP_ERROR_INVALID_CONTENT,
     917                 :            :                    _("Attribute “%s” of element “%s” not found"),
     918                 :            :                    BOOKMARK_EXEC_ATTRIBUTE,
     919                 :            :                    BOOKMARK_APPLICATION_ELEMENT);
     920                 :          0 :       return;
     921                 :            :     }
     922                 :            : 
     923         [ -  + ]:         28 :   g_warn_if_fail (parse_data->current_item != NULL);
     924                 :         28 :   item = parse_data->current_item;
     925                 :            : 
     926                 :         28 :   ai = bookmark_item_lookup_app_info (item, name);
     927         [ +  + ]:         28 :   if (!ai)
     928                 :            :     {
     929                 :         26 :       ai = bookmark_app_info_new (name);
     930                 :            : 
     931         [ -  + ]:         26 :       if (!item->metadata)
     932                 :          0 :         item->metadata = bookmark_metadata_new ();
     933                 :            : 
     934                 :         26 :       item->metadata->applications = g_list_prepend (item->metadata->applications, ai);
     935                 :         26 :       g_hash_table_replace (item->metadata->apps_by_name, ai->name, ai);
     936                 :            :     }
     937                 :            : 
     938                 :         28 :   g_free (ai->exec);
     939                 :         28 :   ai->exec = g_strdup (exec);
     940                 :            : 
     941         [ +  + ]:         28 :   if (count)
     942                 :         22 :     ai->count = atoi (count);
     943                 :            :   else
     944                 :          6 :     ai->count = 1;
     945                 :            : 
     946                 :         28 :   g_clear_pointer (&ai->stamp, g_date_time_unref);
     947         [ +  + ]:         28 :   if (modified != NULL)
     948                 :            :     {
     949         [ -  + ]:          8 :       if (!timestamp_from_iso8601 (modified, &ai->stamp, error))
     950                 :          0 :         return;
     951                 :            :     }
     952                 :            :   else
     953                 :            :     {
     954                 :            :       /* the timestamp attribute has been deprecated but we still parse
     955                 :            :        * it for backward compatibility
     956                 :            :        */
     957         [ +  + ]:         20 :       if (stamp)
     958                 :         16 :         ai->stamp = g_date_time_new_from_unix_utc (atol (stamp));
     959                 :            :       else
     960                 :          4 :         ai->stamp = g_date_time_new_now_utc ();
     961                 :            :     }
     962                 :            : }
     963                 :            : 
     964                 :            : static void
     965                 :         28 : parse_mime_type_element (GMarkupParseContext  *context,
     966                 :            :                          ParseData            *parse_data,
     967                 :            :                          const gchar         **attribute_names,
     968                 :            :                          const gchar         **attribute_values,
     969                 :            :                          GError              **error)
     970                 :            : {
     971                 :            :   const gchar *type;
     972                 :            :   const gchar *attr;
     973                 :            :   gint i;
     974                 :            :   BookmarkItem *item;
     975                 :            : 
     976   [ +  -  +  - ]:         28 :   g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_MIME));
     977                 :            : 
     978                 :         28 :   i = 0;
     979                 :         28 :   type = NULL;
     980         [ +  + ]:         52 :   for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
     981                 :            :     {
     982         [ +  - ]:         24 :       if (IS_ATTRIBUTE (attr, MIME_TYPE_ATTRIBUTE))
     983                 :         24 :         type = attribute_values[i];
     984                 :            :     }
     985                 :            : 
     986         [ +  + ]:         28 :   if (!type)
     987                 :          4 :     type = "application/octet-stream";
     988                 :            : 
     989         [ -  + ]:         28 :   g_warn_if_fail (parse_data->current_item != NULL);
     990                 :         28 :   item = parse_data->current_item;
     991                 :            : 
     992         [ -  + ]:         28 :   if (!item->metadata)
     993                 :          0 :     item->metadata = bookmark_metadata_new ();
     994                 :            : 
     995                 :         28 :   g_free (item->metadata->mime_type);
     996                 :         28 :   item->metadata->mime_type = g_strdup (type);
     997                 :         28 : }
     998                 :            : 
     999                 :            : static void
    1000                 :          4 : parse_icon_element (GMarkupParseContext  *context,
    1001                 :            :                     ParseData            *parse_data,
    1002                 :            :                     const gchar         **attribute_names,
    1003                 :            :                     const gchar         **attribute_values,
    1004                 :            :                     GError              **error)
    1005                 :            : {
    1006                 :            :   const gchar *href;
    1007                 :            :   const gchar *type;
    1008                 :            :   const gchar *attr;
    1009                 :            :   gint i;
    1010                 :            :   BookmarkItem *item;
    1011                 :            : 
    1012   [ +  -  +  - ]:          4 :   g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_ICON));
    1013                 :            : 
    1014                 :          4 :   i = 0;
    1015                 :          4 :   href = NULL;
    1016                 :          4 :   type = NULL;
    1017         [ +  + ]:         10 :   for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
    1018                 :            :     {
    1019         [ +  + ]:          6 :       if (IS_ATTRIBUTE (attr, BOOKMARK_HREF_ATTRIBUTE))
    1020                 :          2 :         href = attribute_values[i];
    1021         [ +  - ]:          4 :       else if (IS_ATTRIBUTE (attr, BOOKMARK_TYPE_ATTRIBUTE))
    1022                 :          4 :         type = attribute_values[i];
    1023                 :            :     }
    1024                 :            : 
    1025                 :            :   /* the "href" attribute is mandatory */
    1026         [ +  + ]:          4 :   if (!href)
    1027                 :            :     {
    1028                 :          2 :       g_set_error (error, G_MARKUP_ERROR,
    1029                 :            :                    G_MARKUP_ERROR_INVALID_CONTENT,
    1030                 :            :                    _("Attribute “%s” of element “%s” not found"),
    1031                 :            :                    BOOKMARK_HREF_ATTRIBUTE,
    1032                 :            :                    BOOKMARK_ICON_ELEMENT);
    1033                 :          2 :       return;
    1034                 :            :     }
    1035                 :            : 
    1036         [ -  + ]:          2 :   if (!type)
    1037                 :          0 :     type = "application/octet-stream";
    1038                 :            : 
    1039         [ -  + ]:          2 :   g_warn_if_fail (parse_data->current_item != NULL);
    1040                 :          2 :   item = parse_data->current_item;
    1041                 :            : 
    1042         [ -  + ]:          2 :   if (!item->metadata)
    1043                 :          0 :     item->metadata = bookmark_metadata_new ();
    1044                 :            : 
    1045                 :          2 :   g_free (item->metadata->icon_href);
    1046                 :          2 :   g_free (item->metadata->icon_mime);
    1047                 :          2 :   item->metadata->icon_href = g_strdup (href);
    1048                 :          2 :   item->metadata->icon_mime = g_strdup (type);
    1049                 :            : }
    1050                 :            : 
    1051                 :            : /* scans through the attributes of an element for the "xmlns" pragma, and
    1052                 :            :  * adds any resulting namespace declaration to a per-parser hashtable, using
    1053                 :            :  * the namespace name as a key for the namespace URI; if no key was found,
    1054                 :            :  * the namespace is considered as default, and stored under the "default" key.
    1055                 :            :  *
    1056                 :            :  * FIXME: this works on the assumption that the generator of the XBEL file
    1057                 :            :  * is either this code or is smart enough to place the namespace declarations
    1058                 :            :  * inside the main root node or inside the metadata node and does not redefine
    1059                 :            :  * a namespace inside an inner node; this does *not* conform to the
    1060                 :            :  * XML-NS standard, although is a close approximation.  In order to make this
    1061                 :            :  * conformant to the XML-NS specification we should use a per-element
    1062                 :            :  * namespace table inside GMarkup and ask it to resolve the namespaces for us.
    1063                 :            :  */
    1064                 :            : static void
    1065                 :        448 : map_namespace_to_name (ParseData    *parse_data,
    1066                 :            :                        const gchar **attribute_names,
    1067                 :            :                        const gchar **attribute_values)
    1068                 :            : {
    1069                 :            :   const gchar *attr;
    1070                 :            :   gint i;
    1071                 :            : 
    1072         [ -  + ]:        448 :   g_warn_if_fail (parse_data != NULL);
    1073                 :            : 
    1074   [ +  -  +  + ]:        448 :   if (!attribute_names || !attribute_names[0])
    1075                 :        226 :     return;
    1076                 :            : 
    1077                 :        222 :   i = 0;
    1078         [ +  + ]:        736 :   for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
    1079                 :            :     {
    1080   [ +  -  -  +  :        514 :       if (g_str_has_prefix (attr, "xmlns"))
             +  +  +  + ]
    1081                 :            :         {
    1082                 :            :           gchar *namespace_name, *namespace_uri;
    1083                 :            :           gchar *p;
    1084                 :            : 
    1085                 :        104 :           p = g_utf8_strchr (attr, -1, ':');
    1086         [ +  - ]:        104 :           if (p)
    1087                 :        104 :             p = g_utf8_next_char (p);
    1088                 :            :           else
    1089                 :          0 :             p = "default";
    1090                 :            : 
    1091                 :        104 :           namespace_name = g_strdup (p);
    1092                 :        104 :           namespace_uri = g_strdup (attribute_values[i]);
    1093                 :            : 
    1094                 :        104 :           g_hash_table_replace (parse_data->namespaces,
    1095                 :            :                                 namespace_name,
    1096                 :            :                                 namespace_uri);
    1097                 :            :         }
    1098                 :            :      }
    1099                 :            : }
    1100                 :            : 
    1101                 :            : /* checks whether @element_full is equal to @element.
    1102                 :            :  *
    1103                 :            :  * if @namespace is set, it tries to resolve the namespace to a known URI,
    1104                 :            :  * and if found is prepended to the element name, from which is separated
    1105                 :            :  * using the character specified in the @sep parameter.
    1106                 :            :  */
    1107                 :            : static gboolean
    1108                 :       2157 : is_element_full (ParseData   *parse_data,
    1109                 :            :                  const gchar *element_full,
    1110                 :            :                  const gchar *namespace,
    1111                 :            :                  const gchar *element,
    1112                 :            :                  const gchar  sep)
    1113                 :            : {
    1114                 :            :   gchar *ns_uri, *ns_name;
    1115                 :            :   const gchar *p, *element_name;
    1116                 :            :   gboolean retval;
    1117                 :            : 
    1118         [ -  + ]:       2157 :   g_warn_if_fail (parse_data != NULL);
    1119         [ -  + ]:       2157 :   g_warn_if_fail (element_full != NULL);
    1120                 :            : 
    1121         [ -  + ]:       2157 :   if (!element)
    1122                 :          0 :     return FALSE;
    1123                 :            : 
    1124                 :            :   /* no namespace requested: dumb element compare */
    1125         [ +  + ]:       2157 :   if (!namespace)
    1126                 :       1553 :     return (0 == strcmp (element_full, element));
    1127                 :            : 
    1128                 :            :   /* search for namespace separator; if none found, assume we are under the
    1129                 :            :    * default namespace, and set ns_name to our "default" marker; if no default
    1130                 :            :    * namespace has been set, just do a plain comparison between @full_element
    1131                 :            :    * and @element.
    1132                 :            :    */
    1133                 :        604 :   p = g_utf8_strchr (element_full, -1, ':');
    1134         [ +  + ]:        604 :   if (p)
    1135                 :            :     {
    1136                 :        510 :       ns_name = g_strndup (element_full, p - element_full);
    1137                 :        510 :       element_name = g_utf8_next_char (p);
    1138                 :            :     }
    1139                 :            :   else
    1140                 :            :     {
    1141                 :         94 :       ns_name = g_strdup ("default");
    1142                 :         94 :       element_name = element_full;
    1143                 :            :     }
    1144                 :            : 
    1145                 :        604 :   ns_uri = g_hash_table_lookup (parse_data->namespaces, ns_name);
    1146         [ +  + ]:        604 :   if (!ns_uri)
    1147                 :            :     {
    1148                 :            :       /* no default namespace found */
    1149                 :         94 :       g_free (ns_name);
    1150                 :            : 
    1151                 :         94 :       return (0 == strcmp (element_full, element));
    1152                 :            :     }
    1153                 :            : 
    1154         [ +  + ]:        798 :   retval = (0 == strcmp (ns_uri, namespace) &&
    1155         [ +  + ]:        288 :             0 == strcmp (element_name, element));
    1156                 :            : 
    1157                 :        510 :   g_free (ns_name);
    1158                 :            : 
    1159                 :        510 :   return retval;
    1160                 :            : }
    1161                 :            : 
    1162                 :            : #define IS_ELEMENT(p,s,e)       (is_element_full ((p), (s), NULL, (e), '\0'))
    1163                 :            : #define IS_ELEMENT_NS(p,s,n,e)  (is_element_full ((p), (s), (n), (e), '|'))
    1164                 :            : 
    1165                 :            : static const gchar *
    1166                 :         22 : parser_state_to_element_name (ParserState state)
    1167                 :            : {
    1168   [ +  -  -  +  :         22 :   switch (state)
          -  -  -  -  -  
             -  -  -  -  
                      - ]
    1169                 :            :     {
    1170                 :          2 :     case STATE_STARTED:
    1171                 :            :     case STATE_FINISHED:
    1172                 :          2 :       return "(top-level)";
    1173                 :          0 :     case STATE_ROOT:
    1174                 :          0 :       return XBEL_ROOT_ELEMENT;
    1175                 :          0 :     case STATE_BOOKMARK:
    1176                 :          0 :       return XBEL_BOOKMARK_ELEMENT;
    1177                 :         20 :     case STATE_TITLE:
    1178                 :         20 :       return XBEL_TITLE_ELEMENT;
    1179                 :          0 :     case STATE_DESC:
    1180                 :          0 :       return XBEL_DESC_ELEMENT;
    1181                 :          0 :     case STATE_INFO:
    1182                 :          0 :       return XBEL_INFO_ELEMENT;
    1183                 :          0 :     case STATE_METADATA:
    1184                 :          0 :       return XBEL_METADATA_ELEMENT;
    1185                 :          0 :     case STATE_APPLICATIONS:
    1186                 :          0 :       return BOOKMARK_APPLICATIONS_ELEMENT;
    1187                 :          0 :     case STATE_APPLICATION:
    1188                 :          0 :       return BOOKMARK_APPLICATION_ELEMENT;
    1189                 :          0 :     case STATE_GROUPS:
    1190                 :          0 :       return BOOKMARK_GROUPS_ELEMENT;
    1191                 :          0 :     case STATE_GROUP:
    1192                 :          0 :       return BOOKMARK_GROUP_ELEMENT;
    1193                 :          0 :     case STATE_MIME:
    1194                 :          0 :       return MIME_TYPE_ELEMENT;
    1195                 :          0 :     case STATE_ICON:
    1196                 :          0 :       return BOOKMARK_ICON_ELEMENT;
    1197                 :          0 :     default:
    1198                 :            :       g_assert_not_reached ();
    1199                 :            :     }
    1200                 :            : }
    1201                 :            : 
    1202                 :            : static void
    1203                 :        448 : start_element_raw_cb (GMarkupParseContext *context,
    1204                 :            :                       const gchar         *element_name,
    1205                 :            :                       const gchar        **attribute_names,
    1206                 :            :                       const gchar        **attribute_values,
    1207                 :            :                       gpointer             user_data,
    1208                 :            :                       GError             **error)
    1209                 :            : {
    1210                 :        448 :   ParseData *parse_data = (ParseData *) user_data;
    1211                 :            : 
    1212                 :            :   /* we must check for namespace declarations first
    1213                 :            :    *
    1214                 :            :    * XXX - we could speed up things by checking for namespace declarations
    1215                 :            :    * only on the root node, where they usually are; this would probably break
    1216                 :            :    * on streams not produced by us or by "smart" generators
    1217                 :            :    */
    1218                 :        448 :   map_namespace_to_name (parse_data, attribute_names, attribute_values);
    1219                 :            : 
    1220   [ +  +  +  +  :        448 :   switch (parse_data->state)
             +  +  +  +  
                      - ]
    1221                 :            :     {
    1222                 :         72 :     case STATE_STARTED:
    1223         [ +  + ]:         72 :       if (IS_ELEMENT (parse_data, element_name, XBEL_ROOT_ELEMENT))
    1224                 :            :         {
    1225                 :            :           const gchar *attr;
    1226                 :            :           gint i;
    1227                 :            : 
    1228                 :         70 :           i = 0;
    1229         [ +  + ]:        246 :           for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
    1230                 :            :             {
    1231         [ +  + ]:        176 :               if ((IS_ATTRIBUTE (attr, XBEL_VERSION_ATTRIBUTE)) &&
    1232         [ +  - ]:         68 :                   (0 == strcmp (attribute_values[i], XBEL_VERSION)))
    1233                 :         68 :                 parse_data->state = STATE_ROOT;
    1234                 :            :             }
    1235                 :            :         }
    1236                 :            :       else
    1237                 :          2 :         g_set_error (error, G_MARKUP_ERROR,
    1238                 :            :                      G_MARKUP_ERROR_INVALID_CONTENT,
    1239                 :            :                      _("Unexpected tag “%s”, tag “%s” expected"),
    1240                 :            :                      element_name, XBEL_ROOT_ELEMENT);
    1241                 :         72 :       break;
    1242                 :        144 :     case STATE_ROOT:
    1243         [ +  + ]:        144 :       if (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT))
    1244                 :         59 :         parse_data->state = STATE_TITLE;
    1245         [ +  + ]:         85 :       else if (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT))
    1246                 :         39 :         parse_data->state = STATE_DESC;
    1247         [ +  - ]:         46 :       else if (IS_ELEMENT (parse_data, element_name, XBEL_BOOKMARK_ELEMENT))
    1248                 :            :         {
    1249                 :         46 :           GError *inner_error = NULL;
    1250                 :            : 
    1251                 :         46 :           parse_data->state = STATE_BOOKMARK;
    1252                 :            : 
    1253                 :         46 :           parse_bookmark_element (context,
    1254                 :            :                                   parse_data,
    1255                 :            :                                   attribute_names,
    1256                 :            :                                   attribute_values,
    1257                 :            :                                   &inner_error);
    1258         [ +  + ]:         46 :           if (inner_error)
    1259                 :          6 :             g_propagate_error (error, inner_error);
    1260                 :            :         }
    1261                 :            :       else
    1262                 :          0 :         g_set_error (error, G_MARKUP_ERROR,
    1263                 :            :                      G_MARKUP_ERROR_INVALID_CONTENT,
    1264                 :            :                      _("Unexpected tag “%s” inside “%s”"),
    1265                 :            :                      element_name,
    1266                 :            :                      XBEL_ROOT_ELEMENT);
    1267                 :        144 :       break;
    1268                 :         45 :     case STATE_BOOKMARK:
    1269         [ +  + ]:         45 :       if (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT))
    1270                 :          5 :         parse_data->state = STATE_TITLE;
    1271         [ +  + ]:         40 :       else if (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT))
    1272                 :          2 :         parse_data->state = STATE_DESC;
    1273         [ +  + ]:         38 :       else if (IS_ELEMENT (parse_data, element_name, XBEL_INFO_ELEMENT))
    1274                 :         36 :         parse_data->state = STATE_INFO;
    1275                 :            :       else
    1276                 :          2 :         g_set_error (error, G_MARKUP_ERROR,
    1277                 :            :                      G_MARKUP_ERROR_INVALID_CONTENT,
    1278                 :            :                      _("Unexpected tag “%s” inside “%s”"),
    1279                 :            :                      element_name,
    1280                 :            :                      XBEL_BOOKMARK_ELEMENT);
    1281                 :         45 :       break;
    1282                 :         40 :     case STATE_INFO:
    1283         [ +  + ]:         40 :       if (IS_ELEMENT (parse_data, element_name, XBEL_METADATA_ELEMENT))
    1284                 :            :         {
    1285                 :            :           const gchar *attr;
    1286                 :            :           gint i;
    1287                 :            : 
    1288                 :         36 :           i = 0;
    1289         [ +  + ]:         70 :           for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
    1290                 :            :             {
    1291         [ +  - ]:         34 :               if ((IS_ATTRIBUTE (attr, XBEL_OWNER_ATTRIBUTE)) &&
    1292         [ +  - ]:         34 :                   (0 == strcmp (attribute_values[i], BOOKMARK_METADATA_OWNER)))
    1293                 :            :                 {
    1294                 :         34 :                   parse_data->state = STATE_METADATA;
    1295                 :            : 
    1296         [ +  - ]:         34 :                   if (!parse_data->current_item->metadata)
    1297                 :         34 :                     parse_data->current_item->metadata = bookmark_metadata_new ();
    1298                 :            :                 }
    1299                 :            :             }
    1300                 :            :         }
    1301                 :            :       else
    1302                 :          4 :         g_set_error (error, G_MARKUP_ERROR,
    1303                 :            :                      G_MARKUP_ERROR_INVALID_CONTENT,
    1304                 :            :                      _("Unexpected tag “%s”, tag “%s” expected"),
    1305                 :            :                      element_name,
    1306                 :            :                      XBEL_METADATA_ELEMENT);
    1307                 :         40 :       break;
    1308                 :         77 :     case STATE_METADATA:
    1309         [ +  + ]:         77 :       if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATIONS_ELEMENT))
    1310                 :         30 :         parse_data->state = STATE_APPLICATIONS;
    1311         [ +  + ]:         47 :       else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUPS_ELEMENT))
    1312                 :         11 :         parse_data->state = STATE_GROUPS;
    1313         [ -  + ]:         36 :       else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_PRIVATE_ELEMENT))
    1314                 :          0 :         parse_data->current_item->metadata->is_private = TRUE;
    1315         [ +  + ]:         36 :       else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_ICON_ELEMENT))
    1316                 :            :         {
    1317                 :          4 :           GError *inner_error = NULL;
    1318                 :            : 
    1319                 :          4 :           parse_data->state = STATE_ICON;
    1320                 :            : 
    1321                 :          4 :           parse_icon_element (context,
    1322                 :            :                               parse_data,
    1323                 :            :                               attribute_names,
    1324                 :            :                               attribute_values,
    1325                 :            :                               &inner_error);
    1326         [ +  + ]:          4 :           if (inner_error)
    1327                 :          2 :             g_propagate_error (error, inner_error);
    1328                 :            :         }
    1329         [ +  + ]:         32 :       else if (IS_ELEMENT_NS (parse_data, element_name, MIME_NAMESPACE_URI, MIME_TYPE_ELEMENT))
    1330                 :            :         {
    1331                 :         28 :           GError *inner_error = NULL;
    1332                 :            : 
    1333                 :         28 :           parse_data->state = STATE_MIME;
    1334                 :            : 
    1335                 :         28 :           parse_mime_type_element (context,
    1336                 :            :                                    parse_data,
    1337                 :            :                                    attribute_names,
    1338                 :            :                                    attribute_values,
    1339                 :            :                                    &inner_error);
    1340         [ -  + ]:         28 :           if (inner_error)
    1341                 :          0 :             g_propagate_error (error, inner_error);
    1342                 :            :         }
    1343                 :            :       else
    1344                 :          4 :         g_set_error (error, G_MARKUP_ERROR,
    1345                 :            :                      G_MARKUP_ERROR_UNKNOWN_ELEMENT,
    1346                 :            :                      _("Unexpected tag “%s” inside “%s”"),
    1347                 :            :                      element_name,
    1348                 :            :                      XBEL_METADATA_ELEMENT);
    1349                 :         77 :       break;
    1350                 :         32 :     case STATE_APPLICATIONS:
    1351         [ +  - ]:         32 :       if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATION_ELEMENT))
    1352                 :            :         {
    1353                 :         32 :           GError *inner_error = NULL;
    1354                 :            : 
    1355                 :         32 :           parse_data->state = STATE_APPLICATION;
    1356                 :            : 
    1357                 :         32 :           parse_application_element (context,
    1358                 :            :                                      parse_data,
    1359                 :            :                                      attribute_names,
    1360                 :            :                                      attribute_values,
    1361                 :            :                                      &inner_error);
    1362         [ +  + ]:         32 :           if (inner_error)
    1363                 :          4 :             g_propagate_error (error, inner_error);
    1364                 :            :         }
    1365                 :            :       else
    1366                 :          0 :         g_set_error (error, G_MARKUP_ERROR,
    1367                 :            :                      G_MARKUP_ERROR_INVALID_CONTENT,
    1368                 :            :                      _("Unexpected tag “%s”, tag “%s” expected"),
    1369                 :            :                      element_name,
    1370                 :            :                      BOOKMARK_APPLICATION_ELEMENT);
    1371                 :         32 :       break;
    1372                 :         16 :     case STATE_GROUPS:
    1373         [ +  + ]:         16 :       if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUP_ELEMENT))
    1374                 :         14 :         parse_data->state = STATE_GROUP;
    1375                 :            :       else
    1376                 :          2 :         g_set_error (error, G_MARKUP_ERROR,
    1377                 :            :                      G_MARKUP_ERROR_INVALID_CONTENT,
    1378                 :            :                      _("Unexpected tag “%s”, tag “%s” expected"),
    1379                 :            :                      element_name,
    1380                 :            :                      BOOKMARK_GROUP_ELEMENT);
    1381                 :         16 :       break;
    1382                 :            : 
    1383                 :         22 :     case STATE_TITLE:
    1384                 :            :     case STATE_DESC:
    1385                 :            :     case STATE_APPLICATION:
    1386                 :            :     case STATE_GROUP:
    1387                 :            :     case STATE_MIME:
    1388                 :            :     case STATE_ICON:
    1389                 :            :     case STATE_FINISHED:
    1390                 :         22 :       g_set_error (error, G_MARKUP_ERROR,
    1391                 :            :                    G_MARKUP_ERROR_INVALID_CONTENT,
    1392                 :            :                    _("Unexpected tag “%s” inside “%s”"),
    1393                 :            :                    element_name,
    1394                 :            :                    parser_state_to_element_name (parse_data->state));
    1395                 :         22 :       break;
    1396                 :            : 
    1397                 :          0 :     default:
    1398                 :            :       g_assert_not_reached ();
    1399                 :            :       break;
    1400                 :            :     }
    1401                 :        448 : }
    1402                 :            : 
    1403                 :            : static void
    1404                 :        216 : end_element_raw_cb (GMarkupParseContext *context,
    1405                 :            :                     const gchar         *element_name,
    1406                 :            :                     gpointer             user_data,
    1407                 :            :                     GError             **error)
    1408                 :            : {
    1409                 :        216 :   ParseData *parse_data = (ParseData *) user_data;
    1410                 :            : 
    1411         [ +  + ]:        216 :   if (IS_ELEMENT (parse_data, element_name, XBEL_ROOT_ELEMENT))
    1412                 :         12 :     parse_data->state = STATE_FINISHED;
    1413         [ +  + ]:        204 :   else if (IS_ELEMENT (parse_data, element_name, XBEL_BOOKMARK_ELEMENT))
    1414                 :            :     {
    1415                 :         10 :       parse_data->current_item = NULL;
    1416                 :            : 
    1417                 :         10 :       parse_data->state = STATE_ROOT;
    1418                 :            :     }
    1419   [ +  +  +  + ]:        378 :   else if ((IS_ELEMENT (parse_data, element_name, XBEL_INFO_ELEMENT)) ||
    1420         [ +  + ]:        326 :            (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT)) ||
    1421                 :        142 :            (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT)))
    1422                 :            :     {
    1423         [ +  + ]:         91 :       if (parse_data->current_item)
    1424                 :         15 :         parse_data->state = STATE_BOOKMARK;
    1425                 :            :       else
    1426                 :         76 :         parse_data->state = STATE_ROOT;
    1427                 :            :     }
    1428         [ +  + ]:        103 :   else if (IS_ELEMENT (parse_data, element_name, XBEL_METADATA_ELEMENT))
    1429                 :         12 :     parse_data->state = STATE_INFO;
    1430         [ +  + ]:         91 :   else if (IS_ELEMENT_NS (parse_data, element_name,
    1431                 :            :                           BOOKMARK_NAMESPACE_URI,
    1432                 :            :                           BOOKMARK_APPLICATION_ELEMENT))
    1433                 :         24 :     parse_data->state = STATE_APPLICATIONS;
    1434         [ +  + ]:         67 :   else if (IS_ELEMENT_NS (parse_data, element_name,
    1435                 :            :                           BOOKMARK_NAMESPACE_URI,
    1436                 :            :                           BOOKMARK_GROUP_ELEMENT))
    1437                 :         12 :     parse_data->state = STATE_GROUPS;
    1438   [ +  +  +  + ]:         88 :   else if ((IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATIONS_ELEMENT)) ||
    1439         [ +  - ]:         61 :            (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUPS_ELEMENT)) ||
    1440         [ +  + ]:         56 :            (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_PRIVATE_ELEMENT)) ||
    1441         [ +  - ]:         54 :            (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_ICON_ELEMENT)) ||
    1442                 :         26 :            (IS_ELEMENT_NS (parse_data, element_name, MIME_NAMESPACE_URI, MIME_TYPE_ELEMENT)))
    1443                 :         55 :     parse_data->state = STATE_METADATA;
    1444                 :        216 : }
    1445                 :            : 
    1446                 :            : static void
    1447                 :        540 : text_raw_cb (GMarkupParseContext *context,
    1448                 :            :              const gchar         *text,
    1449                 :            :              gsize                length,
    1450                 :            :              gpointer             user_data,
    1451                 :            :              GError             **error)
    1452                 :            : {
    1453                 :        540 :   ParseData *parse_data = (ParseData *) user_data;
    1454                 :            :   gchar *payload;
    1455                 :            : 
    1456                 :        540 :   payload = g_strndup (text, length);
    1457                 :            : 
    1458   [ +  +  +  +  :        540 :   switch (parse_data->state)
                      - ]
    1459                 :            :     {
    1460                 :         62 :     case STATE_TITLE:
    1461         [ +  + ]:         62 :       if (parse_data->current_item)
    1462                 :            :         {
    1463                 :          3 :           g_free (parse_data->current_item->title);
    1464                 :          6 :           parse_data->current_item->title = g_strdup (payload);
    1465                 :            :         }
    1466                 :            :       else
    1467                 :            :         {
    1468                 :         59 :           g_free (parse_data->bookmark_file->title);
    1469                 :        118 :           parse_data->bookmark_file->title = g_strdup (payload);
    1470                 :            :         }
    1471                 :         62 :       break;
    1472                 :         39 :     case STATE_DESC:
    1473         [ +  + ]:         39 :       if (parse_data->current_item)
    1474                 :            :         {
    1475                 :          2 :           g_free (parse_data->current_item->description);
    1476                 :          4 :           parse_data->current_item->description = g_strdup (payload);
    1477                 :            :         }
    1478                 :            :       else
    1479                 :            :         {
    1480                 :         37 :           g_free (parse_data->bookmark_file->description);
    1481                 :         74 :           parse_data->bookmark_file->description = g_strdup (payload);
    1482                 :            :         }
    1483                 :         39 :       break;
    1484                 :         14 :     case STATE_GROUP:
    1485                 :            :       {
    1486                 :            :       GList *groups;
    1487                 :            : 
    1488         [ -  + ]:         14 :       g_warn_if_fail (parse_data->current_item != NULL);
    1489                 :            : 
    1490         [ -  + ]:         14 :       if (!parse_data->current_item->metadata)
    1491                 :          0 :         parse_data->current_item->metadata = bookmark_metadata_new ();
    1492                 :            : 
    1493                 :         14 :       groups = parse_data->current_item->metadata->groups;
    1494                 :         14 :       parse_data->current_item->metadata->groups = g_list_prepend (groups, g_strdup (payload));
    1495                 :            :       }
    1496                 :         14 :       break;
    1497                 :        425 :     case STATE_ROOT:
    1498                 :            :     case STATE_BOOKMARK:
    1499                 :            :     case STATE_INFO:
    1500                 :            :     case STATE_METADATA:
    1501                 :            :     case STATE_APPLICATIONS:
    1502                 :            :     case STATE_APPLICATION:
    1503                 :            :     case STATE_GROUPS:
    1504                 :            :     case STATE_MIME:
    1505                 :            :     case STATE_ICON:
    1506                 :        425 :       break;
    1507                 :          0 :     default:
    1508                 :          0 :       g_warn_if_reached ();
    1509                 :          0 :       break;
    1510                 :            :     }
    1511                 :            : 
    1512                 :        540 :   g_free (payload);
    1513                 :        540 : }
    1514                 :            : 
    1515                 :            : static const GMarkupParser markup_parser =
    1516                 :            : {
    1517                 :            :   start_element_raw_cb, /* start_element */
    1518                 :            :   end_element_raw_cb,   /* end_element */
    1519                 :            :   text_raw_cb,          /* text */
    1520                 :            :   NULL,                 /* passthrough */
    1521                 :            :   NULL
    1522                 :            : };
    1523                 :            : 
    1524                 :            : static gboolean
    1525                 :         96 : g_bookmark_file_parse (GBookmarkFile  *bookmark,
    1526                 :            :                          const gchar  *buffer,
    1527                 :            :                          gsize         length,
    1528                 :            :                          GError       **error)
    1529                 :            : {
    1530                 :            :   GMarkupParseContext *context;
    1531                 :            :   ParseData *parse_data;
    1532                 :            :   GError *parse_error, *end_error;
    1533                 :            :   gboolean retval;
    1534                 :            : 
    1535         [ -  + ]:         96 :   g_warn_if_fail (bookmark != NULL);
    1536                 :            : 
    1537         [ -  + ]:         96 :   if (!buffer)
    1538                 :          0 :     return FALSE;
    1539                 :            : 
    1540                 :         96 :   parse_error = NULL;
    1541                 :         96 :   end_error = NULL;
    1542                 :            : 
    1543         [ -  + ]:         96 :   if (length == (gsize) -1)
    1544                 :          0 :     length = strlen (buffer);
    1545                 :            : 
    1546                 :         96 :   parse_data = parse_data_new ();
    1547                 :         96 :   parse_data->bookmark_file = bookmark;
    1548                 :            : 
    1549                 :         96 :   context = g_markup_parse_context_new (&markup_parser,
    1550                 :            :                                         G_MARKUP_DEFAULT_FLAGS,
    1551                 :            :                                         parse_data,
    1552                 :            :                                         (GDestroyNotify) parse_data_free);
    1553                 :            : 
    1554                 :         96 :   retval = g_markup_parse_context_parse (context,
    1555                 :            :                                          buffer,
    1556                 :            :                                          length,
    1557                 :            :                                          &parse_error);
    1558         [ +  + ]:         96 :   if (!retval)
    1559                 :         58 :     g_propagate_error (error, parse_error);
    1560                 :            :   else
    1561                 :            :    {
    1562                 :         38 :      retval = g_markup_parse_context_end_parse (context, &end_error);
    1563         [ +  + ]:         38 :       if (!retval)
    1564                 :         28 :         g_propagate_error (error, end_error);
    1565                 :            :    }
    1566                 :            : 
    1567                 :         96 :   g_markup_parse_context_free (context);
    1568                 :            : 
    1569                 :         96 :   return retval;
    1570                 :            : }
    1571                 :            : 
    1572                 :            : static gchar *
    1573                 :        106 : g_bookmark_file_dump (GBookmarkFile  *bookmark,
    1574                 :            :                       gsize          *length,
    1575                 :            :                       GError        **error)
    1576                 :            : {
    1577                 :            :   GString *retval;
    1578                 :            :   gchar *buffer;
    1579                 :            :   GList *l;
    1580                 :            : 
    1581                 :        106 :   retval = g_string_sized_new (4096);
    1582                 :            : 
    1583         [ +  - ]:        106 :   g_string_append (retval,
    1584                 :            :                    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    1585                 :            : #if 0
    1586                 :            :                    /* XXX - do we really need the doctype? */
    1587                 :            :                    "<!DOCTYPE " XBEL_DTD_NICK "\n"
    1588                 :            :                    "  PUBLIC \"" XBEL_DTD_SYSTEM "\"\n"
    1589                 :            :                    "         \"" XBEL_DTD_URI "\">\n"
    1590                 :            : #endif
    1591                 :            :                    "<" XBEL_ROOT_ELEMENT " " XBEL_VERSION_ATTRIBUTE "=\"" XBEL_VERSION "\"\n"
    1592                 :            :                    "      xmlns:" BOOKMARK_NAMESPACE_NAME "=\"" BOOKMARK_NAMESPACE_URI "\"\n"
    1593                 :            :                    "      xmlns:" MIME_NAMESPACE_NAME     "=\"" MIME_NAMESPACE_URI "\"\n>");
    1594                 :            : 
    1595         [ +  + ]:        106 :   if (bookmark->title)
    1596                 :            :     {
    1597                 :            :       gchar *escaped_title;
    1598                 :            : 
    1599                 :         69 :       escaped_title = g_markup_escape_text (bookmark->title, -1);
    1600                 :            : 
    1601                 :         69 :       buffer = g_strconcat ("  "
    1602                 :            :                             "<" XBEL_TITLE_ELEMENT ">",
    1603                 :            :                             escaped_title,
    1604                 :            :                             "</" XBEL_TITLE_ELEMENT ">\n", NULL);
    1605                 :            : 
    1606                 :            :       g_string_append (retval, buffer);
    1607                 :            : 
    1608                 :         69 :       g_free (buffer);
    1609                 :         69 :       g_free (escaped_title);
    1610                 :            :     }
    1611                 :            : 
    1612         [ +  + ]:        106 :   if (bookmark->description)
    1613                 :            :     {
    1614                 :            :       gchar *escaped_desc;
    1615                 :            : 
    1616                 :         47 :       escaped_desc = g_markup_escape_text (bookmark->description, -1);
    1617                 :            : 
    1618                 :         47 :       buffer = g_strconcat ("  "
    1619                 :            :                             "<" XBEL_DESC_ELEMENT ">",
    1620                 :            :                             escaped_desc,
    1621                 :            :                             "</" XBEL_DESC_ELEMENT ">\n", NULL);
    1622                 :            :       g_string_append (retval, buffer);
    1623                 :            : 
    1624                 :         47 :       g_free (buffer);
    1625                 :         47 :       g_free (escaped_desc);
    1626                 :            :     }
    1627                 :            : 
    1628         [ +  + ]:        106 :   if (!bookmark->items)
    1629                 :         56 :     goto out;
    1630                 :            :   else
    1631         [ +  - ]:         50 :     retval = g_string_append (retval, "\n");
    1632                 :            : 
    1633                 :            :   /* the items are stored in reverse order */
    1634                 :         50 :   for (l = g_list_last (bookmark->items);
    1635         [ +  + ]:        112 :        l != NULL;
    1636                 :         62 :        l = l->prev)
    1637                 :            :     {
    1638                 :         62 :       BookmarkItem *item = (BookmarkItem *) l->data;
    1639                 :            :       gchar *item_dump;
    1640                 :            : 
    1641                 :         62 :       item_dump = bookmark_item_dump (item);
    1642         [ +  + ]:         62 :       if (!item_dump)
    1643                 :         14 :         continue;
    1644                 :            : 
    1645                 :         48 :       retval = g_string_append (retval, item_dump);
    1646                 :            : 
    1647                 :         48 :       g_free (item_dump);
    1648                 :            :     }
    1649                 :            : 
    1650                 :         50 : out:
    1651         [ +  - ]:        106 :   g_string_append (retval, "</" XBEL_ROOT_ELEMENT ">");
    1652                 :            : 
    1653         [ +  + ]:        106 :   if (length)
    1654                 :        103 :     *length = retval->len;
    1655                 :            : 
    1656                 :        106 :   return g_string_free (retval, FALSE);
    1657                 :            : }
    1658                 :            : 
    1659                 :            : /**************
    1660                 :            :  *    Misc    *
    1661                 :            :  **************/
    1662                 :            : 
    1663                 :            : static gboolean
    1664                 :        112 : timestamp_from_iso8601 (const gchar  *iso_date,
    1665                 :            :                         GDateTime   **out_date_time,
    1666                 :            :                         GError      **error)
    1667                 :            : {
    1668                 :        112 :   GDateTime *dt = g_date_time_new_from_iso8601 (iso_date, NULL);
    1669         [ +  + ]:        112 :   if (dt == NULL)
    1670                 :            :     {
    1671                 :          2 :       g_set_error (error, G_BOOKMARK_FILE_ERROR, G_BOOKMARK_FILE_ERROR_READ,
    1672                 :            :                    _("Invalid date/time ‘%s’ in bookmark file"), iso_date);
    1673                 :          2 :       return FALSE;
    1674                 :            :     }
    1675                 :            : 
    1676                 :        110 :   *out_date_time = g_steal_pointer (&dt);
    1677                 :        110 :   return TRUE;
    1678                 :            : }
    1679                 :            : 
    1680         [ +  + ]:        189 : G_DEFINE_QUARK (g-bookmark-file-error-quark, g_bookmark_file_error)
    1681                 :            : 
    1682                 :            : /********************
    1683                 :            :  *    Public API    *
    1684                 :            :  ********************/
    1685                 :            : 
    1686                 :            : /**
    1687                 :            :  * g_bookmark_file_new: (constructor)
    1688                 :            :  *
    1689                 :            :  * Creates a new empty #GBookmarkFile object.
    1690                 :            :  *
    1691                 :            :  * Use g_bookmark_file_load_from_file(), g_bookmark_file_load_from_data()
    1692                 :            :  * or g_bookmark_file_load_from_data_dirs() to read an existing bookmark
    1693                 :            :  * file.
    1694                 :            :  *
    1695                 :            :  * Returns: an empty #GBookmarkFile
    1696                 :            :  *
    1697                 :            :  * Since: 2.12
    1698                 :            :  */
    1699                 :            : GBookmarkFile *
    1700                 :        146 : g_bookmark_file_new (void)
    1701                 :            : {
    1702                 :            :   GBookmarkFile *bookmark;
    1703                 :            : 
    1704                 :        146 :   bookmark = g_new (GBookmarkFile, 1);
    1705                 :            : 
    1706                 :        146 :   g_bookmark_file_init (bookmark);
    1707                 :            : 
    1708                 :        146 :   return bookmark;
    1709                 :            : }
    1710                 :            : 
    1711                 :            : /**
    1712                 :            :  * g_bookmark_file_copy:
    1713                 :            :  * @bookmark: A #GBookmarkFile
    1714                 :            :  *
    1715                 :            :  * Deeply copies a @bookmark #GBookmarkFile object to a new one.
    1716                 :            :  *
    1717                 :            :  * Returns: (transfer full): the copy of @bookmark. Use
    1718                 :            :  *   g_bookmark_free() when finished using it.
    1719                 :            :  *
    1720                 :            :  * Since: 2.76
    1721                 :            :  */
    1722                 :            : GBookmarkFile *
    1723                 :         48 : g_bookmark_file_copy (GBookmarkFile *bookmark)
    1724                 :            : {
    1725                 :            :   GBookmarkFile *copy;
    1726                 :            :   GList *l;
    1727                 :            : 
    1728                 :         48 :   g_return_val_if_fail (bookmark != NULL, NULL);
    1729                 :            : 
    1730                 :         48 :   copy = g_bookmark_file_new ();
    1731                 :         48 :   copy->title = g_strdup (bookmark->title);
    1732                 :         48 :   copy->description = g_strdup (bookmark->description);
    1733                 :         48 :   copy->items = g_list_copy_deep (bookmark->items, (GCopyFunc) bookmark_item_copy, NULL);
    1734                 :            : 
    1735         [ +  + ]:         72 :   for (l = copy->items; l; l = l->next)
    1736                 :            :     {
    1737                 :         24 :       BookmarkItem *item = l->data;
    1738                 :         24 :       g_hash_table_insert (copy->items_by_uri, item->uri, item);
    1739                 :            :     }
    1740                 :            : 
    1741                 :         48 :   g_assert (g_hash_table_size (copy->items_by_uri) ==
    1742                 :            :             g_hash_table_size (bookmark->items_by_uri));
    1743                 :            : 
    1744                 :         48 :   return copy;
    1745                 :            : }
    1746                 :            : 
    1747                 :            : /**
    1748                 :            :  * g_bookmark_file_free:
    1749                 :            :  * @bookmark: a #GBookmarkFile
    1750                 :            :  *
    1751                 :            :  * Frees a #GBookmarkFile.
    1752                 :            :  *
    1753                 :            :  * Since: 2.12
    1754                 :            :  */
    1755                 :            : void
    1756                 :        147 : g_bookmark_file_free (GBookmarkFile *bookmark)
    1757                 :            : {
    1758         [ +  + ]:        147 :   if (!bookmark)
    1759                 :          1 :     return;
    1760                 :            : 
    1761                 :        146 :   g_bookmark_file_clear (bookmark);
    1762                 :            : 
    1763                 :        146 :   g_free (bookmark);
    1764                 :            : }
    1765                 :            : 
    1766                 :            : /**
    1767                 :            :  * g_bookmark_file_load_from_data:
    1768                 :            :  * @bookmark: an empty #GBookmarkFile struct
    1769                 :            :  * @data: (array length=length) (element-type guint8): desktop bookmarks
    1770                 :            :  *    loaded in memory
    1771                 :            :  * @length: the length of @data in bytes
    1772                 :            :  * @error: return location for a #GError, or %NULL
    1773                 :            :  *
    1774                 :            :  * Loads a bookmark file from memory into an empty #GBookmarkFile
    1775                 :            :  * structure.  If the object cannot be created then @error is set to a
    1776                 :            :  * #GBookmarkFileError.
    1777                 :            :  *
    1778                 :            :  * Returns: %TRUE if a desktop bookmark could be loaded.
    1779                 :            :  *
    1780                 :            :  * Since: 2.12
    1781                 :            :  */
    1782                 :            : gboolean
    1783                 :         97 : g_bookmark_file_load_from_data (GBookmarkFile  *bookmark,
    1784                 :            :                                 const gchar    *data,
    1785                 :            :                                 gsize           length,
    1786                 :            :                                 GError        **error)
    1787                 :            : {
    1788                 :            :   GError *parse_error;
    1789                 :            :   gboolean retval;
    1790                 :            : 
    1791                 :         97 :   g_return_val_if_fail (bookmark != NULL, FALSE);
    1792                 :            : 
    1793         [ +  + ]:         96 :   if (length == (gsize) -1)
    1794                 :          1 :     length = strlen (data);
    1795                 :            : 
    1796         [ +  + ]:         96 :   if (bookmark->items)
    1797                 :            :     {
    1798                 :          2 :       g_bookmark_file_clear (bookmark);
    1799                 :          2 :       g_bookmark_file_init (bookmark);
    1800                 :            :     }
    1801                 :            : 
    1802                 :         96 :   parse_error = NULL;
    1803                 :         96 :   retval = g_bookmark_file_parse (bookmark, data, length, &parse_error);
    1804                 :            : 
    1805         [ +  + ]:         96 :   if (!retval)
    1806                 :         86 :     g_propagate_error (error, parse_error);
    1807                 :            : 
    1808                 :         96 :   return retval;
    1809                 :            : }
    1810                 :            : 
    1811                 :            : /**
    1812                 :            :  * g_bookmark_file_load_from_file:
    1813                 :            :  * @bookmark: an empty #GBookmarkFile struct
    1814                 :            :  * @filename: (type filename): the path of a filename to load, in the
    1815                 :            :  *     GLib file name encoding
    1816                 :            :  * @error: return location for a #GError, or %NULL
    1817                 :            :  *
    1818                 :            :  * Loads a desktop bookmark file into an empty #GBookmarkFile structure.
    1819                 :            :  * If the file could not be loaded then @error is set to either a #GFileError
    1820                 :            :  * or #GBookmarkFileError.
    1821                 :            :  *
    1822                 :            :  * Returns: %TRUE if a desktop bookmark file could be loaded
    1823                 :            :  *
    1824                 :            :  * Since: 2.12
    1825                 :            :  */
    1826                 :            : gboolean
    1827                 :         99 : g_bookmark_file_load_from_file (GBookmarkFile  *bookmark,
    1828                 :            :                                 const gchar    *filename,
    1829                 :            :                                 GError        **error)
    1830                 :            : {
    1831                 :         99 :   gboolean ret = FALSE;
    1832                 :         99 :   gchar *buffer = NULL;
    1833                 :            :   gsize len;
    1834                 :            : 
    1835                 :         99 :   g_return_val_if_fail (bookmark != NULL, FALSE);
    1836                 :         98 :   g_return_val_if_fail (filename != NULL, FALSE);
    1837                 :            : 
    1838         [ +  + ]:         97 :   if (!g_file_get_contents (filename, &buffer, &len, error))
    1839                 :          3 :     goto out;
    1840                 :            : 
    1841         [ +  + ]:         94 :   if (!g_bookmark_file_load_from_data (bookmark, buffer, len, error))
    1842                 :         84 :     goto out;
    1843                 :            : 
    1844                 :         10 :   ret = TRUE;
    1845                 :         97 :  out:
    1846                 :         97 :   g_free (buffer);
    1847                 :         97 :   return ret;
    1848                 :            : }
    1849                 :            : 
    1850                 :            : 
    1851                 :            : /* Iterates through all the directories in *dirs trying to
    1852                 :            :  * find file.  When it successfully locates file, returns a
    1853                 :            :  * string its absolute path.  It also leaves the unchecked
    1854                 :            :  * directories in *dirs.  You should free the returned string
    1855                 :            :  *
    1856                 :            :  * Adapted from gkeyfile.c
    1857                 :            :  */
    1858                 :            : static gchar *
    1859                 :          5 : find_file_in_data_dirs (const gchar   *file,
    1860                 :            :                         gchar       ***dirs,
    1861                 :            :                         GError       **error)
    1862                 :            : {
    1863                 :            :   gchar **data_dirs, *data_dir, *path;
    1864                 :            : 
    1865                 :          5 :   path = NULL;
    1866                 :            : 
    1867         [ -  + ]:          5 :   if (dirs == NULL)
    1868                 :          0 :     return NULL;
    1869                 :            : 
    1870                 :          5 :   data_dirs = *dirs;
    1871                 :          5 :   path = NULL;
    1872   [ +  -  +  +  :         14 :   while (data_dirs && (data_dir = *data_dirs) && !path)
                   +  + ]
    1873                 :            :     {
    1874                 :            :       gchar *candidate_file, *sub_dir;
    1875                 :            : 
    1876                 :          9 :       candidate_file = (gchar *) file;
    1877                 :          9 :       sub_dir = g_strdup ("");
    1878   [ +  +  +  + ]:         10 :       while (candidate_file != NULL && !path)
    1879                 :            :         {
    1880                 :            :           gchar *p;
    1881                 :            : 
    1882                 :          3 :           path = g_build_filename (data_dir, sub_dir,
    1883                 :            :                                    candidate_file, NULL);
    1884                 :            : 
    1885                 :          3 :           candidate_file = strchr (candidate_file, '-');
    1886                 :            : 
    1887         [ +  + ]:          3 :           if (candidate_file == NULL)
    1888                 :          2 :             break;
    1889                 :            : 
    1890                 :          1 :           candidate_file++;
    1891                 :            : 
    1892                 :          1 :           g_free (sub_dir);
    1893                 :          1 :           sub_dir = g_strndup (file, candidate_file - file - 1);
    1894                 :            : 
    1895         [ +  + ]:          3 :           for (p = sub_dir; *p != '\0'; p++)
    1896                 :            :             {
    1897         [ -  + ]:          2 :               if (*p == '-')
    1898                 :          0 :                 *p = G_DIR_SEPARATOR;
    1899                 :            :             }
    1900                 :            :         }
    1901                 :          9 :       g_free (sub_dir);
    1902                 :          9 :       data_dirs++;
    1903                 :            :     }
    1904                 :            : 
    1905                 :          5 :   *dirs = data_dirs;
    1906                 :            : 
    1907         [ +  + ]:          5 :   if (!path)
    1908                 :            :     {
    1909                 :          2 :       g_set_error_literal (error, G_BOOKMARK_FILE_ERROR,
    1910                 :            :                            G_BOOKMARK_FILE_ERROR_FILE_NOT_FOUND,
    1911                 :            :                            _("No valid bookmark file found in data dirs"));
    1912                 :            : 
    1913                 :          2 :       return NULL;
    1914                 :            :     }
    1915                 :            : 
    1916                 :          3 :   return path;
    1917                 :            : }
    1918                 :            : 
    1919                 :            : 
    1920                 :            : /**
    1921                 :            :  * g_bookmark_file_load_from_data_dirs:
    1922                 :            :  * @bookmark: a #GBookmarkFile
    1923                 :            :  * @file: (type filename): a relative path to a filename to open and parse
    1924                 :            :  * @full_path: (out) (optional) (type filename): return location for a string
    1925                 :            :  *    containing the full path of the file, or %NULL
    1926                 :            :  * @error: return location for a #GError, or %NULL
    1927                 :            :  *
    1928                 :            :  * This function looks for a desktop bookmark file named @file in the
    1929                 :            :  * paths returned from g_get_user_data_dir() and g_get_system_data_dirs(),
    1930                 :            :  * loads the file into @bookmark and returns the file's full path in
    1931                 :            :  * @full_path.  If the file could not be loaded then @error is
    1932                 :            :  * set to either a #GFileError or #GBookmarkFileError.
    1933                 :            :  *
    1934                 :            :  * Returns: %TRUE if a key file could be loaded, %FALSE otherwise
    1935                 :            :  *
    1936                 :            :  * Since: 2.12
    1937                 :            :  */
    1938                 :            : gboolean
    1939                 :          7 : g_bookmark_file_load_from_data_dirs (GBookmarkFile  *bookmark,
    1940                 :            :                                      const gchar    *file,
    1941                 :            :                                      gchar         **full_path,
    1942                 :            :                                      GError        **error)
    1943                 :            : {
    1944                 :          7 :   GError *file_error = NULL;
    1945                 :            :   gchar **all_data_dirs, **data_dirs;
    1946                 :            :   const gchar *user_data_dir;
    1947                 :            :   const gchar * const * system_data_dirs;
    1948                 :            :   gsize i, j;
    1949                 :            :   gchar *output_path;
    1950                 :            :   gboolean found_file;
    1951                 :            : 
    1952                 :          7 :   g_return_val_if_fail (bookmark != NULL, FALSE);
    1953                 :          5 :   g_return_val_if_fail (!g_path_is_absolute (file), FALSE);
    1954                 :            : 
    1955                 :          5 :   user_data_dir = g_get_user_data_dir ();
    1956                 :          5 :   system_data_dirs = g_get_system_data_dirs ();
    1957                 :          5 :   all_data_dirs = g_new0 (gchar *, g_strv_length ((gchar **)system_data_dirs) + 2);
    1958                 :            : 
    1959                 :          5 :   i = 0;
    1960                 :          5 :   all_data_dirs[i++] = g_strdup (user_data_dir);
    1961                 :            : 
    1962                 :          5 :   j = 0;
    1963         [ +  + ]:         15 :   while (system_data_dirs[j] != NULL)
    1964                 :         20 :     all_data_dirs[i++] = g_strdup (system_data_dirs[j++]);
    1965                 :            : 
    1966                 :          5 :   found_file = FALSE;
    1967                 :          5 :   data_dirs = all_data_dirs;
    1968                 :          5 :   output_path = NULL;
    1969   [ +  -  +  - ]:          5 :   while (*data_dirs != NULL && !found_file)
    1970                 :            :     {
    1971                 :          5 :       g_free (output_path);
    1972                 :            : 
    1973                 :          5 :       output_path = find_file_in_data_dirs (file, &data_dirs, &file_error);
    1974                 :            : 
    1975         [ +  + ]:          5 :       if (file_error)
    1976                 :            :         {
    1977                 :          2 :           g_propagate_error (error, file_error);
    1978                 :          2 :           break;
    1979                 :            :         }
    1980                 :            : 
    1981                 :          3 :       found_file = g_bookmark_file_load_from_file (bookmark,
    1982                 :            :                                                    output_path,
    1983                 :            :                                                    &file_error);
    1984         [ +  - ]:          3 :       if (file_error)
    1985                 :            :         {
    1986                 :          3 :           g_propagate_error (error, file_error);
    1987                 :          3 :           break;
    1988                 :            :         }
    1989                 :            :     }
    1990                 :            : 
    1991   [ -  +  -  - ]:          5 :   if (found_file && full_path)
    1992                 :          0 :     *full_path = output_path;
    1993                 :            :   else
    1994                 :          5 :     g_free (output_path);
    1995                 :            : 
    1996                 :          5 :   g_strfreev (all_data_dirs);
    1997                 :            : 
    1998                 :          5 :   return found_file;
    1999                 :            : }
    2000                 :            : 
    2001                 :            : 
    2002                 :            : /**
    2003                 :            :  * g_bookmark_file_to_data:
    2004                 :            :  * @bookmark: a #GBookmarkFile
    2005                 :            :  * @length: (out) (optional): return location for the length of the returned string, or %NULL
    2006                 :            :  * @error: return location for a #GError, or %NULL
    2007                 :            :  *
    2008                 :            :  * This function outputs @bookmark as a string.
    2009                 :            :  *
    2010                 :            :  * Returns: (transfer full) (array length=length) (element-type guint8):
    2011                 :            :  *   a newly allocated string holding the contents of the #GBookmarkFile
    2012                 :            :  *
    2013                 :            :  * Since: 2.12
    2014                 :            :  */
    2015                 :            : gchar *
    2016                 :        107 : g_bookmark_file_to_data (GBookmarkFile  *bookmark,
    2017                 :            :                          gsize          *length,
    2018                 :            :                          GError        **error)
    2019                 :            : {
    2020                 :        107 :   GError *write_error = NULL;
    2021                 :            :   gchar *retval;
    2022                 :            : 
    2023                 :        107 :   g_return_val_if_fail (bookmark != NULL, NULL);
    2024                 :            : 
    2025                 :        106 :   retval = g_bookmark_file_dump (bookmark, length, &write_error);
    2026         [ -  + ]:        106 :   if (write_error)
    2027                 :            :     {
    2028                 :          0 :       g_propagate_error (error, write_error);
    2029                 :            : 
    2030                 :          0 :       return NULL;
    2031                 :            :     }
    2032                 :            : 
    2033                 :        106 :   return retval;
    2034                 :            : }
    2035                 :            : 
    2036                 :            : /**
    2037                 :            :  * g_bookmark_file_to_file:
    2038                 :            :  * @bookmark: a #GBookmarkFile
    2039                 :            :  * @filename: (type filename): path of the output file
    2040                 :            :  * @error: return location for a #GError, or %NULL
    2041                 :            :  *
    2042                 :            :  * This function outputs @bookmark into a file.  The write process is
    2043                 :            :  * guaranteed to be atomic by using g_file_set_contents() internally.
    2044                 :            :  *
    2045                 :            :  * Returns: %TRUE if the file was successfully written.
    2046                 :            :  *
    2047                 :            :  * Since: 2.12
    2048                 :            :  */
    2049                 :            : gboolean
    2050                 :          7 : g_bookmark_file_to_file (GBookmarkFile  *bookmark,
    2051                 :            :                          const gchar    *filename,
    2052                 :            :                          GError        **error)
    2053                 :            : {
    2054                 :            :   gchar *data;
    2055                 :            :   GError *data_error, *write_error;
    2056                 :            :   gsize len;
    2057                 :            :   gboolean retval;
    2058                 :            : 
    2059                 :          7 :   g_return_val_if_fail (bookmark != NULL, FALSE);
    2060                 :          5 :   g_return_val_if_fail (filename != NULL, FALSE);
    2061                 :            : 
    2062                 :          3 :   data_error = NULL;
    2063                 :          3 :   data = g_bookmark_file_to_data (bookmark, &len, &data_error);
    2064         [ -  + ]:          3 :   if (data_error)
    2065                 :            :     {
    2066                 :          0 :       g_propagate_error (error, data_error);
    2067                 :            : 
    2068                 :          0 :       return FALSE;
    2069                 :            :     }
    2070                 :            : 
    2071                 :          3 :   write_error = NULL;
    2072                 :          3 :   g_file_set_contents (filename, data, len, &write_error);
    2073         [ -  + ]:          3 :   if (write_error)
    2074                 :            :     {
    2075                 :          0 :       g_propagate_error (error, write_error);
    2076                 :            : 
    2077                 :          0 :       retval = FALSE;
    2078                 :            :     }
    2079                 :            :   else
    2080                 :          3 :     retval = TRUE;
    2081                 :            : 
    2082                 :          3 :   g_free (data);
    2083                 :            : 
    2084                 :          3 :   return retval;
    2085                 :            : }
    2086                 :            : 
    2087                 :            : static BookmarkItem *
    2088                 :        465 : g_bookmark_file_lookup_item (GBookmarkFile *bookmark,
    2089                 :            :                              const gchar   *uri)
    2090                 :            : {
    2091   [ +  -  +  - ]:        465 :   g_warn_if_fail (bookmark != NULL && uri != NULL);
    2092                 :            : 
    2093                 :        465 :   return g_hash_table_lookup (bookmark->items_by_uri, uri);
    2094                 :            : }
    2095                 :            : 
    2096                 :            : /* this function adds a new item to the list */
    2097                 :            : static void
    2098                 :         69 : g_bookmark_file_add_item (GBookmarkFile  *bookmark,
    2099                 :            :                           BookmarkItem   *item,
    2100                 :            :                           GError        **error)
    2101                 :            : {
    2102         [ -  + ]:         69 :   g_warn_if_fail (bookmark != NULL);
    2103         [ -  + ]:         69 :   g_warn_if_fail (item != NULL);
    2104                 :            : 
    2105                 :            :   /* this should never happen; and if it does, then we are
    2106                 :            :    * screwing up something big time.
    2107                 :            :    */
    2108         [ -  + ]:         69 :   if (G_UNLIKELY (g_bookmark_file_has_item (bookmark, item->uri)))
    2109                 :            :     {
    2110                 :          0 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    2111                 :            :                    G_BOOKMARK_FILE_ERROR_INVALID_URI,
    2112                 :            :                    _("A bookmark for URI “%s” already exists"),
    2113                 :            :                    item->uri);
    2114                 :          0 :       return;
    2115                 :            :     }
    2116                 :            : 
    2117                 :         69 :   bookmark->items = g_list_prepend (bookmark->items, item);
    2118                 :            : 
    2119                 :         69 :   g_hash_table_replace (bookmark->items_by_uri,
    2120                 :         69 :                         item->uri,
    2121                 :            :                         item);
    2122                 :            : 
    2123         [ +  + ]:         69 :   if (item->added == NULL)
    2124                 :         35 :     item->added = g_date_time_new_now_utc ();
    2125                 :            : 
    2126         [ +  + ]:         69 :   if (item->modified == NULL)
    2127                 :         35 :     item->modified = g_date_time_new_now_utc ();
    2128                 :            : 
    2129         [ +  + ]:         69 :   if (item->visited == NULL)
    2130                 :         35 :     item->visited = g_date_time_new_now_utc ();
    2131                 :            : }
    2132                 :            : 
    2133                 :            : /**
    2134                 :            :  * g_bookmark_file_remove_item:
    2135                 :            :  * @bookmark: a #GBookmarkFile
    2136                 :            :  * @uri: a valid URI
    2137                 :            :  * @error: return location for a #GError, or %NULL
    2138                 :            :  *
    2139                 :            :  * Removes the bookmark for @uri from the bookmark file @bookmark.
    2140                 :            :  *
    2141                 :            :  * Returns: %TRUE if the bookmark was removed successfully.
    2142                 :            :  *
    2143                 :            :  * Since: 2.12
    2144                 :            :  */
    2145                 :            : gboolean
    2146                 :         22 : g_bookmark_file_remove_item (GBookmarkFile  *bookmark,
    2147                 :            :                              const gchar    *uri,
    2148                 :            :                              GError        **error)
    2149                 :            : {
    2150                 :            :   BookmarkItem *item;
    2151                 :            : 
    2152                 :         22 :   g_return_val_if_fail (bookmark != NULL, FALSE);
    2153                 :         21 :   g_return_val_if_fail (uri != NULL, FALSE);
    2154                 :            : 
    2155                 :         20 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    2156                 :            : 
    2157         [ +  + ]:         20 :   if (!item)
    2158                 :            :     {
    2159                 :          9 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    2160                 :            :                    G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
    2161                 :            :                    _("No bookmark found for URI “%s”"),
    2162                 :            :                    uri);
    2163                 :          9 :       return FALSE;
    2164                 :            :     }
    2165                 :            : 
    2166                 :         11 :   bookmark->items = g_list_remove (bookmark->items, item);
    2167                 :         11 :   g_hash_table_remove (bookmark->items_by_uri, item->uri);
    2168                 :            : 
    2169                 :         11 :   bookmark_item_free (item);
    2170                 :            : 
    2171                 :         11 :   return TRUE;
    2172                 :            : }
    2173                 :            : 
    2174                 :            : /**
    2175                 :            :  * g_bookmark_file_has_item:
    2176                 :            :  * @bookmark: a #GBookmarkFile
    2177                 :            :  * @uri: a valid URI
    2178                 :            :  *
    2179                 :            :  * Looks whether the desktop bookmark has an item with its URI set to @uri.
    2180                 :            :  *
    2181                 :            :  * Returns: %TRUE if @uri is inside @bookmark, %FALSE otherwise
    2182                 :            :  *
    2183                 :            :  * Since: 2.12
    2184                 :            :  */
    2185                 :            : gboolean
    2186                 :         78 : g_bookmark_file_has_item (GBookmarkFile *bookmark,
    2187                 :            :                           const gchar   *uri)
    2188                 :            : {
    2189                 :         78 :   g_return_val_if_fail (bookmark != NULL, FALSE);
    2190                 :         77 :   g_return_val_if_fail (uri != NULL, FALSE);
    2191                 :            : 
    2192                 :         76 :   return (NULL != g_hash_table_lookup (bookmark->items_by_uri, uri));
    2193                 :            : }
    2194                 :            : 
    2195                 :            : /**
    2196                 :            :  * g_bookmark_file_get_uris:
    2197                 :            :  * @bookmark: a #GBookmarkFile
    2198                 :            :  * @length: (out) (optional): return location for the number of returned URIs, or %NULL
    2199                 :            :  *
    2200                 :            :  * Returns all URIs of the bookmarks in the bookmark file @bookmark.
    2201                 :            :  * The array of returned URIs will be %NULL-terminated, so @length may
    2202                 :            :  * optionally be %NULL.
    2203                 :            :  *
    2204                 :            :  * Returns: (array length=length) (transfer full): a newly allocated %NULL-terminated array of strings.
    2205                 :            :  *   Use g_strfreev() to free it.
    2206                 :            :  *
    2207                 :            :  * Since: 2.12
    2208                 :            :  */
    2209                 :            : gchar **
    2210                 :          5 : g_bookmark_file_get_uris (GBookmarkFile *bookmark,
    2211                 :            :                           gsize         *length)
    2212                 :            : {
    2213                 :            :   GList *l;
    2214                 :            :   gchar **uris;
    2215                 :            :   gsize i, n_items;
    2216                 :            : 
    2217                 :          5 :   g_return_val_if_fail (bookmark != NULL, NULL);
    2218                 :            : 
    2219                 :          4 :   n_items = g_list_length (bookmark->items);
    2220                 :          4 :   uris = g_new0 (gchar *, n_items + 1);
    2221                 :            : 
    2222                 :            :   /* the items are stored in reverse order, so we walk the list backward */
    2223         [ +  + ]:          7 :   for (l = g_list_last (bookmark->items), i = 0; l != NULL; l = l->prev)
    2224                 :            :     {
    2225                 :          3 :       BookmarkItem *item = (BookmarkItem *) l->data;
    2226                 :            : 
    2227         [ -  + ]:          3 :       g_warn_if_fail (item != NULL);
    2228                 :            : 
    2229                 :          6 :       uris[i++] = g_strdup (item->uri);
    2230                 :            :     }
    2231                 :          4 :   uris[i] = NULL;
    2232                 :            : 
    2233         [ +  - ]:          4 :   if (length)
    2234                 :          4 :     *length = i;
    2235                 :            : 
    2236                 :          4 :   return uris;
    2237                 :            : }
    2238                 :            : 
    2239                 :            : /**
    2240                 :            :  * g_bookmark_file_set_title:
    2241                 :            :  * @bookmark: a #GBookmarkFile
    2242                 :            :  * @uri: (nullable): a valid URI or %NULL
    2243                 :            :  * @title: a UTF-8 encoded string
    2244                 :            :  *
    2245                 :            :  * Sets @title as the title of the bookmark for @uri inside the
    2246                 :            :  * bookmark file @bookmark.
    2247                 :            :  *
    2248                 :            :  * If @uri is %NULL, the title of @bookmark is set.
    2249                 :            :  *
    2250                 :            :  * If a bookmark for @uri cannot be found then it is created.
    2251                 :            :  *
    2252                 :            :  * Since: 2.12
    2253                 :            :  */
    2254                 :            : void
    2255                 :         20 : g_bookmark_file_set_title (GBookmarkFile *bookmark,
    2256                 :            :                            const gchar   *uri,
    2257                 :            :                            const gchar   *title)
    2258                 :            : {
    2259                 :         20 :   g_return_if_fail (bookmark != NULL);
    2260                 :            : 
    2261         [ +  + ]:         19 :   if (!uri)
    2262                 :            :     {
    2263                 :          9 :       g_free (bookmark->title);
    2264                 :          9 :       bookmark->title = g_strdup (title);
    2265                 :            :     }
    2266                 :            :   else
    2267                 :            :     {
    2268                 :            :       BookmarkItem *item;
    2269                 :            : 
    2270                 :         10 :       item = g_bookmark_file_lookup_item (bookmark, uri);
    2271         [ +  - ]:         10 :       if (!item)
    2272                 :            :         {
    2273                 :         10 :           item = bookmark_item_new (uri);
    2274                 :         10 :           g_bookmark_file_add_item (bookmark, item, NULL);
    2275                 :            :         }
    2276                 :            : 
    2277                 :         10 :       g_free (item->title);
    2278                 :         10 :       item->title = g_strdup (title);
    2279                 :            : 
    2280                 :         10 :       bookmark_item_touch_modified (item);
    2281                 :            :     }
    2282                 :            : }
    2283                 :            : 
    2284                 :            : /**
    2285                 :            :  * g_bookmark_file_get_title:
    2286                 :            :  * @bookmark: a #GBookmarkFile
    2287                 :            :  * @uri: (nullable): a valid URI or %NULL
    2288                 :            :  * @error: return location for a #GError, or %NULL
    2289                 :            :  *
    2290                 :            :  * Returns the title of the bookmark for @uri.
    2291                 :            :  *
    2292                 :            :  * If @uri is %NULL, the title of @bookmark is returned.
    2293                 :            :  *
    2294                 :            :  * In the event the URI cannot be found, %NULL is returned and
    2295                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
    2296                 :            :  *
    2297                 :            :  * Returns: (transfer full): a newly allocated string or %NULL if the specified
    2298                 :            :  *   URI cannot be found.
    2299                 :            :  *
    2300                 :            :  * Since: 2.12
    2301                 :            :  */
    2302                 :            : gchar *
    2303                 :         21 : g_bookmark_file_get_title (GBookmarkFile  *bookmark,
    2304                 :            :                            const gchar    *uri,
    2305                 :            :                            GError        **error)
    2306                 :            : {
    2307                 :            :   BookmarkItem *item;
    2308                 :            : 
    2309                 :         21 :   g_return_val_if_fail (bookmark != NULL, NULL);
    2310                 :            : 
    2311         [ +  + ]:         20 :   if (!uri)
    2312                 :         18 :     return g_strdup (bookmark->title);
    2313                 :            : 
    2314                 :         11 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    2315         [ +  + ]:         11 :   if (!item)
    2316                 :            :     {
    2317                 :          1 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    2318                 :            :                    G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
    2319                 :            :                    _("No bookmark found for URI “%s”"),
    2320                 :            :                    uri);
    2321                 :          1 :       return NULL;
    2322                 :            :     }
    2323                 :            : 
    2324                 :         20 :   return g_strdup (item->title);
    2325                 :            : }
    2326                 :            : 
    2327                 :            : /**
    2328                 :            :  * g_bookmark_file_set_description:
    2329                 :            :  * @bookmark: a #GBookmarkFile
    2330                 :            :  * @uri: (nullable): a valid URI or %NULL
    2331                 :            :  * @description: a string
    2332                 :            :  *
    2333                 :            :  * Sets @description as the description of the bookmark for @uri.
    2334                 :            :  *
    2335                 :            :  * If @uri is %NULL, the description of @bookmark is set.
    2336                 :            :  *
    2337                 :            :  * If a bookmark for @uri cannot be found then it is created.
    2338                 :            :  *
    2339                 :            :  * Since: 2.12
    2340                 :            :  */
    2341                 :            : void
    2342                 :         20 : g_bookmark_file_set_description (GBookmarkFile *bookmark,
    2343                 :            :                                  const gchar   *uri,
    2344                 :            :                                  const gchar   *description)
    2345                 :            : {
    2346                 :         20 :   g_return_if_fail (bookmark != NULL);
    2347                 :            : 
    2348         [ +  + ]:         19 :   if (!uri)
    2349                 :            :     {
    2350                 :          9 :       g_free (bookmark->description);
    2351                 :          9 :       bookmark->description = g_strdup (description);
    2352                 :            :     }
    2353                 :            :   else
    2354                 :            :     {
    2355                 :            :       BookmarkItem *item;
    2356                 :            : 
    2357                 :         10 :       item = g_bookmark_file_lookup_item (bookmark, uri);
    2358         [ +  + ]:         10 :       if (!item)
    2359                 :            :         {
    2360                 :          1 :           item = bookmark_item_new (uri);
    2361                 :          1 :           g_bookmark_file_add_item (bookmark, item, NULL);
    2362                 :            :         }
    2363                 :            : 
    2364                 :         10 :       g_free (item->description);
    2365                 :         10 :       item->description = g_strdup (description);
    2366                 :            : 
    2367                 :         10 :       bookmark_item_touch_modified (item);
    2368                 :            :     }
    2369                 :            : }
    2370                 :            : 
    2371                 :            : /**
    2372                 :            :  * g_bookmark_file_get_description:
    2373                 :            :  * @bookmark: a #GBookmarkFile
    2374                 :            :  * @uri: a valid URI
    2375                 :            :  * @error: return location for a #GError, or %NULL
    2376                 :            :  *
    2377                 :            :  * Retrieves the description of the bookmark for @uri.
    2378                 :            :  *
    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.
    2381                 :            :  *
    2382                 :            :  * Returns: (transfer full): a newly allocated string or %NULL if the specified
    2383                 :            :  *   URI cannot be found.
    2384                 :            :  *
    2385                 :            :  * Since: 2.12
    2386                 :            :  */
    2387                 :            : gchar *
    2388                 :         29 : g_bookmark_file_get_description (GBookmarkFile  *bookmark,
    2389                 :            :                                  const gchar    *uri,
    2390                 :            :                                  GError        **error)
    2391                 :            : {
    2392                 :            :   BookmarkItem *item;
    2393                 :            : 
    2394                 :         29 :   g_return_val_if_fail (bookmark != NULL, NULL);
    2395                 :            : 
    2396         [ +  + ]:         28 :   if (!uri)
    2397                 :         18 :     return g_strdup (bookmark->description);
    2398                 :            : 
    2399                 :         19 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    2400         [ +  + ]:         19 :   if (!item)
    2401                 :            :     {
    2402                 :          9 :       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                 :          9 :       return NULL;
    2407                 :            :     }
    2408                 :            : 
    2409                 :         20 :   return g_strdup (item->description);
    2410                 :            : }
    2411                 :            : 
    2412                 :            : /**
    2413                 :            :  * g_bookmark_file_set_mime_type:
    2414                 :            :  * @bookmark: a #GBookmarkFile
    2415                 :            :  * @uri: a valid URI
    2416                 :            :  * @mime_type: a MIME type
    2417                 :            :  *
    2418                 :            :  * Sets @mime_type as the MIME type of the bookmark for @uri.
    2419                 :            :  *
    2420                 :            :  * If a bookmark for @uri cannot be found then it is created.
    2421                 :            :  *
    2422                 :            :  * Since: 2.12
    2423                 :            :  */
    2424                 :            : void
    2425                 :         14 : g_bookmark_file_set_mime_type (GBookmarkFile *bookmark,
    2426                 :            :                                const gchar   *uri,
    2427                 :            :                                const gchar   *mime_type)
    2428                 :            : {
    2429                 :            :   BookmarkItem *item;
    2430                 :            : 
    2431                 :         14 :   g_return_if_fail (bookmark != NULL);
    2432                 :         13 :   g_return_if_fail (uri != NULL);
    2433                 :         12 :   g_return_if_fail (mime_type != NULL);
    2434                 :            : 
    2435                 :         11 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    2436         [ +  + ]:         11 :   if (!item)
    2437                 :            :     {
    2438                 :          1 :       item = bookmark_item_new (uri);
    2439                 :          1 :       g_bookmark_file_add_item (bookmark, item, NULL);
    2440                 :            :     }
    2441                 :            : 
    2442         [ +  + ]:         11 :   if (!item->metadata)
    2443                 :          2 :     item->metadata = bookmark_metadata_new ();
    2444                 :            : 
    2445                 :         11 :   g_free (item->metadata->mime_type);
    2446                 :            : 
    2447                 :         11 :   item->metadata->mime_type = g_strdup (mime_type);
    2448                 :         11 :   bookmark_item_touch_modified (item);
    2449                 :            : }
    2450                 :            : 
    2451                 :            : /**
    2452                 :            :  * g_bookmark_file_get_mime_type:
    2453                 :            :  * @bookmark: a #GBookmarkFile
    2454                 :            :  * @uri: a valid URI
    2455                 :            :  * @error: return location for a #GError, or %NULL
    2456                 :            :  *
    2457                 :            :  * Retrieves the MIME type of the resource pointed by @uri.
    2458                 :            :  *
    2459                 :            :  * In the event the URI cannot be found, %NULL is returned and
    2460                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.  In the
    2461                 :            :  * event that the MIME type cannot be found, %NULL is returned and
    2462                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
    2463                 :            :  *
    2464                 :            :  * Returns: (transfer full): a newly allocated string or %NULL if the specified
    2465                 :            :  *   URI cannot be found.
    2466                 :            :  *
    2467                 :            :  * Since: 2.12
    2468                 :            :  */
    2469                 :            : gchar *
    2470                 :         11 : g_bookmark_file_get_mime_type (GBookmarkFile  *bookmark,
    2471                 :            :                                const gchar    *uri,
    2472                 :            :                                GError        **error)
    2473                 :            : {
    2474                 :            :   BookmarkItem *item;
    2475                 :            : 
    2476                 :         11 :   g_return_val_if_fail (bookmark != NULL, NULL);
    2477                 :         10 :   g_return_val_if_fail (uri != NULL, NULL);
    2478                 :            : 
    2479                 :          9 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    2480         [ +  + ]:          9 :   if (!item)
    2481                 :            :     {
    2482                 :          3 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    2483                 :            :                    G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
    2484                 :            :                    _("No bookmark found for URI “%s”"),
    2485                 :            :                    uri);
    2486                 :          3 :       return NULL;
    2487                 :            :     }
    2488                 :            : 
    2489         [ +  + ]:          6 :   if (!item->metadata)
    2490                 :            :     {
    2491                 :          1 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    2492                 :            :                    G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
    2493                 :            :                    _("No MIME type defined in the bookmark for URI “%s”"),
    2494                 :            :                    uri);
    2495                 :          1 :       return NULL;
    2496                 :            :     }
    2497                 :            : 
    2498                 :         10 :   return g_strdup (item->metadata->mime_type);
    2499                 :            : }
    2500                 :            : 
    2501                 :            : /**
    2502                 :            :  * g_bookmark_file_set_is_private:
    2503                 :            :  * @bookmark: a #GBookmarkFile
    2504                 :            :  * @uri: a valid URI
    2505                 :            :  * @is_private: %TRUE if the bookmark should be marked as private
    2506                 :            :  *
    2507                 :            :  * Sets the private flag of the bookmark for @uri.
    2508                 :            :  *
    2509                 :            :  * If a bookmark for @uri cannot be found then it is created.
    2510                 :            :  *
    2511                 :            :  * Since: 2.12
    2512                 :            :  */
    2513                 :            : void
    2514                 :         12 : g_bookmark_file_set_is_private (GBookmarkFile *bookmark,
    2515                 :            :                                 const gchar   *uri,
    2516                 :            :                                 gboolean       is_private)
    2517                 :            : {
    2518                 :            :   BookmarkItem *item;
    2519                 :            : 
    2520                 :         12 :   g_return_if_fail (bookmark != NULL);
    2521                 :         11 :   g_return_if_fail (uri != NULL);
    2522                 :            : 
    2523                 :         10 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    2524         [ +  + ]:         10 :   if (!item)
    2525                 :            :     {
    2526                 :          1 :       item = bookmark_item_new (uri);
    2527                 :          1 :       g_bookmark_file_add_item (bookmark, item, NULL);
    2528                 :            :     }
    2529                 :            : 
    2530         [ +  - ]:         10 :   if (!item->metadata)
    2531                 :         10 :     item->metadata = bookmark_metadata_new ();
    2532                 :            : 
    2533                 :         10 :   item->metadata->is_private = (is_private == TRUE);
    2534                 :         10 :   bookmark_item_touch_modified (item);
    2535                 :            : }
    2536                 :            : 
    2537                 :            : /**
    2538                 :            :  * g_bookmark_file_get_is_private:
    2539                 :            :  * @bookmark: a #GBookmarkFile
    2540                 :            :  * @uri: a valid URI
    2541                 :            :  * @error: return location for a #GError, or %NULL
    2542                 :            :  *
    2543                 :            :  * Gets whether the private flag of the bookmark for @uri is set.
    2544                 :            :  *
    2545                 :            :  * In the event the URI cannot be found, %FALSE is returned and
    2546                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.  In the
    2547                 :            :  * event that the private flag cannot be found, %FALSE is returned and
    2548                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
    2549                 :            :  *
    2550                 :            :  * Returns: %TRUE if the private flag is set, %FALSE otherwise.
    2551                 :            :  *
    2552                 :            :  * Since: 2.12
    2553                 :            :  */
    2554                 :            : gboolean
    2555                 :         22 : g_bookmark_file_get_is_private (GBookmarkFile  *bookmark,
    2556                 :            :                                 const gchar    *uri,
    2557                 :            :                                 GError        **error)
    2558                 :            : {
    2559                 :            :   BookmarkItem *item;
    2560                 :            : 
    2561                 :         22 :   g_return_val_if_fail (bookmark != NULL, FALSE);
    2562                 :         21 :   g_return_val_if_fail (uri != NULL, FALSE);
    2563                 :            : 
    2564                 :         20 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    2565         [ +  + ]:         20 :   if (!item)
    2566                 :            :     {
    2567                 :          9 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    2568                 :            :                    G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
    2569                 :            :                    _("No bookmark found for URI “%s”"),
    2570                 :            :                    uri);
    2571                 :          9 :       return FALSE;
    2572                 :            :     }
    2573                 :            : 
    2574         [ +  + ]:         11 :   if (!item->metadata)
    2575                 :            :     {
    2576                 :          1 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    2577                 :            :                    G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
    2578                 :            :                    _("No private flag has been defined in bookmark for URI “%s”"),
    2579                 :            :                     uri);
    2580                 :          1 :       return FALSE;
    2581                 :            :     }
    2582                 :            : 
    2583                 :         10 :   return item->metadata->is_private;
    2584                 :            : }
    2585                 :            : 
    2586                 :            : /**
    2587                 :            :  * g_bookmark_file_set_added:
    2588                 :            :  * @bookmark: a #GBookmarkFile
    2589                 :            :  * @uri: a valid URI
    2590                 :            :  * @added: a timestamp or -1 to use the current time
    2591                 :            :  *
    2592                 :            :  * Sets the time the bookmark for @uri was added into @bookmark.
    2593                 :            :  *
    2594                 :            :  * If no bookmark for @uri is found then it is created.
    2595                 :            :  *
    2596                 :            :  * Since: 2.12
    2597                 :            :  * Deprecated: 2.66: Use g_bookmark_file_set_added_date_time() instead, as
    2598                 :            :  *    `time_t` is deprecated due to the year 2038 problem.
    2599                 :            :  */
    2600                 :            : void
    2601                 :          2 : g_bookmark_file_set_added (GBookmarkFile *bookmark,
    2602                 :            :                            const gchar   *uri,
    2603                 :            :                            time_t         added)
    2604                 :            : {
    2605         [ +  + ]:          2 :   GDateTime *added_dt = (added != (time_t) -1) ? g_date_time_new_from_unix_utc (added) : g_date_time_new_now_utc ();
    2606                 :          2 :   g_bookmark_file_set_added_date_time (bookmark, uri, added_dt);
    2607                 :          2 :   g_date_time_unref (added_dt);
    2608                 :          2 : }
    2609                 :            : 
    2610                 :            : /**
    2611                 :            :  * g_bookmark_file_set_added_date_time:
    2612                 :            :  * @bookmark: a #GBookmarkFile
    2613                 :            :  * @uri: a valid URI
    2614                 :            :  * @added: a #GDateTime
    2615                 :            :  *
    2616                 :            :  * Sets the time the bookmark for @uri was added into @bookmark.
    2617                 :            :  *
    2618                 :            :  * If no bookmark for @uri is found then it is created.
    2619                 :            :  *
    2620                 :            :  * Since: 2.66
    2621                 :            :  */
    2622                 :            : void
    2623                 :         15 : g_bookmark_file_set_added_date_time (GBookmarkFile *bookmark,
    2624                 :            :                                      const char    *uri,
    2625                 :            :                                      GDateTime     *added)
    2626                 :            : {
    2627                 :            :   BookmarkItem *item;
    2628                 :            : 
    2629                 :         15 :   g_return_if_fail (bookmark != NULL);
    2630                 :         14 :   g_return_if_fail (uri != NULL);
    2631                 :         13 :   g_return_if_fail (added != NULL);
    2632                 :            : 
    2633                 :         12 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    2634         [ +  + ]:         12 :   if (!item)
    2635                 :            :     {
    2636                 :          2 :       item = bookmark_item_new (uri);
    2637                 :          2 :       g_bookmark_file_add_item (bookmark, item, NULL);
    2638                 :            :     }
    2639                 :            : 
    2640                 :         12 :   g_clear_pointer (&item->added, g_date_time_unref);
    2641                 :         12 :   item->added = g_date_time_ref (added);
    2642                 :         12 :   g_clear_pointer (&item->modified, g_date_time_unref);
    2643                 :         12 :   item->modified = g_date_time_ref (added);
    2644                 :            : }
    2645                 :            : 
    2646                 :            : /**
    2647                 :            :  * g_bookmark_file_get_added:
    2648                 :            :  * @bookmark: a #GBookmarkFile
    2649                 :            :  * @uri: a valid URI
    2650                 :            :  * @error: return location for a #GError, or %NULL
    2651                 :            :  *
    2652                 :            :  * Gets the time the bookmark for @uri was added to @bookmark
    2653                 :            :  *
    2654                 :            :  * In the event the URI cannot be found, -1 is returned and
    2655                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
    2656                 :            :  *
    2657                 :            :  * Returns: a timestamp
    2658                 :            :  *
    2659                 :            :  * Since: 2.12
    2660                 :            :  * Deprecated: 2.66: Use g_bookmark_file_get_added_date_time() instead, as
    2661                 :            :  *    `time_t` is deprecated due to the year 2038 problem.
    2662                 :            :  */
    2663                 :            : time_t
    2664                 :          3 : g_bookmark_file_get_added (GBookmarkFile  *bookmark,
    2665                 :            :                            const gchar    *uri,
    2666                 :            :                            GError        **error)
    2667                 :            : {
    2668                 :          3 :   GDateTime *added = g_bookmark_file_get_added_date_time (bookmark, uri, error);
    2669         [ +  + ]:          3 :   return (added != NULL) ? g_date_time_to_unix (added) : (time_t) -1;
    2670                 :            : }
    2671                 :            : 
    2672                 :            : /**
    2673                 :            :  * g_bookmark_file_get_added_date_time:
    2674                 :            :  * @bookmark: a #GBookmarkFile
    2675                 :            :  * @uri: a valid URI
    2676                 :            :  * @error: return location for a #GError, or %NULL
    2677                 :            :  *
    2678                 :            :  * Gets the time the bookmark for @uri was added to @bookmark
    2679                 :            :  *
    2680                 :            :  * In the event the URI cannot be found, %NULL is returned and
    2681                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
    2682                 :            :  *
    2683                 :            :  * Returns: (transfer none): a #GDateTime
    2684                 :            :  *
    2685                 :            :  * Since: 2.66
    2686                 :            :  */
    2687                 :            : GDateTime *
    2688                 :         26 : g_bookmark_file_get_added_date_time (GBookmarkFile  *bookmark,
    2689                 :            :                                      const char     *uri,
    2690                 :            :                                      GError        **error)
    2691                 :            : {
    2692                 :            :   BookmarkItem *item;
    2693                 :            : 
    2694                 :         26 :   g_return_val_if_fail (bookmark != NULL, NULL);
    2695                 :         25 :   g_return_val_if_fail (uri != NULL, NULL);
    2696                 :         24 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
    2697                 :            : 
    2698                 :         24 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    2699         [ +  + ]:         24 :   if (!item)
    2700                 :            :     {
    2701                 :         12 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    2702                 :            :                    G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
    2703                 :            :                    _("No bookmark found for URI “%s”"),
    2704                 :            :                    uri);
    2705                 :         12 :       return NULL;
    2706                 :            :     }
    2707                 :            : 
    2708                 :         12 :   return item->added;
    2709                 :            : }
    2710                 :            : 
    2711                 :            : /**
    2712                 :            :  * g_bookmark_file_set_modified:
    2713                 :            :  * @bookmark: a #GBookmarkFile
    2714                 :            :  * @uri: a valid URI
    2715                 :            :  * @modified: a timestamp or -1 to use the current time
    2716                 :            :  *
    2717                 :            :  * Sets the last time the bookmark for @uri was last modified.
    2718                 :            :  *
    2719                 :            :  * If no bookmark for @uri is found then it is created.
    2720                 :            :  *
    2721                 :            :  * The "modified" time should only be set when the bookmark's meta-data
    2722                 :            :  * was actually changed.  Every function of #GBookmarkFile that
    2723                 :            :  * modifies a bookmark also changes the modification time, except for
    2724                 :            :  * g_bookmark_file_set_visited_date_time().
    2725                 :            :  *
    2726                 :            :  * Since: 2.12
    2727                 :            :  * Deprecated: 2.66: Use g_bookmark_file_set_modified_date_time() instead, as
    2728                 :            :  *    `time_t` is deprecated due to the year 2038 problem.
    2729                 :            :  */
    2730                 :            : void
    2731                 :          2 : g_bookmark_file_set_modified (GBookmarkFile *bookmark,
    2732                 :            :                               const gchar   *uri,
    2733                 :            :                               time_t         modified)
    2734                 :            : {
    2735         [ +  + ]:          2 :   GDateTime *modified_dt = (modified != (time_t) -1) ? g_date_time_new_from_unix_utc (modified) : g_date_time_new_now_utc ();
    2736                 :          2 :   g_bookmark_file_set_modified_date_time (bookmark, uri, modified_dt);
    2737                 :          2 :   g_date_time_unref (modified_dt);
    2738                 :          2 : }
    2739                 :            : 
    2740                 :            : /**
    2741                 :            :  * g_bookmark_file_set_modified_date_time:
    2742                 :            :  * @bookmark: a #GBookmarkFile
    2743                 :            :  * @uri: a valid URI
    2744                 :            :  * @modified: a #GDateTime
    2745                 :            :  *
    2746                 :            :  * Sets the last time the bookmark for @uri was last modified.
    2747                 :            :  *
    2748                 :            :  * If no bookmark for @uri is found then it is created.
    2749                 :            :  *
    2750                 :            :  * The "modified" time should only be set when the bookmark's meta-data
    2751                 :            :  * was actually changed.  Every function of #GBookmarkFile that
    2752                 :            :  * modifies a bookmark also changes the modification time, except for
    2753                 :            :  * g_bookmark_file_set_visited_date_time().
    2754                 :            :  *
    2755                 :            :  * Since: 2.66
    2756                 :            :  */
    2757                 :            : void
    2758                 :         15 : g_bookmark_file_set_modified_date_time (GBookmarkFile *bookmark,
    2759                 :            :                                         const char    *uri,
    2760                 :            :                                         GDateTime     *modified)
    2761                 :            : {
    2762                 :            :   BookmarkItem *item;
    2763                 :            : 
    2764                 :         15 :   g_return_if_fail (bookmark != NULL);
    2765                 :         14 :   g_return_if_fail (uri != NULL);
    2766                 :         13 :   g_return_if_fail (modified != NULL);
    2767                 :            : 
    2768                 :         12 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    2769         [ +  + ]:         12 :   if (!item)
    2770                 :            :     {
    2771                 :          1 :       item = bookmark_item_new (uri);
    2772                 :          1 :       g_bookmark_file_add_item (bookmark, item, NULL);
    2773                 :            :     }
    2774                 :            : 
    2775                 :         12 :   g_clear_pointer (&item->modified, g_date_time_unref);
    2776                 :         12 :   item->modified = g_date_time_ref (modified);
    2777                 :            : }
    2778                 :            : 
    2779                 :            : /**
    2780                 :            :  * g_bookmark_file_get_modified:
    2781                 :            :  * @bookmark: a #GBookmarkFile
    2782                 :            :  * @uri: a valid URI
    2783                 :            :  * @error: return location for a #GError, or %NULL
    2784                 :            :  *
    2785                 :            :  * Gets the time when the bookmark for @uri was last modified.
    2786                 :            :  *
    2787                 :            :  * In the event the URI cannot be found, -1 is returned and
    2788                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
    2789                 :            :  *
    2790                 :            :  * Returns: a timestamp
    2791                 :            :  *
    2792                 :            :  * Since: 2.12
    2793                 :            :  * Deprecated: 2.66: Use g_bookmark_file_get_modified_date_time() instead, as
    2794                 :            :  *    `time_t` is deprecated due to the year 2038 problem.
    2795                 :            :  */
    2796                 :            : time_t
    2797                 :          3 : g_bookmark_file_get_modified (GBookmarkFile  *bookmark,
    2798                 :            :                               const gchar    *uri,
    2799                 :            :                               GError        **error)
    2800                 :            : {
    2801                 :          3 :   GDateTime *modified = g_bookmark_file_get_modified_date_time (bookmark, uri, error);
    2802         [ +  + ]:          3 :   return (modified != NULL) ? g_date_time_to_unix (modified) : (time_t) -1;
    2803                 :            : }
    2804                 :            : 
    2805                 :            : /**
    2806                 :            :  * g_bookmark_file_get_modified_date_time:
    2807                 :            :  * @bookmark: a #GBookmarkFile
    2808                 :            :  * @uri: a valid URI
    2809                 :            :  * @error: return location for a #GError, or %NULL
    2810                 :            :  *
    2811                 :            :  * Gets the time when the bookmark for @uri was last modified.
    2812                 :            :  *
    2813                 :            :  * In the event the URI cannot be found, %NULL is returned and
    2814                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
    2815                 :            :  *
    2816                 :            :  * Returns: (transfer none): a #GDateTime
    2817                 :            :  *
    2818                 :            :  * Since: 2.66
    2819                 :            :  */
    2820                 :            : GDateTime *
    2821                 :         35 : g_bookmark_file_get_modified_date_time (GBookmarkFile  *bookmark,
    2822                 :            :                                         const char     *uri,
    2823                 :            :                                         GError        **error)
    2824                 :            : {
    2825                 :            :   BookmarkItem *item;
    2826                 :            : 
    2827                 :         35 :   g_return_val_if_fail (bookmark != NULL, NULL);
    2828                 :         34 :   g_return_val_if_fail (uri != NULL, NULL);
    2829                 :         33 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
    2830                 :            : 
    2831                 :         33 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    2832         [ +  + ]:         33 :   if (!item)
    2833                 :            :     {
    2834                 :         12 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    2835                 :            :                    G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
    2836                 :            :                    _("No bookmark found for URI “%s”"),
    2837                 :            :                    uri);
    2838                 :         12 :       return NULL;
    2839                 :            :     }
    2840                 :            : 
    2841                 :         21 :   return item->modified;
    2842                 :            : }
    2843                 :            : 
    2844                 :            : /**
    2845                 :            :  * g_bookmark_file_set_visited:
    2846                 :            :  * @bookmark: a #GBookmarkFile
    2847                 :            :  * @uri: a valid URI
    2848                 :            :  * @visited: a timestamp or -1 to use the current time
    2849                 :            :  *
    2850                 :            :  * Sets the time the bookmark for @uri was last visited.
    2851                 :            :  *
    2852                 :            :  * If no bookmark for @uri is found then it is created.
    2853                 :            :  *
    2854                 :            :  * The "visited" time should only be set if the bookmark was launched,
    2855                 :            :  * either using the command line retrieved by g_bookmark_file_get_application_info()
    2856                 :            :  * or by the default application for the bookmark's MIME type, retrieved
    2857                 :            :  * using g_bookmark_file_get_mime_type().  Changing the "visited" time
    2858                 :            :  * does not affect the "modified" time.
    2859                 :            :  *
    2860                 :            :  * Since: 2.12
    2861                 :            :  * Deprecated: 2.66: Use g_bookmark_file_set_visited_date_time() instead, as
    2862                 :            :  *    `time_t` is deprecated due to the year 2038 problem.
    2863                 :            :  */
    2864                 :            : void
    2865                 :          2 : g_bookmark_file_set_visited (GBookmarkFile *bookmark,
    2866                 :            :                              const gchar   *uri,
    2867                 :            :                              time_t         visited)
    2868                 :            : {
    2869         [ +  + ]:          2 :   GDateTime *visited_dt = (visited != (time_t) -1) ? g_date_time_new_from_unix_utc (visited) : g_date_time_new_now_utc ();
    2870                 :          2 :   g_bookmark_file_set_visited_date_time (bookmark, uri, visited_dt);
    2871                 :          2 :   g_date_time_unref (visited_dt);
    2872                 :          2 : }
    2873                 :            : 
    2874                 :            : /**
    2875                 :            :  * g_bookmark_file_set_visited_date_time:
    2876                 :            :  * @bookmark: a #GBookmarkFile
    2877                 :            :  * @uri: a valid URI
    2878                 :            :  * @visited: a #GDateTime
    2879                 :            :  *
    2880                 :            :  * Sets the time the bookmark for @uri was last visited.
    2881                 :            :  *
    2882                 :            :  * If no bookmark for @uri is found then it is created.
    2883                 :            :  *
    2884                 :            :  * The "visited" time should only be set if the bookmark was launched,
    2885                 :            :  * either using the command line retrieved by g_bookmark_file_get_application_info()
    2886                 :            :  * or by the default application for the bookmark's MIME type, retrieved
    2887                 :            :  * using g_bookmark_file_get_mime_type().  Changing the "visited" time
    2888                 :            :  * does not affect the "modified" time.
    2889                 :            :  *
    2890                 :            :  * Since: 2.66
    2891                 :            :  */
    2892                 :            : void
    2893                 :         15 : g_bookmark_file_set_visited_date_time (GBookmarkFile *bookmark,
    2894                 :            :                                        const char    *uri,
    2895                 :            :                                        GDateTime     *visited)
    2896                 :            : {
    2897                 :            :   BookmarkItem *item;
    2898                 :            : 
    2899                 :         15 :   g_return_if_fail (bookmark != NULL);
    2900                 :         14 :   g_return_if_fail (uri != NULL);
    2901                 :         13 :   g_return_if_fail (visited != NULL);
    2902                 :            : 
    2903                 :         12 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    2904         [ +  + ]:         12 :   if (!item)
    2905                 :            :     {
    2906                 :          1 :       item = bookmark_item_new (uri);
    2907                 :          1 :       g_bookmark_file_add_item (bookmark, item, NULL);
    2908                 :            :     }
    2909                 :            : 
    2910                 :         12 :   g_clear_pointer (&item->visited, g_date_time_unref);
    2911                 :         12 :   item->visited = g_date_time_ref (visited);
    2912                 :            : }
    2913                 :            : 
    2914                 :            : /**
    2915                 :            :  * g_bookmark_file_get_visited:
    2916                 :            :  * @bookmark: a #GBookmarkFile
    2917                 :            :  * @uri: a valid URI
    2918                 :            :  * @error: return location for a #GError, or %NULL
    2919                 :            :  *
    2920                 :            :  * Gets the time the bookmark for @uri was last visited.
    2921                 :            :  *
    2922                 :            :  * In the event the URI cannot be found, -1 is returned and
    2923                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
    2924                 :            :  *
    2925                 :            :  * Returns: a timestamp.
    2926                 :            :  *
    2927                 :            :  * Since: 2.12
    2928                 :            :  * Deprecated: 2.66: Use g_bookmark_file_get_visited_date_time() instead, as
    2929                 :            :  *    `time_t` is deprecated due to the year 2038 problem.
    2930                 :            :  */
    2931                 :            : time_t
    2932                 :          3 : g_bookmark_file_get_visited (GBookmarkFile  *bookmark,
    2933                 :            :                              const gchar    *uri,
    2934                 :            :                              GError        **error)
    2935                 :            : {
    2936                 :          3 :   GDateTime *visited = g_bookmark_file_get_visited_date_time (bookmark, uri, error);
    2937         [ +  + ]:          3 :   return (visited != NULL) ? g_date_time_to_unix (visited) : (time_t) -1;
    2938                 :            : }
    2939                 :            : 
    2940                 :            : /**
    2941                 :            :  * g_bookmark_file_get_visited_date_time:
    2942                 :            :  * @bookmark: a #GBookmarkFile
    2943                 :            :  * @uri: a valid URI
    2944                 :            :  * @error: return location for a #GError, or %NULL
    2945                 :            :  *
    2946                 :            :  * Gets the time the bookmark for @uri was last visited.
    2947                 :            :  *
    2948                 :            :  * In the event the URI cannot be found, %NULL is returned and
    2949                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
    2950                 :            :  *
    2951                 :            :  * Returns: (transfer none): a #GDateTime
    2952                 :            :  *
    2953                 :            :  * Since: 2.66
    2954                 :            :  */
    2955                 :            : GDateTime *
    2956                 :         26 : g_bookmark_file_get_visited_date_time (GBookmarkFile  *bookmark,
    2957                 :            :                                        const char     *uri,
    2958                 :            :                                        GError        **error)
    2959                 :            : {
    2960                 :            :   BookmarkItem *item;
    2961                 :            : 
    2962                 :         26 :   g_return_val_if_fail (bookmark != NULL, NULL);
    2963                 :         25 :   g_return_val_if_fail (uri != NULL, NULL);
    2964                 :         24 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
    2965                 :            : 
    2966                 :         24 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    2967         [ +  + ]:         24 :   if (!item)
    2968                 :            :     {
    2969                 :         12 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    2970                 :            :                    G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
    2971                 :            :                    _("No bookmark found for URI “%s”"),
    2972                 :            :                    uri);
    2973                 :         12 :       return NULL;
    2974                 :            :     }
    2975                 :            : 
    2976                 :         12 :   return item->visited;
    2977                 :            : }
    2978                 :            : 
    2979                 :            : /**
    2980                 :            :  * g_bookmark_file_has_group:
    2981                 :            :  * @bookmark: a #GBookmarkFile
    2982                 :            :  * @uri: a valid URI
    2983                 :            :  * @group: the group name to be searched
    2984                 :            :  * @error: return location for a #GError, or %NULL
    2985                 :            :  *
    2986                 :            :  * Checks whether @group appears in the list of groups to which
    2987                 :            :  * the bookmark for @uri belongs to.
    2988                 :            :  *
    2989                 :            :  * In the event the URI cannot be found, %FALSE is returned and
    2990                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
    2991                 :            :  *
    2992                 :            :  * Returns: %TRUE if @group was found.
    2993                 :            :  *
    2994                 :            :  * Since: 2.12
    2995                 :            :  */
    2996                 :            : gboolean
    2997                 :         38 : g_bookmark_file_has_group (GBookmarkFile  *bookmark,
    2998                 :            :                            const gchar    *uri,
    2999                 :            :                            const gchar    *group,
    3000                 :            :                            GError        **error)
    3001                 :            : {
    3002                 :            :   BookmarkItem *item;
    3003                 :            :   GList *l;
    3004                 :            : 
    3005                 :         38 :   g_return_val_if_fail (bookmark != NULL, FALSE);
    3006                 :         37 :   g_return_val_if_fail (uri != NULL, FALSE);
    3007                 :            : 
    3008                 :         36 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    3009         [ +  + ]:         36 :   if (!item)
    3010                 :            :     {
    3011                 :          9 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    3012                 :            :                    G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
    3013                 :            :                    _("No bookmark found for URI “%s”"),
    3014                 :            :                    uri);
    3015                 :          9 :       return FALSE;
    3016                 :            :     }
    3017                 :            : 
    3018         [ -  + ]:         27 :   if (!item->metadata)
    3019                 :          0 :     return FALSE;
    3020                 :            : 
    3021         [ +  + ]:         36 :   for (l = item->metadata->groups; l != NULL; l = l->next)
    3022                 :            :     {
    3023         [ +  + ]:         18 :       if (strcmp (l->data, group) == 0)
    3024                 :          9 :         return TRUE;
    3025                 :            :     }
    3026                 :            : 
    3027                 :         18 :   return FALSE;
    3028                 :            : 
    3029                 :            : }
    3030                 :            : 
    3031                 :            : /**
    3032                 :            :  * g_bookmark_file_add_group:
    3033                 :            :  * @bookmark: a #GBookmarkFile
    3034                 :            :  * @uri: a valid URI
    3035                 :            :  * @group: the group name to be added
    3036                 :            :  *
    3037                 :            :  * Adds @group to the list of groups to which the bookmark for @uri
    3038                 :            :  * belongs to.
    3039                 :            :  *
    3040                 :            :  * If no bookmark for @uri is found then it is created.
    3041                 :            :  *
    3042                 :            :  * Since: 2.12
    3043                 :            :  */
    3044                 :            : void
    3045                 :         13 : g_bookmark_file_add_group (GBookmarkFile *bookmark,
    3046                 :            :                            const gchar   *uri,
    3047                 :            :                            const gchar   *group)
    3048                 :            : {
    3049                 :            :   BookmarkItem *item;
    3050                 :            : 
    3051                 :         13 :   g_return_if_fail (bookmark != NULL);
    3052                 :         12 :   g_return_if_fail (uri != NULL);
    3053                 :         11 :   g_return_if_fail (group != NULL && group[0] != '\0');
    3054                 :            : 
    3055                 :          9 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    3056         [ +  - ]:          9 :   if (!item)
    3057                 :            :     {
    3058                 :          9 :       item = bookmark_item_new (uri);
    3059                 :          9 :       g_bookmark_file_add_item (bookmark, item, NULL);
    3060                 :            :     }
    3061                 :            : 
    3062         [ +  - ]:          9 :   if (!item->metadata)
    3063                 :          9 :     item->metadata = bookmark_metadata_new ();
    3064                 :            : 
    3065         [ +  - ]:          9 :   if (!g_bookmark_file_has_group (bookmark, uri, group, NULL))
    3066                 :            :     {
    3067                 :         18 :       item->metadata->groups = g_list_prepend (item->metadata->groups,
    3068                 :          9 :                                                g_strdup (group));
    3069                 :            : 
    3070                 :          9 :       bookmark_item_touch_modified (item);
    3071                 :            :     }
    3072                 :            : }
    3073                 :            : 
    3074                 :            : /**
    3075                 :            :  * g_bookmark_file_remove_group:
    3076                 :            :  * @bookmark: a #GBookmarkFile
    3077                 :            :  * @uri: a valid URI
    3078                 :            :  * @group: the group name to be removed
    3079                 :            :  * @error: return location for a #GError, or %NULL
    3080                 :            :  *
    3081                 :            :  * Removes @group from the list of groups to which the bookmark
    3082                 :            :  * for @uri belongs to.
    3083                 :            :  *
    3084                 :            :  * In the event the URI cannot be found, %FALSE is returned and
    3085                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
    3086                 :            :  * In the event no group was defined, %FALSE is returned and
    3087                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
    3088                 :            :  *
    3089                 :            :  * Returns: %TRUE if @group was successfully removed.
    3090                 :            :  *
    3091                 :            :  * Since: 2.12
    3092                 :            :  */
    3093                 :            : gboolean
    3094                 :         13 : g_bookmark_file_remove_group (GBookmarkFile  *bookmark,
    3095                 :            :                               const gchar    *uri,
    3096                 :            :                               const gchar    *group,
    3097                 :            :                               GError        **error)
    3098                 :            : {
    3099                 :            :   BookmarkItem *item;
    3100                 :            :   GList *l;
    3101                 :            : 
    3102                 :         13 :   g_return_val_if_fail (bookmark != NULL, FALSE);
    3103                 :         12 :   g_return_val_if_fail (uri != NULL, FALSE);
    3104                 :            : 
    3105                 :         11 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    3106         [ +  + ]:         11 :   if (!item)
    3107                 :            :     {
    3108                 :          2 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    3109                 :            :                    G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
    3110                 :            :                    _("No bookmark found for URI “%s”"),
    3111                 :            :                    uri);
    3112                 :          2 :       return FALSE;
    3113                 :            :     }
    3114                 :            : 
    3115         [ -  + ]:          9 :   if (!item->metadata)
    3116                 :            :     {
    3117                 :          0 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    3118                 :            :                    G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
    3119                 :            :                    _("No groups set in bookmark for URI “%s”"),
    3120                 :            :                    uri);
    3121                 :          0 :       return FALSE;
    3122                 :            :     }
    3123                 :            : 
    3124         [ +  - ]:          9 :   for (l = item->metadata->groups; l != NULL; l = l->next)
    3125                 :            :     {
    3126         [ +  - ]:          9 :       if (strcmp (l->data, group) == 0)
    3127                 :            :         {
    3128                 :          9 :           item->metadata->groups = g_list_remove_link (item->metadata->groups, l);
    3129                 :          9 :           g_free (l->data);
    3130                 :          9 :           g_list_free_1 (l);
    3131                 :            : 
    3132                 :          9 :           bookmark_item_touch_modified (item);
    3133                 :            : 
    3134                 :          9 :           return TRUE;
    3135                 :            :         }
    3136                 :            :     }
    3137                 :            : 
    3138                 :          0 :   return FALSE;
    3139                 :            : }
    3140                 :            : 
    3141                 :            : /**
    3142                 :            :  * g_bookmark_file_set_groups:
    3143                 :            :  * @bookmark: a #GBookmarkFile
    3144                 :            :  * @uri: an item's URI
    3145                 :            :  * @groups: (nullable) (array length=length) (element-type utf8): an array of
    3146                 :            :  *    group names, or %NULL to remove all groups
    3147                 :            :  * @length: number of group name values in @groups
    3148                 :            :  *
    3149                 :            :  * Sets a list of group names for the item with URI @uri.  Each previously
    3150                 :            :  * set group name list is removed.
    3151                 :            :  *
    3152                 :            :  * If @uri cannot be found then an item for it is created.
    3153                 :            :  *
    3154                 :            :  * Since: 2.12
    3155                 :            :  */
    3156                 :            : void
    3157                 :         12 : g_bookmark_file_set_groups (GBookmarkFile  *bookmark,
    3158                 :            :                             const gchar    *uri,
    3159                 :            :                             const gchar   **groups,
    3160                 :            :                             gsize           length)
    3161                 :            : {
    3162                 :            :   BookmarkItem *item;
    3163                 :            :   gsize i;
    3164                 :            : 
    3165                 :         12 :   g_return_if_fail (bookmark != NULL);
    3166                 :         11 :   g_return_if_fail (uri != NULL);
    3167                 :         10 :   g_return_if_fail (groups != NULL);
    3168                 :            : 
    3169                 :          9 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    3170         [ -  + ]:          9 :   if (!item)
    3171                 :            :     {
    3172                 :          0 :       item = bookmark_item_new (uri);
    3173                 :          0 :       g_bookmark_file_add_item (bookmark, item, NULL);
    3174                 :            :     }
    3175                 :            : 
    3176         [ -  + ]:          9 :   if (!item->metadata)
    3177                 :          0 :     item->metadata = bookmark_metadata_new ();
    3178                 :            : 
    3179                 :          9 :   g_list_free_full (item->metadata->groups, g_free);
    3180                 :          9 :   item->metadata->groups = NULL;
    3181                 :            : 
    3182         [ +  - ]:          9 :   if (groups)
    3183                 :            :     {
    3184   [ +  +  +  - ]:         27 :       for (i = 0; i < length && groups[i] != NULL; i++)
    3185                 :         18 :         item->metadata->groups = g_list_append (item->metadata->groups,
    3186                 :         36 :                                                 g_strdup (groups[i]));
    3187                 :            :     }
    3188                 :            : 
    3189                 :          9 :   bookmark_item_touch_modified (item);
    3190                 :            : }
    3191                 :            : 
    3192                 :            : /**
    3193                 :            :  * g_bookmark_file_get_groups:
    3194                 :            :  * @bookmark: a #GBookmarkFile
    3195                 :            :  * @uri: a valid URI
    3196                 :            :  * @length: (out) (optional): return location for the length of the returned string, or %NULL
    3197                 :            :  * @error: return location for a #GError, or %NULL
    3198                 :            :  *
    3199                 :            :  * Retrieves the list of group names of the bookmark for @uri.
    3200                 :            :  *
    3201                 :            :  * In the event the URI cannot be found, %NULL is returned and
    3202                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
    3203                 :            :  *
    3204                 :            :  * The returned array is %NULL terminated, so @length may optionally
    3205                 :            :  * be %NULL.
    3206                 :            :  *
    3207                 :            :  * Returns: (array length=length) (transfer full): a newly allocated %NULL-terminated array of group names.
    3208                 :            :  *   Use g_strfreev() to free it.
    3209                 :            :  *
    3210                 :            :  * Since: 2.12
    3211                 :            :  */
    3212                 :            : gchar **
    3213                 :         22 : g_bookmark_file_get_groups (GBookmarkFile  *bookmark,
    3214                 :            :                             const gchar    *uri,
    3215                 :            :                             gsize          *length,
    3216                 :            :                             GError        **error)
    3217                 :            : {
    3218                 :            :   BookmarkItem *item;
    3219                 :            :   GList *l;
    3220                 :            :   gsize len, i;
    3221                 :            :   gchar **retval;
    3222                 :            : 
    3223                 :         22 :   g_return_val_if_fail (bookmark != NULL, NULL);
    3224                 :         21 :   g_return_val_if_fail (uri != NULL, NULL);
    3225                 :            : 
    3226                 :         20 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    3227         [ +  + ]:         20 :   if (!item)
    3228                 :            :     {
    3229                 :          2 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    3230                 :            :                    G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
    3231                 :            :                    _("No bookmark found for URI “%s”"),
    3232                 :            :                    uri);
    3233                 :          2 :       return NULL;
    3234                 :            :     }
    3235                 :            : 
    3236         [ -  + ]:         18 :   if (!item->metadata)
    3237                 :            :     {
    3238         [ #  # ]:          0 :       if (length)
    3239                 :          0 :         *length = 0;
    3240                 :            : 
    3241                 :          0 :       return NULL;
    3242                 :            :     }
    3243                 :            : 
    3244                 :         18 :   len = g_list_length (item->metadata->groups);
    3245                 :         18 :   retval = g_new0 (gchar *, len + 1);
    3246                 :         18 :   for (l = g_list_last (item->metadata->groups), i = 0;
    3247         [ +  + ]:         36 :        l != NULL;
    3248                 :         18 :        l = l->prev)
    3249                 :            :     {
    3250                 :         18 :       gchar *group_name = (gchar *) l->data;
    3251                 :            : 
    3252         [ -  + ]:         18 :       g_warn_if_fail (group_name != NULL);
    3253                 :            : 
    3254                 :         36 :       retval[i++] = g_strdup (group_name);
    3255                 :            :     }
    3256                 :         18 :   retval[i] = NULL;
    3257                 :            : 
    3258         [ +  + ]:         18 :   if (length)
    3259                 :          9 :     *length = len;
    3260                 :            : 
    3261                 :         18 :   return retval;
    3262                 :            : }
    3263                 :            : 
    3264                 :            : /**
    3265                 :            :  * g_bookmark_file_add_application:
    3266                 :            :  * @bookmark: a #GBookmarkFile
    3267                 :            :  * @uri: a valid URI
    3268                 :            :  * @name: (nullable): the name of the application registering the bookmark
    3269                 :            :  *   or %NULL
    3270                 :            :  * @exec: (nullable): command line to be used to launch the bookmark or %NULL
    3271                 :            :  *
    3272                 :            :  * Adds the application with @name and @exec to the list of
    3273                 :            :  * applications that have registered a bookmark for @uri into
    3274                 :            :  * @bookmark.
    3275                 :            :  *
    3276                 :            :  * Every bookmark inside a #GBookmarkFile must have at least an
    3277                 :            :  * application registered.  Each application must provide a name, a
    3278                 :            :  * command line useful for launching the bookmark, the number of times
    3279                 :            :  * the bookmark has been registered by the application and the last
    3280                 :            :  * time the application registered this bookmark.
    3281                 :            :  *
    3282                 :            :  * If @name is %NULL, the name of the application will be the
    3283                 :            :  * same returned by g_get_application_name(); if @exec is %NULL, the
    3284                 :            :  * command line will be a composition of the program name as
    3285                 :            :  * returned by g_get_prgname() and the "\%u" modifier, which will be
    3286                 :            :  * expanded to the bookmark's URI.
    3287                 :            :  *
    3288                 :            :  * This function will automatically take care of updating the
    3289                 :            :  * registrations count and timestamping in case an application
    3290                 :            :  * with the same @name had already registered a bookmark for
    3291                 :            :  * @uri inside @bookmark.
    3292                 :            :  *
    3293                 :            :  * If no bookmark for @uri is found, one is created.
    3294                 :            :  *
    3295                 :            :  * Since: 2.12
    3296                 :            :  */
    3297                 :            : void
    3298                 :         25 : g_bookmark_file_add_application (GBookmarkFile *bookmark,
    3299                 :            :                                  const gchar   *uri,
    3300                 :            :                                  const gchar   *name,
    3301                 :            :                                  const gchar   *exec)
    3302                 :            : {
    3303                 :            :   BookmarkItem *item;
    3304                 :            :   gchar *app_name, *app_exec;
    3305                 :            :   GDateTime *stamp;
    3306                 :            : 
    3307                 :         25 :   g_return_if_fail (bookmark != NULL);
    3308                 :         24 :   g_return_if_fail (uri != NULL);
    3309                 :            : 
    3310                 :         23 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    3311         [ +  + ]:         23 :   if (!item)
    3312                 :            :     {
    3313                 :          2 :       item = bookmark_item_new (uri);
    3314                 :          2 :       g_bookmark_file_add_item (bookmark, item, NULL);
    3315                 :            :     }
    3316                 :            : 
    3317   [ +  +  +  - ]:         23 :   if (name && name[0] != '\0')
    3318                 :         21 :     app_name = g_strdup (name);
    3319                 :            :   else
    3320                 :          4 :     app_name = g_strdup (g_get_application_name ());
    3321                 :            : 
    3322   [ +  +  +  - ]:         23 :   if (exec && exec[0] != '\0')
    3323                 :         20 :     app_exec = g_strdup (exec);
    3324                 :            :   else
    3325                 :          3 :     app_exec = g_strjoin (" ", g_get_prgname(), "%u", NULL);
    3326                 :            : 
    3327                 :         23 :   stamp = g_date_time_new_now_utc ();
    3328                 :            : 
    3329                 :         23 :   g_bookmark_file_set_application_info (bookmark, uri,
    3330                 :            :                                         app_name,
    3331                 :            :                                         app_exec,
    3332                 :            :                                         -1,
    3333                 :            :                                         stamp,
    3334                 :            :                                         NULL);
    3335                 :            : 
    3336                 :         23 :   g_date_time_unref (stamp);
    3337                 :         23 :   g_free (app_exec);
    3338                 :         23 :   g_free (app_name);
    3339                 :            : }
    3340                 :            : 
    3341                 :            : /**
    3342                 :            :  * g_bookmark_file_remove_application:
    3343                 :            :  * @bookmark: a #GBookmarkFile
    3344                 :            :  * @uri: a valid URI
    3345                 :            :  * @name: the name of the application
    3346                 :            :  * @error: return location for a #GError or %NULL
    3347                 :            :  *
    3348                 :            :  * Removes application registered with @name from the list of applications
    3349                 :            :  * that have registered a bookmark for @uri inside @bookmark.
    3350                 :            :  *
    3351                 :            :  * In the event the URI cannot be found, %FALSE is returned and
    3352                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
    3353                 :            :  * In the event that no application with name @app_name has registered
    3354                 :            :  * a bookmark for @uri,  %FALSE is returned and error is set to
    3355                 :            :  * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED.
    3356                 :            :  *
    3357                 :            :  * Returns: %TRUE if the application was successfully removed.
    3358                 :            :  *
    3359                 :            :  * Since: 2.12
    3360                 :            :  */
    3361                 :            : gboolean
    3362                 :         14 : g_bookmark_file_remove_application (GBookmarkFile  *bookmark,
    3363                 :            :                                     const gchar    *uri,
    3364                 :            :                                     const gchar    *name,
    3365                 :            :                                     GError        **error)
    3366                 :            : {
    3367                 :            :   GError *set_error;
    3368                 :            :   gboolean retval;
    3369                 :            : 
    3370                 :         14 :   g_return_val_if_fail (bookmark != NULL, FALSE);
    3371                 :         13 :   g_return_val_if_fail (uri != NULL, FALSE);
    3372                 :         12 :   g_return_val_if_fail (name != NULL, FALSE);
    3373                 :            : 
    3374                 :         11 :   set_error = NULL;
    3375                 :         11 :   retval = g_bookmark_file_set_application_info (bookmark, uri,
    3376                 :            :                                                  name,
    3377                 :            :                                                  "",
    3378                 :            :                                                  0,
    3379                 :            :                                                  NULL,
    3380                 :            :                                                  &set_error);
    3381         [ +  + ]:         11 :   if (set_error)
    3382                 :            :     {
    3383                 :          1 :       g_propagate_error (error, set_error);
    3384                 :            : 
    3385                 :          1 :       return FALSE;
    3386                 :            :     }
    3387                 :            : 
    3388                 :         10 :   return retval;
    3389                 :            : }
    3390                 :            : 
    3391                 :            : /**
    3392                 :            :  * g_bookmark_file_has_application:
    3393                 :            :  * @bookmark: a #GBookmarkFile
    3394                 :            :  * @uri: a valid URI
    3395                 :            :  * @name: the name of the application
    3396                 :            :  * @error: return location for a #GError or %NULL
    3397                 :            :  *
    3398                 :            :  * Checks whether the bookmark for @uri inside @bookmark has been
    3399                 :            :  * registered by application @name.
    3400                 :            :  *
    3401                 :            :  * In the event the URI cannot be found, %FALSE is returned and
    3402                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
    3403                 :            :  *
    3404                 :            :  * Returns: %TRUE if the application @name was found
    3405                 :            :  *
    3406                 :            :  * Since: 2.12
    3407                 :            :  */
    3408                 :            : gboolean
    3409                 :         22 : g_bookmark_file_has_application (GBookmarkFile  *bookmark,
    3410                 :            :                                  const gchar    *uri,
    3411                 :            :                                  const gchar    *name,
    3412                 :            :                                  GError        **error)
    3413                 :            : {
    3414                 :            :   BookmarkItem *item;
    3415                 :            : 
    3416                 :         22 :   g_return_val_if_fail (bookmark != NULL, FALSE);
    3417                 :         21 :   g_return_val_if_fail (uri != NULL, FALSE);
    3418                 :         20 :   g_return_val_if_fail (name != NULL, FALSE);
    3419                 :            : 
    3420                 :         19 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    3421         [ +  + ]:         19 :   if (!item)
    3422                 :            :     {
    3423                 :          1 :       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                 :          1 :       return FALSE;
    3428                 :            :     }
    3429                 :            : 
    3430                 :         18 :   return (NULL != bookmark_item_lookup_app_info (item, name));
    3431                 :            : }
    3432                 :            : 
    3433                 :            : /**
    3434                 :            :  * g_bookmark_file_set_app_info:
    3435                 :            :  * @bookmark: a #GBookmarkFile
    3436                 :            :  * @uri: a valid URI
    3437                 :            :  * @name: an application's name
    3438                 :            :  * @exec: an application's command line
    3439                 :            :  * @count: the number of registrations done for this application
    3440                 :            :  * @stamp: the time of the last registration for this application
    3441                 :            :  * @error: return location for a #GError or %NULL
    3442                 :            :  *
    3443                 :            :  * Sets the meta-data of application @name inside the list of
    3444                 :            :  * applications that have registered a bookmark for @uri inside
    3445                 :            :  * @bookmark.
    3446                 :            :  *
    3447                 :            :  * You should rarely use this function; use g_bookmark_file_add_application()
    3448                 :            :  * and g_bookmark_file_remove_application() instead.
    3449                 :            :  *
    3450                 :            :  * @name can be any UTF-8 encoded string used to identify an
    3451                 :            :  * application.
    3452                 :            :  * @exec can have one of these two modifiers: "\%f", which will
    3453                 :            :  * be expanded as the local file name retrieved from the bookmark's
    3454                 :            :  * URI; "\%u", which will be expanded as the bookmark's URI.
    3455                 :            :  * The expansion is done automatically when retrieving the stored
    3456                 :            :  * command line using the g_bookmark_file_get_application_info() function.
    3457                 :            :  * @count is the number of times the application has registered the
    3458                 :            :  * bookmark; if is < 0, the current registration count will be increased
    3459                 :            :  * by one, if is 0, the application with @name will be removed from
    3460                 :            :  * the list of registered applications.
    3461                 :            :  * @stamp is the Unix time of the last registration; if it is -1, the
    3462                 :            :  * current time will be used.
    3463                 :            :  *
    3464                 :            :  * If you try to remove an application by setting its registration count to
    3465                 :            :  * zero, and no bookmark for @uri is found, %FALSE is returned and
    3466                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND; similarly,
    3467                 :            :  * in the event that no application @name has registered a bookmark
    3468                 :            :  * for @uri,  %FALSE is returned and error is set to
    3469                 :            :  * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED.  Otherwise, if no bookmark
    3470                 :            :  * for @uri is found, one is created.
    3471                 :            :  *
    3472                 :            :  * Returns: %TRUE if the application's meta-data was successfully
    3473                 :            :  *   changed.
    3474                 :            :  *
    3475                 :            :  * Since: 2.12
    3476                 :            :  * Deprecated: 2.66: Use g_bookmark_file_set_application_info() instead, as
    3477                 :            :  *    `time_t` is deprecated due to the year 2038 problem.
    3478                 :            :  */
    3479                 :            : gboolean
    3480                 :          2 : g_bookmark_file_set_app_info (GBookmarkFile  *bookmark,
    3481                 :            :                               const gchar    *uri,
    3482                 :            :                               const gchar    *name,
    3483                 :            :                               const gchar    *exec,
    3484                 :            :                               gint            count,
    3485                 :            :                               time_t          stamp,
    3486                 :            :                               GError        **error)
    3487                 :            : {
    3488         [ +  + ]:          2 :   GDateTime *stamp_dt = (stamp != (time_t) -1) ? g_date_time_new_from_unix_utc (stamp) : g_date_time_new_now_utc ();
    3489                 :            :   gboolean retval;
    3490                 :          2 :   retval = g_bookmark_file_set_application_info (bookmark, uri, name, exec, count,
    3491                 :            :                                                  stamp_dt, error);
    3492                 :          2 :   g_date_time_unref (stamp_dt);
    3493                 :          2 :   return retval;
    3494                 :            : }
    3495                 :            : 
    3496                 :            : /**
    3497                 :            :  * g_bookmark_file_set_application_info:
    3498                 :            :  * @bookmark: a #GBookmarkFile
    3499                 :            :  * @uri: a valid URI
    3500                 :            :  * @name: an application's name
    3501                 :            :  * @exec: an application's command line
    3502                 :            :  * @count: the number of registrations done for this application
    3503                 :            :  * @stamp: (nullable): the time of the last registration for this application,
    3504                 :            :  *    which may be %NULL if @count is 0
    3505                 :            :  * @error: return location for a #GError or %NULL
    3506                 :            :  *
    3507                 :            :  * Sets the meta-data of application @name inside the list of
    3508                 :            :  * applications that have registered a bookmark for @uri inside
    3509                 :            :  * @bookmark.
    3510                 :            :  *
    3511                 :            :  * You should rarely use this function; use g_bookmark_file_add_application()
    3512                 :            :  * and g_bookmark_file_remove_application() instead.
    3513                 :            :  *
    3514                 :            :  * @name can be any UTF-8 encoded string used to identify an
    3515                 :            :  * application.
    3516                 :            :  * @exec can have one of these two modifiers: "\%f", which will
    3517                 :            :  * be expanded as the local file name retrieved from the bookmark's
    3518                 :            :  * URI; "\%u", which will be expanded as the bookmark's URI.
    3519                 :            :  * The expansion is done automatically when retrieving the stored
    3520                 :            :  * command line using the g_bookmark_file_get_application_info() function.
    3521                 :            :  * @count is the number of times the application has registered the
    3522                 :            :  * bookmark; if is < 0, the current registration count will be increased
    3523                 :            :  * by one, if is 0, the application with @name will be removed from
    3524                 :            :  * the list of registered applications.
    3525                 :            :  * @stamp is the Unix time of the last registration.
    3526                 :            :  *
    3527                 :            :  * If you try to remove an application by setting its registration count to
    3528                 :            :  * zero, and no bookmark for @uri is found, %FALSE is returned and
    3529                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND; similarly,
    3530                 :            :  * in the event that no application @name has registered a bookmark
    3531                 :            :  * for @uri,  %FALSE is returned and error is set to
    3532                 :            :  * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED.  Otherwise, if no bookmark
    3533                 :            :  * for @uri is found, one is created.
    3534                 :            :  *
    3535                 :            :  * Returns: %TRUE if the application's meta-data was successfully
    3536                 :            :  *   changed.
    3537                 :            :  *
    3538                 :            :  * Since: 2.66
    3539                 :            :  */
    3540                 :            : gboolean
    3541                 :         41 : g_bookmark_file_set_application_info (GBookmarkFile  *bookmark,
    3542                 :            :                                       const char     *uri,
    3543                 :            :                                       const char     *name,
    3544                 :            :                                       const char     *exec,
    3545                 :            :                                       int             count,
    3546                 :            :                                       GDateTime      *stamp,
    3547                 :            :                                       GError        **error)
    3548                 :            : {
    3549                 :            :   BookmarkItem *item;
    3550                 :            :   BookmarkAppInfo *ai;
    3551                 :            : 
    3552                 :         41 :   g_return_val_if_fail (bookmark != NULL, FALSE);
    3553                 :         40 :   g_return_val_if_fail (uri != NULL, FALSE);
    3554                 :         39 :   g_return_val_if_fail (name != NULL, FALSE);
    3555                 :         38 :   g_return_val_if_fail (exec != NULL, FALSE);
    3556                 :         37 :   g_return_val_if_fail (count == 0 || stamp != NULL, FALSE);
    3557                 :         36 :   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
    3558                 :            : 
    3559                 :         36 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    3560         [ -  + ]:         36 :   if (!item)
    3561                 :            :     {
    3562         [ #  # ]:          0 :       if (count == 0)
    3563                 :            :         {
    3564                 :          0 :           g_set_error (error, G_BOOKMARK_FILE_ERROR,
    3565                 :            :                        G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
    3566                 :            :                        _("No bookmark found for URI “%s”"),
    3567                 :            :                        uri);
    3568                 :          0 :           return FALSE;
    3569                 :            :         }
    3570                 :            :       else
    3571                 :            :         {
    3572                 :          0 :           item = bookmark_item_new (uri);
    3573                 :          0 :           g_bookmark_file_add_item (bookmark, item, NULL);
    3574                 :            :         }
    3575                 :            :     }
    3576                 :            : 
    3577         [ +  + ]:         36 :   if (!item->metadata)
    3578                 :          3 :     item->metadata = bookmark_metadata_new ();
    3579                 :            : 
    3580                 :         36 :   ai = bookmark_item_lookup_app_info (item, name);
    3581         [ +  + ]:         36 :   if (!ai)
    3582                 :            :     {
    3583         [ +  + ]:         24 :       if (count == 0)
    3584                 :            :         {
    3585                 :          1 :           g_set_error (error, G_BOOKMARK_FILE_ERROR,
    3586                 :            :                        G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED,
    3587                 :            :                        _("No application with name “%s” registered a bookmark for “%s”"),
    3588                 :            :                        name,
    3589                 :            :                        uri);
    3590                 :          1 :           return FALSE;
    3591                 :            :         }
    3592                 :            :       else
    3593                 :            :         {
    3594                 :         23 :           ai = bookmark_app_info_new (name);
    3595                 :            : 
    3596                 :         23 :           item->metadata->applications = g_list_prepend (item->metadata->applications, ai);
    3597                 :         23 :           g_hash_table_replace (item->metadata->apps_by_name, ai->name, ai);
    3598                 :            :         }
    3599                 :            :     }
    3600                 :            : 
    3601         [ +  + ]:         35 :   if (count == 0)
    3602                 :            :     {
    3603                 :         10 :       item->metadata->applications = g_list_remove (item->metadata->applications, ai);
    3604                 :         10 :       g_hash_table_remove (item->metadata->apps_by_name, ai->name);
    3605                 :         10 :       bookmark_app_info_free (ai);
    3606                 :            : 
    3607                 :         10 :       bookmark_item_touch_modified (item);
    3608                 :            : 
    3609                 :         10 :       return TRUE;
    3610                 :            :     }
    3611         [ +  + ]:         25 :   else if (count > 0)
    3612                 :          2 :     ai->count = count;
    3613                 :            :   else
    3614                 :         23 :     ai->count += 1;
    3615                 :            : 
    3616                 :         25 :   g_clear_pointer (&ai->stamp, g_date_time_unref);
    3617                 :         25 :   ai->stamp = g_date_time_ref (stamp);
    3618                 :            : 
    3619   [ +  -  +  - ]:         25 :   if (exec && exec[0] != '\0')
    3620                 :            :     {
    3621                 :         25 :       g_free (ai->exec);
    3622                 :         25 :       ai->exec = g_shell_quote (exec);
    3623                 :            :     }
    3624                 :            : 
    3625                 :         25 :   bookmark_item_touch_modified (item);
    3626                 :            : 
    3627                 :         25 :   return TRUE;
    3628                 :            : }
    3629                 :            : 
    3630                 :            : /* expands the application's command line */
    3631                 :            : static gchar *
    3632                 :         10 : expand_exec_line (const gchar *exec_fmt,
    3633                 :            :                   const gchar *uri)
    3634                 :            : {
    3635                 :            :   GString *exec;
    3636                 :            :   gchar ch;
    3637                 :            : 
    3638                 :         10 :   exec = g_string_sized_new (512);
    3639         [ +  + ]:        232 :   while ((ch = *exec_fmt++) != '\0')
    3640                 :            :    {
    3641         [ +  + ]:        222 :      if (ch != '%')
    3642                 :            :        {
    3643         [ +  - ]:        212 :          exec = g_string_append_c (exec, ch);
    3644                 :        212 :          continue;
    3645                 :            :        }
    3646                 :            : 
    3647                 :         10 :      ch = *exec_fmt++;
    3648   [ -  +  +  - ]:         10 :      switch (ch)
    3649                 :            :        {
    3650                 :          0 :        case '\0':
    3651                 :          0 :          goto out;
    3652         [ -  + ]:          1 :        case 'U':
    3653                 :            :        case 'u':
    3654                 :            :          g_string_append (exec, uri);
    3655                 :          1 :          break;
    3656                 :          9 :        case 'F':
    3657                 :            :        case 'f':
    3658                 :            :          {
    3659                 :          9 :            gchar *file = g_filename_from_uri (uri, NULL, NULL);
    3660         [ +  - ]:          9 :            if (file)
    3661                 :            :              {
    3662                 :            :                g_string_append (exec, file);
    3663                 :          9 :                g_free (file);
    3664                 :            :              }
    3665                 :            :            else
    3666                 :            :              {
    3667                 :          0 :                g_string_free (exec, TRUE);
    3668                 :          0 :                return NULL;
    3669                 :            :              }
    3670                 :            :          }
    3671                 :          9 :          break;
    3672                 :          0 :        case '%':
    3673                 :            :        default:
    3674         [ #  # ]:          0 :          exec = g_string_append_c (exec, ch);
    3675                 :          0 :          break;
    3676                 :            :        }
    3677                 :            :    }
    3678                 :            : 
    3679                 :         10 :  out:
    3680                 :         10 :   return g_string_free (exec, FALSE);
    3681                 :            : }
    3682                 :            : 
    3683                 :            : /**
    3684                 :            :  * g_bookmark_file_get_app_info:
    3685                 :            :  * @bookmark: a #GBookmarkFile
    3686                 :            :  * @uri: a valid URI
    3687                 :            :  * @name: an application's name
    3688                 :            :  * @exec: (out) (optional): return location for the command line of the application, or %NULL
    3689                 :            :  * @count: (out) (optional): return location for the registration count, or %NULL
    3690                 :            :  * @stamp: (out) (optional): return location for the last registration time, or %NULL
    3691                 :            :  * @error: return location for a #GError, or %NULL
    3692                 :            :  *
    3693                 :            :  * Gets the registration information of @app_name for the bookmark for
    3694                 :            :  * @uri.  See g_bookmark_file_set_application_info() for more information about
    3695                 :            :  * the returned data.
    3696                 :            :  *
    3697                 :            :  * The string returned in @app_exec must be freed.
    3698                 :            :  *
    3699                 :            :  * In the event the URI cannot be found, %FALSE is returned and
    3700                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.  In the
    3701                 :            :  * event that no application with name @app_name has registered a bookmark
    3702                 :            :  * for @uri,  %FALSE is returned and error is set to
    3703                 :            :  * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. In the event that unquoting
    3704                 :            :  * the command line fails, an error of the %G_SHELL_ERROR domain is
    3705                 :            :  * set and %FALSE is returned.
    3706                 :            :  *
    3707                 :            :  * Returns: %TRUE on success.
    3708                 :            :  *
    3709                 :            :  * Since: 2.12
    3710                 :            :  * Deprecated: 2.66: Use g_bookmark_file_get_application_info() instead, as
    3711                 :            :  *    `time_t` is deprecated due to the year 2038 problem.
    3712                 :            :  */
    3713                 :            : gboolean
    3714                 :          4 : g_bookmark_file_get_app_info (GBookmarkFile  *bookmark,
    3715                 :            :                               const gchar    *uri,
    3716                 :            :                               const gchar    *name,
    3717                 :            :                               gchar         **exec,
    3718                 :            :                               guint          *count,
    3719                 :            :                               time_t         *stamp,
    3720                 :            :                               GError        **error)
    3721                 :            : {
    3722                 :          4 :   GDateTime *stamp_dt = NULL;
    3723                 :            :   gboolean retval;
    3724                 :            : 
    3725                 :          4 :   retval = g_bookmark_file_get_application_info (bookmark, uri, name, exec, count, &stamp_dt, error);
    3726         [ +  + ]:          4 :   if (!retval)
    3727                 :          1 :     return FALSE;
    3728                 :            : 
    3729         [ +  + ]:          3 :   if (stamp != NULL)
    3730                 :          2 :     *stamp = g_date_time_to_unix (stamp_dt);
    3731                 :            : 
    3732                 :          3 :   return TRUE;
    3733                 :            : }
    3734                 :            : 
    3735                 :            : /**
    3736                 :            :  * g_bookmark_file_get_application_info:
    3737                 :            :  * @bookmark: a #GBookmarkFile
    3738                 :            :  * @uri: a valid URI
    3739                 :            :  * @name: an application's name
    3740                 :            :  * @exec: (out) (optional): return location for the command line of the application, or %NULL
    3741                 :            :  * @count: (out) (optional): return location for the registration count, or %NULL
    3742                 :            :  * @stamp: (out) (optional) (transfer none): return location for the last registration time, or %NULL
    3743                 :            :  * @error: return location for a #GError, or %NULL
    3744                 :            :  *
    3745                 :            :  * Gets the registration information of @app_name for the bookmark for
    3746                 :            :  * @uri.  See g_bookmark_file_set_application_info() for more information about
    3747                 :            :  * the returned data.
    3748                 :            :  *
    3749                 :            :  * The string returned in @app_exec must be freed.
    3750                 :            :  *
    3751                 :            :  * In the event the URI cannot be found, %FALSE is returned and
    3752                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.  In the
    3753                 :            :  * event that no application with name @app_name has registered a bookmark
    3754                 :            :  * for @uri,  %FALSE is returned and error is set to
    3755                 :            :  * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. In the event that unquoting
    3756                 :            :  * the command line fails, an error of the %G_SHELL_ERROR domain is
    3757                 :            :  * set and %FALSE is returned.
    3758                 :            :  *
    3759                 :            :  * Returns: %TRUE on success.
    3760                 :            :  *
    3761                 :            :  * Since: 2.66
    3762                 :            :  */
    3763                 :            : gboolean
    3764                 :         28 : g_bookmark_file_get_application_info (GBookmarkFile  *bookmark,
    3765                 :            :                                       const char     *uri,
    3766                 :            :                                       const char     *name,
    3767                 :            :                                       char          **exec,
    3768                 :            :                                       unsigned int   *count,
    3769                 :            :                                       GDateTime     **stamp,
    3770                 :            :                                       GError        **error)
    3771                 :            : {
    3772                 :            :   BookmarkItem *item;
    3773                 :            :   BookmarkAppInfo *ai;
    3774                 :            : 
    3775                 :         28 :   g_return_val_if_fail (bookmark != NULL, FALSE);
    3776                 :         27 :   g_return_val_if_fail (uri != NULL, FALSE);
    3777                 :         26 :   g_return_val_if_fail (name != NULL, FALSE);
    3778                 :         25 :   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
    3779                 :            : 
    3780                 :         25 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    3781         [ +  + ]:         25 :   if (!item)
    3782                 :            :     {
    3783                 :          1 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    3784                 :            :                    G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
    3785                 :            :                    _("No bookmark found for URI “%s”"),
    3786                 :            :                    uri);
    3787                 :          1 :       return FALSE;
    3788                 :            :     }
    3789                 :            : 
    3790                 :         24 :   ai = bookmark_item_lookup_app_info (item, name);
    3791         [ +  + ]:         24 :   if (!ai)
    3792                 :            :     {
    3793                 :         11 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    3794                 :            :                    G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED,
    3795                 :            :                    _("No application with name “%s” registered a bookmark for “%s”"),
    3796                 :            :                    name,
    3797                 :            :                    uri);
    3798                 :         11 :       return FALSE;
    3799                 :            :     }
    3800                 :            : 
    3801         [ +  + ]:         13 :   if (exec)
    3802                 :            :     {
    3803                 :         10 :       GError *unquote_error = NULL;
    3804                 :            :       gchar *command_line;
    3805                 :            : 
    3806                 :         10 :       command_line = g_shell_unquote (ai->exec, &unquote_error);
    3807         [ -  + ]:         10 :       if (unquote_error)
    3808                 :            :         {
    3809                 :          0 :           g_propagate_error (error, unquote_error);
    3810                 :          0 :           return FALSE;
    3811                 :            :         }
    3812                 :            : 
    3813                 :         10 :       *exec = expand_exec_line (command_line, uri);
    3814         [ -  + ]:         10 :       if (!*exec)
    3815                 :            :         {
    3816                 :          0 :           g_set_error (error, G_BOOKMARK_FILE_ERROR,
    3817                 :            :                        G_BOOKMARK_FILE_ERROR_INVALID_URI,
    3818                 :            :                        _("Failed to expand exec line “%s” with URI “%s”"),
    3819                 :            :                      ai->exec, uri);
    3820                 :          0 :           g_free (command_line);
    3821                 :            : 
    3822                 :          0 :           return FALSE;
    3823                 :            :         }
    3824                 :            :       else
    3825                 :         10 :         g_free (command_line);
    3826                 :            :     }
    3827                 :            : 
    3828         [ +  + ]:         13 :   if (count)
    3829                 :         10 :     *count = ai->count;
    3830                 :            : 
    3831         [ +  - ]:         13 :   if (stamp)
    3832                 :         13 :     *stamp = ai->stamp;
    3833                 :            : 
    3834                 :         13 :   return TRUE;
    3835                 :            : }
    3836                 :            : 
    3837                 :            : /**
    3838                 :            :  * g_bookmark_file_get_applications:
    3839                 :            :  * @bookmark: a #GBookmarkFile
    3840                 :            :  * @uri: a valid URI
    3841                 :            :  * @length: (out) (optional): return location of the length of the returned list, or %NULL
    3842                 :            :  * @error: return location for a #GError, or %NULL
    3843                 :            :  *
    3844                 :            :  * Retrieves the names of the applications that have registered the
    3845                 :            :  * bookmark for @uri.
    3846                 :            :  *
    3847                 :            :  * In the event the URI cannot be found, %NULL is returned and
    3848                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
    3849                 :            :  *
    3850                 :            :  * Returns: (array length=length) (transfer full): a newly allocated %NULL-terminated array of strings.
    3851                 :            :  *   Use g_strfreev() to free it.
    3852                 :            :  *
    3853                 :            :  * Since: 2.12
    3854                 :            :  */
    3855                 :            : gchar **
    3856                 :         11 : g_bookmark_file_get_applications (GBookmarkFile  *bookmark,
    3857                 :            :                                   const gchar    *uri,
    3858                 :            :                                   gsize          *length,
    3859                 :            :                                   GError        **error)
    3860                 :            : {
    3861                 :            :   BookmarkItem *item;
    3862                 :            :   GList *l;
    3863                 :            :   gchar **apps;
    3864                 :            :   gsize i, n_apps;
    3865                 :            : 
    3866                 :         11 :   g_return_val_if_fail (bookmark != NULL, NULL);
    3867                 :         10 :   g_return_val_if_fail (uri != NULL, NULL);
    3868                 :            : 
    3869                 :          9 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    3870         [ -  + ]:          9 :   if (!item)
    3871                 :            :     {
    3872                 :          0 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    3873                 :            :                    G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
    3874                 :            :                    _("No bookmark found for URI “%s”"),
    3875                 :            :                    uri);
    3876                 :          0 :       return NULL;
    3877                 :            :     }
    3878                 :            : 
    3879         [ -  + ]:          9 :   if (!item->metadata)
    3880                 :            :     {
    3881         [ #  # ]:          0 :       if (length)
    3882                 :          0 :         *length = 0;
    3883                 :            : 
    3884                 :          0 :       return NULL;
    3885                 :            :     }
    3886                 :            : 
    3887                 :          9 :   n_apps = g_list_length (item->metadata->applications);
    3888                 :          9 :   apps = g_new0 (gchar *, n_apps + 1);
    3889                 :            : 
    3890                 :          9 :   for (l = g_list_last (item->metadata->applications), i = 0;
    3891         [ +  + ]:         18 :        l != NULL;
    3892                 :          9 :        l = l->prev)
    3893                 :            :     {
    3894                 :            :       BookmarkAppInfo *ai;
    3895                 :            : 
    3896                 :          9 :       ai = (BookmarkAppInfo *) l->data;
    3897                 :            : 
    3898         [ -  + ]:          9 :       g_warn_if_fail (ai != NULL);
    3899         [ -  + ]:          9 :       g_warn_if_fail (ai->name != NULL);
    3900                 :            : 
    3901                 :         18 :       apps[i++] = g_strdup (ai->name);
    3902                 :            :     }
    3903                 :          9 :   apps[i] = NULL;
    3904                 :            : 
    3905         [ +  - ]:          9 :   if (length)
    3906                 :          9 :     *length = i;
    3907                 :            : 
    3908                 :          9 :   return apps;
    3909                 :            : }
    3910                 :            : 
    3911                 :            : /**
    3912                 :            :  * g_bookmark_file_get_size:
    3913                 :            :  * @bookmark: a #GBookmarkFile
    3914                 :            :  *
    3915                 :            :  * Gets the number of bookmarks inside @bookmark.
    3916                 :            :  *
    3917                 :            :  * Returns: the number of bookmarks
    3918                 :            :  *
    3919                 :            :  * Since: 2.12
    3920                 :            :  */
    3921                 :            : gint
    3922                 :          4 : g_bookmark_file_get_size (GBookmarkFile *bookmark)
    3923                 :            : {
    3924                 :          4 :   g_return_val_if_fail (bookmark != NULL, 0);
    3925                 :            : 
    3926                 :          3 :   return g_list_length (bookmark->items);
    3927                 :            : }
    3928                 :            : 
    3929                 :            : /**
    3930                 :            :  * g_bookmark_file_move_item:
    3931                 :            :  * @bookmark: a #GBookmarkFile
    3932                 :            :  * @old_uri: a valid URI
    3933                 :            :  * @new_uri: (nullable): a valid URI, or %NULL
    3934                 :            :  * @error: return location for a #GError or %NULL
    3935                 :            :  *
    3936                 :            :  * Changes the URI of a bookmark item from @old_uri to @new_uri.  Any
    3937                 :            :  * existing bookmark for @new_uri will be overwritten.  If @new_uri is
    3938                 :            :  * %NULL, then the bookmark is removed.
    3939                 :            :  *
    3940                 :            :  * In the event the URI cannot be found, %FALSE is returned and
    3941                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
    3942                 :            :  *
    3943                 :            :  * Returns: %TRUE if the URI was successfully changed
    3944                 :            :  *
    3945                 :            :  * Since: 2.12
    3946                 :            :  */
    3947                 :            : gboolean
    3948                 :          9 : g_bookmark_file_move_item (GBookmarkFile  *bookmark,
    3949                 :            :                            const gchar    *old_uri,
    3950                 :            :                            const gchar    *new_uri,
    3951                 :            :                            GError        **error)
    3952                 :            : {
    3953                 :            :   BookmarkItem *item;
    3954                 :            : 
    3955                 :          9 :   g_return_val_if_fail (bookmark != NULL, FALSE);
    3956                 :          8 :   g_return_val_if_fail (old_uri != NULL, FALSE);
    3957                 :            : 
    3958                 :          7 :   item = g_bookmark_file_lookup_item (bookmark, old_uri);
    3959         [ +  + ]:          7 :   if (!item)
    3960                 :            :     {
    3961                 :          3 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    3962                 :            :                    G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
    3963                 :            :                    _("No bookmark found for URI “%s”"),
    3964                 :            :                    old_uri);
    3965                 :          3 :       return FALSE;
    3966                 :            :     }
    3967                 :            : 
    3968   [ +  +  +  - ]:          4 :   if (new_uri && new_uri[0] != '\0')
    3969                 :            :     {
    3970         [ +  + ]:          2 :       if (g_strcmp0 (old_uri, new_uri) == 0)
    3971                 :          1 :         return TRUE;
    3972                 :            : 
    3973         [ -  + ]:          1 :       if (g_bookmark_file_has_item (bookmark, new_uri))
    3974                 :            :         {
    3975         [ #  # ]:          0 :           if (!g_bookmark_file_remove_item (bookmark, new_uri, error))
    3976                 :          0 :             return FALSE;
    3977                 :            :         }
    3978                 :            : 
    3979                 :          1 :       g_hash_table_steal (bookmark->items_by_uri, item->uri);
    3980                 :            : 
    3981                 :          1 :       g_free (item->uri);
    3982                 :          1 :       item->uri = g_strdup (new_uri);
    3983                 :          1 :       bookmark_item_touch_modified (item);
    3984                 :            : 
    3985                 :          1 :       g_hash_table_replace (bookmark->items_by_uri, item->uri, item);
    3986                 :            : 
    3987                 :          1 :       return TRUE;
    3988                 :            :     }
    3989                 :            :   else
    3990                 :            :     {
    3991         [ -  + ]:          2 :       if (!g_bookmark_file_remove_item (bookmark, old_uri, error))
    3992                 :          0 :         return FALSE;
    3993                 :            : 
    3994                 :          2 :       return TRUE;
    3995                 :            :     }
    3996                 :            : }
    3997                 :            : 
    3998                 :            : /**
    3999                 :            :  * g_bookmark_file_set_icon:
    4000                 :            :  * @bookmark: a #GBookmarkFile
    4001                 :            :  * @uri: a valid URI
    4002                 :            :  * @href: (nullable): the URI of the icon for the bookmark, or %NULL
    4003                 :            :  * @mime_type: the MIME type of the icon for the bookmark
    4004                 :            :  *
    4005                 :            :  * Sets the icon for the bookmark for @uri. If @href is %NULL, unsets
    4006                 :            :  * the currently set icon. @href can either be a full URL for the icon
    4007                 :            :  * file or the icon name following the Icon Naming specification.
    4008                 :            :  *
    4009                 :            :  * If no bookmark for @uri is found one is created.
    4010                 :            :  *
    4011                 :            :  * Since: 2.12
    4012                 :            :  */
    4013                 :            : void
    4014                 :         13 : g_bookmark_file_set_icon (GBookmarkFile *bookmark,
    4015                 :            :                           const gchar   *uri,
    4016                 :            :                           const gchar   *href,
    4017                 :            :                           const gchar   *mime_type)
    4018                 :            : {
    4019                 :            :   BookmarkItem *item;
    4020                 :            : 
    4021                 :         13 :   g_return_if_fail (bookmark != NULL);
    4022                 :         12 :   g_return_if_fail (uri != NULL);
    4023                 :            : 
    4024                 :         11 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    4025         [ +  + ]:         11 :   if (!item)
    4026                 :            :     {
    4027                 :          1 :       item = bookmark_item_new (uri);
    4028                 :          1 :       g_bookmark_file_add_item (bookmark, item, NULL);
    4029                 :            :     }
    4030                 :            : 
    4031         [ +  + ]:         11 :   if (!item->metadata)
    4032                 :          1 :     item->metadata = bookmark_metadata_new ();
    4033                 :            : 
    4034                 :         11 :   g_free (item->metadata->icon_href);
    4035                 :         11 :   g_free (item->metadata->icon_mime);
    4036                 :            : 
    4037                 :         11 :   item->metadata->icon_href = g_strdup (href);
    4038                 :            : 
    4039   [ +  +  +  - ]:         11 :   if (mime_type && mime_type[0] != '\0')
    4040                 :         20 :     item->metadata->icon_mime = g_strdup (mime_type);
    4041                 :            :   else
    4042                 :          2 :     item->metadata->icon_mime = g_strdup ("application/octet-stream");
    4043                 :            : 
    4044                 :         11 :   bookmark_item_touch_modified (item);
    4045                 :            : }
    4046                 :            : 
    4047                 :            : /**
    4048                 :            :  * g_bookmark_file_get_icon:
    4049                 :            :  * @bookmark: a #GBookmarkFile
    4050                 :            :  * @uri: a valid URI
    4051                 :            :  * @href: (out) (optional): return location for the icon's location or %NULL
    4052                 :            :  * @mime_type: (out) (optional): return location for the icon's MIME type or %NULL
    4053                 :            :  * @error: return location for a #GError or %NULL
    4054                 :            :  *
    4055                 :            :  * Gets the icon of the bookmark for @uri.
    4056                 :            :  *
    4057                 :            :  * In the event the URI cannot be found, %FALSE is returned and
    4058                 :            :  * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
    4059                 :            :  *
    4060                 :            :  * Returns: %TRUE if the icon for the bookmark for the URI was found.
    4061                 :            :  *   You should free the returned strings.
    4062                 :            :  *
    4063                 :            :  * Since: 2.12
    4064                 :            :  */
    4065                 :            : gboolean
    4066                 :         15 : g_bookmark_file_get_icon (GBookmarkFile  *bookmark,
    4067                 :            :                           const gchar    *uri,
    4068                 :            :                           gchar         **href,
    4069                 :            :                           gchar         **mime_type,
    4070                 :            :                           GError        **error)
    4071                 :            : {
    4072                 :            :   BookmarkItem *item;
    4073                 :            : 
    4074                 :         15 :   g_return_val_if_fail (bookmark != NULL, FALSE);
    4075                 :         14 :   g_return_val_if_fail (uri != NULL, FALSE);
    4076                 :            : 
    4077                 :         13 :   item = g_bookmark_file_lookup_item (bookmark, uri);
    4078         [ +  + ]:         13 :   if (!item)
    4079                 :            :     {
    4080                 :          1 :       g_set_error (error, G_BOOKMARK_FILE_ERROR,
    4081                 :            :                    G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
    4082                 :            :                    _("No bookmark found for URI “%s”"),
    4083                 :            :                    uri);
    4084                 :          1 :       return FALSE;
    4085                 :            :     }
    4086                 :            : 
    4087   [ +  -  +  + ]:         12 :   if ((!item->metadata) || (!item->metadata->icon_href))
    4088                 :          2 :     return FALSE;
    4089                 :            : 
    4090         [ +  - ]:         10 :   if (href)
    4091                 :         20 :     *href = g_strdup (item->metadata->icon_href);
    4092                 :            : 
    4093         [ +  + ]:         10 :   if (mime_type)
    4094                 :         18 :     *mime_type = g_strdup (item->metadata->icon_mime);
    4095                 :            : 
    4096                 :         10 :   return TRUE;
    4097                 :            : }

Generated by: LCOV version 1.14