LCOV - code coverage report
Current view: top level - glib/gio - gcontenttype.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 499 572 87.2 %
Date: 2024-04-16 05:15:53 Functions: 46 49 93.9 %
Branches: 196 262 74.8 %

           Branch data     Line data    Source code
       1                 :            : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
       2                 :            : 
       3                 :            : /* GIO - GLib Input, Output and Streaming Library
       4                 :            :  *
       5                 :            :  * Copyright (C) 2006-2007 Red Hat, Inc.
       6                 :            :  *
       7                 :            :  * SPDX-License-Identifier: LGPL-2.1-or-later
       8                 :            :  *
       9                 :            :  * This library is free software; you can redistribute it and/or
      10                 :            :  * modify it under the terms of the GNU Lesser General Public
      11                 :            :  * License as published by the Free Software Foundation; either
      12                 :            :  * version 2.1 of the License, or (at your option) any later version.
      13                 :            :  *
      14                 :            :  * This library is distributed in the hope that it will be useful,
      15                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      17                 :            :  * Lesser General Public License for more details.
      18                 :            :  *
      19                 :            :  * You should have received a copy of the GNU Lesser General
      20                 :            :  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      21                 :            :  *
      22                 :            :  * Author: Alexander Larsson <alexl@redhat.com>
      23                 :            :  */
      24                 :            : 
      25                 :            : #include "config.h"
      26                 :            : #include <sys/types.h>
      27                 :            : #include <stdlib.h>
      28                 :            : #include <string.h>
      29                 :            : #include <stdio.h>
      30                 :            : #include "gcontenttypeprivate.h"
      31                 :            : #include "gthemedicon.h"
      32                 :            : #include "gicon.h"
      33                 :            : #include "gfile.h"
      34                 :            : #include "gfileenumerator.h"
      35                 :            : #include "gfileinfo.h"
      36                 :            : #include "glibintl.h"
      37                 :            : #include "glib-private.h"
      38                 :            : 
      39                 :            : 
      40                 :            : /**
      41                 :            :  * GContentType:
      42                 :            :  *
      43                 :            :  * A content type is a platform specific string that defines the type
      44                 :            :  * of a file. On UNIX it is a
      45                 :            :  * [MIME type](http://www.wikipedia.org/wiki/Internet_media_type)
      46                 :            :  * like `text/plain` or `image/png`.
      47                 :            :  * On Win32 it is an extension string like `.doc`, `.txt` or a perceived
      48                 :            :  * string like `audio`. Such strings can be looked up in the registry at
      49                 :            :  * `HKEY_CLASSES_ROOT`.
      50                 :            :  * On macOS it is a [Uniform Type Identifier](https://en.wikipedia.org/wiki/Uniform_Type_Identifier)
      51                 :            :  * such as `com.apple.application`.
      52                 :            :  **/
      53                 :            : 
      54                 :            : #include <dirent.h>
      55                 :            : 
      56                 :            : #define XDG_PREFIX _gio_xdg
      57                 :            : #include "xdgmime/xdgmime.h"
      58                 :            : 
      59                 :            : static void tree_magic_schedule_reload (void);
      60                 :            : 
      61                 :            : /* We lock this mutex whenever we modify global state in this module.
      62                 :            :  * Taking and releasing this lock should always be associated with a pair of
      63                 :            :  * g_begin_ignore_leaks()/g_end_ignore_leaks() calls, as any call into xdgmime
      64                 :            :  * could trigger xdg_mime_init(), which makes a number of one-time allocations
      65                 :            :  * which GLib can never free as it doesn’t know when is suitable to call
      66                 :            :  * xdg_mime_shutdown(). */
      67                 :            : G_LOCK_DEFINE_STATIC (gio_xdgmime);
      68                 :            : 
      69                 :            : gsize
      70                 :         10 : _g_unix_content_type_get_sniff_len (void)
      71                 :            : {
      72                 :            :   gsize size;
      73                 :            : 
      74                 :         10 :   G_LOCK (gio_xdgmime);
      75                 :         10 :   g_begin_ignore_leaks ();
      76                 :         10 :   size = xdg_mime_get_max_buffer_extents ();
      77                 :         10 :   g_end_ignore_leaks ();
      78                 :         10 :   G_UNLOCK (gio_xdgmime);
      79                 :            : 
      80                 :         10 :   return size;
      81                 :            : }
      82                 :            : 
      83                 :            : gchar *
      84                 :      10091 : _g_unix_content_type_unalias (const gchar *type)
      85                 :            : {
      86                 :            :   gchar *res;
      87                 :            : 
      88                 :      10091 :   G_LOCK (gio_xdgmime);
      89                 :      10091 :   g_begin_ignore_leaks ();
      90                 :      10091 :   res = g_strdup (xdg_mime_unalias_mime_type (type));
      91                 :      10091 :   g_end_ignore_leaks ();
      92                 :      10091 :   G_UNLOCK (gio_xdgmime);
      93                 :            : 
      94                 :      10091 :   return res;
      95                 :            : }
      96                 :            : 
      97                 :            : gchar **
      98                 :         69 : _g_unix_content_type_get_parents (const gchar *type)
      99                 :            : {
     100                 :            :   const gchar *umime;
     101                 :            :   gchar **parents;
     102                 :            :   GPtrArray *array;
     103                 :            :   int i;
     104                 :            : 
     105                 :         69 :   array = g_ptr_array_new ();
     106                 :            : 
     107                 :         69 :   G_LOCK (gio_xdgmime);
     108                 :         69 :   g_begin_ignore_leaks ();
     109                 :            : 
     110                 :         69 :   umime = xdg_mime_unalias_mime_type (type);
     111                 :            : 
     112                 :         69 :   g_ptr_array_add (array, g_strdup (umime));
     113                 :            : 
     114                 :         69 :   parents = xdg_mime_list_mime_parents (umime);
     115   [ +  +  +  + ]:         75 :   for (i = 0; parents && parents[i] != NULL; i++)
     116                 :         12 :     g_ptr_array_add (array, g_strdup (parents[i]));
     117                 :            : 
     118                 :         69 :   free (parents);
     119                 :            : 
     120                 :         69 :   g_end_ignore_leaks ();
     121                 :         69 :   G_UNLOCK (gio_xdgmime);
     122                 :            : 
     123                 :         69 :   g_ptr_array_add (array, NULL);
     124                 :            : 
     125                 :         69 :   return (gchar **)g_ptr_array_free (array, FALSE);
     126                 :            : }
     127                 :            : 
     128                 :            : G_LOCK_DEFINE_STATIC (global_mime_dirs);
     129                 :            : static gchar **global_mime_dirs = NULL;
     130                 :            : 
     131                 :            : static void
     132                 :          2 : _g_content_type_set_mime_dirs_locked (const char * const *dirs)
     133                 :            : {
     134                 :          2 :   g_clear_pointer (&global_mime_dirs, g_strfreev);
     135                 :            : 
     136         [ -  + ]:          2 :   if (dirs != NULL)
     137                 :            :     {
     138                 :          0 :       global_mime_dirs = g_strdupv ((gchar **) dirs);
     139                 :            :     }
     140                 :            :   else
     141                 :            :     {
     142                 :          2 :       GPtrArray *mime_dirs = g_ptr_array_new_with_free_func (g_free);
     143                 :          2 :       const gchar * const *system_dirs = g_get_system_data_dirs ();
     144                 :            : 
     145                 :          2 :       g_ptr_array_add (mime_dirs, g_build_filename (g_get_user_data_dir (), "mime", NULL));
     146         [ +  + ]:          6 :       for (; *system_dirs != NULL; system_dirs++)
     147                 :          4 :         g_ptr_array_add (mime_dirs, g_build_filename (*system_dirs, "mime", NULL));
     148                 :          2 :       g_ptr_array_add (mime_dirs, NULL);  /* NULL terminator */
     149                 :            : 
     150                 :          2 :       global_mime_dirs = (gchar **) g_ptr_array_free (mime_dirs, FALSE);
     151                 :            :     }
     152                 :            : 
     153                 :          2 :   xdg_mime_set_dirs ((const gchar * const *) global_mime_dirs);
     154                 :          2 :   tree_magic_schedule_reload ();
     155                 :          2 : }
     156                 :            : 
     157                 :            : /**
     158                 :            :  * g_content_type_set_mime_dirs:
     159                 :            :  * @dirs: (array zero-terminated=1) (nullable): %NULL-terminated list of
     160                 :            :  *    directories to load MIME data from, including any `mime/` subdirectory,
     161                 :            :  *    and with the first directory to try listed first
     162                 :            :  *
     163                 :            :  * Set the list of directories used by GIO to load the MIME database.
     164                 :            :  * If @dirs is %NULL, the directories used are the default:
     165                 :            :  *
     166                 :            :  *  - the `mime` subdirectory of the directory in `$XDG_DATA_HOME`
     167                 :            :  *  - the `mime` subdirectory of every directory in `$XDG_DATA_DIRS`
     168                 :            :  *
     169                 :            :  * This function is intended to be used when writing tests that depend on
     170                 :            :  * information stored in the MIME database, in order to control the data.
     171                 :            :  *
     172                 :            :  * Typically, in case your tests use %G_TEST_OPTION_ISOLATE_DIRS, but they
     173                 :            :  * depend on the system’s MIME database, you should call this function
     174                 :            :  * with @dirs set to %NULL before calling g_test_init(), for instance:
     175                 :            :  *
     176                 :            :  * |[<!-- language="C" -->
     177                 :            :  *   // Load MIME data from the system
     178                 :            :  *   g_content_type_set_mime_dirs (NULL);
     179                 :            :  *   // Isolate the environment
     180                 :            :  *   g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
     181                 :            :  *
     182                 :            :  *   …
     183                 :            :  *
     184                 :            :  *   return g_test_run ();
     185                 :            :  * ]|
     186                 :            :  *
     187                 :            :  * Since: 2.60
     188                 :            :  */
     189                 :            : /*< private >*/
     190                 :            : void
     191                 :          1 : g_content_type_set_mime_dirs (const gchar * const *dirs)
     192                 :            : {
     193                 :          1 :   G_LOCK (global_mime_dirs);
     194                 :          1 :   _g_content_type_set_mime_dirs_locked (dirs);
     195                 :          1 :   G_UNLOCK (global_mime_dirs);
     196                 :          1 : }
     197                 :            : 
     198                 :            : /**
     199                 :            :  * g_content_type_get_mime_dirs:
     200                 :            :  *
     201                 :            :  * Get the list of directories which MIME data is loaded from. See
     202                 :            :  * g_content_type_set_mime_dirs() for details.
     203                 :            :  *
     204                 :            :  * Returns: (transfer none) (array zero-terminated=1): %NULL-terminated list of
     205                 :            :  *    directories to load MIME data from, including any `mime/` subdirectory,
     206                 :            :  *    and with the first directory to try listed first
     207                 :            :  * Since: 2.60
     208                 :            :  */
     209                 :            : /*< private >*/
     210                 :            : const gchar * const *
     211                 :          3 : g_content_type_get_mime_dirs (void)
     212                 :            : {
     213                 :            :   const gchar * const *mime_dirs;
     214                 :            : 
     215                 :          3 :   G_LOCK (global_mime_dirs);
     216                 :            : 
     217         [ +  + ]:          3 :   if (global_mime_dirs == NULL)
     218                 :          1 :     _g_content_type_set_mime_dirs_locked (NULL);
     219                 :            : 
     220                 :          3 :   mime_dirs = (const gchar * const *) global_mime_dirs;
     221                 :            : 
     222                 :          3 :   G_UNLOCK (global_mime_dirs);
     223                 :            : 
     224                 :          3 :   g_assert (mime_dirs != NULL);
     225                 :          3 :   return mime_dirs;
     226                 :            : }
     227                 :            : 
     228                 :            : /**
     229                 :            :  * g_content_type_equals:
     230                 :            :  * @type1: a content type string
     231                 :            :  * @type2: a content type string
     232                 :            :  *
     233                 :            :  * Compares two content types for equality.
     234                 :            :  *
     235                 :            :  * Returns: %TRUE if the two strings are identical or equivalent,
     236                 :            :  *     %FALSE otherwise.
     237                 :            :  */
     238                 :            : gboolean
     239                 :        370 : g_content_type_equals (const gchar *type1,
     240                 :            :                        const gchar *type2)
     241                 :            : {
     242                 :            :   gboolean res;
     243                 :            : 
     244                 :        370 :   g_return_val_if_fail (type1 != NULL, FALSE);
     245                 :        370 :   g_return_val_if_fail (type2 != NULL, FALSE);
     246                 :            : 
     247                 :        370 :   G_LOCK (gio_xdgmime);
     248                 :        370 :   g_begin_ignore_leaks ();
     249                 :        370 :   res = xdg_mime_mime_type_equal (type1, type2);
     250                 :        370 :   g_end_ignore_leaks ();
     251                 :        370 :   G_UNLOCK (gio_xdgmime);
     252                 :            : 
     253                 :        370 :   return res;
     254                 :            : }
     255                 :            : 
     256                 :            : /**
     257                 :            :  * g_content_type_is_a:
     258                 :            :  * @type: a content type string
     259                 :            :  * @supertype: a content type string
     260                 :            :  *
     261                 :            :  * Determines if @type is a subset of @supertype.
     262                 :            :  *
     263                 :            :  * Returns: %TRUE if @type is a kind of @supertype,
     264                 :            :  *     %FALSE otherwise.
     265                 :            :  */
     266                 :            : gboolean
     267                 :         10 : g_content_type_is_a (const gchar *type,
     268                 :            :                      const gchar *supertype)
     269                 :            : {
     270                 :            :   gboolean res;
     271                 :            : 
     272                 :         10 :   g_return_val_if_fail (type != NULL, FALSE);
     273                 :         10 :   g_return_val_if_fail (supertype != NULL, FALSE);
     274                 :            : 
     275                 :         10 :   G_LOCK (gio_xdgmime);
     276                 :         10 :   g_begin_ignore_leaks ();
     277                 :         10 :   res = xdg_mime_mime_type_subclass (type, supertype);
     278                 :         10 :   g_end_ignore_leaks ();
     279                 :         10 :   G_UNLOCK (gio_xdgmime);
     280                 :            : 
     281                 :         10 :   return res;
     282                 :            : }
     283                 :            : 
     284                 :            : /**
     285                 :            :  * g_content_type_is_mime_type:
     286                 :            :  * @type: a content type string
     287                 :            :  * @mime_type: a mime type string
     288                 :            :  *
     289                 :            :  * Determines if @type is a subset of @mime_type.
     290                 :            :  * Convenience wrapper around g_content_type_is_a().
     291                 :            :  *
     292                 :            :  * Returns: %TRUE if @type is a kind of @mime_type,
     293                 :            :  *     %FALSE otherwise.
     294                 :            :  *
     295                 :            :  * Since: 2.52
     296                 :            :  */
     297                 :            : gboolean
     298                 :          1 : g_content_type_is_mime_type (const gchar *type,
     299                 :            :                              const gchar *mime_type)
     300                 :            : {
     301                 :          1 :   return g_content_type_is_a (type, mime_type);
     302                 :            : }
     303                 :            : 
     304                 :            : /**
     305                 :            :  * g_content_type_is_unknown:
     306                 :            :  * @type: a content type string
     307                 :            :  *
     308                 :            :  * Checks if the content type is the generic "unknown" type.
     309                 :            :  * On UNIX this is the "application/octet-stream" mimetype,
     310                 :            :  * while on win32 it is "*" and on OSX it is a dynamic type
     311                 :            :  * or octet-stream.
     312                 :            :  *
     313                 :            :  * Returns: %TRUE if the type is the unknown type.
     314                 :            :  */
     315                 :            : gboolean
     316                 :          1 : g_content_type_is_unknown (const gchar *type)
     317                 :            : {
     318                 :          1 :   g_return_val_if_fail (type != NULL, FALSE);
     319                 :            : 
     320                 :          1 :   return strcmp (XDG_MIME_TYPE_UNKNOWN, type) == 0;
     321                 :            : }
     322                 :            : 
     323                 :            : 
     324                 :            : typedef enum {
     325                 :            :   MIME_TAG_TYPE_OTHER,
     326                 :            :   MIME_TAG_TYPE_COMMENT
     327                 :            : } MimeTagType;
     328                 :            : 
     329                 :            : typedef struct {
     330                 :            :   int current_type;
     331                 :            :   int current_lang_level;
     332                 :            :   int comment_lang_level;
     333                 :            :   char *comment;
     334                 :            : } MimeParser;
     335                 :            : 
     336                 :            : 
     337                 :            : static int
     338                 :         51 : language_level (const char *lang)
     339                 :            : {
     340                 :            :   const char * const *lang_list;
     341                 :            :   int i;
     342                 :            : 
     343                 :            :   /* The returned list is sorted from most desirable to least
     344                 :            :      desirable and always contains the default locale "C". */
     345                 :         51 :   lang_list = g_get_language_names ();
     346                 :            : 
     347         [ +  + ]:        202 :   for (i = 0; lang_list[i]; i++)
     348         [ +  + ]:        152 :     if (strcmp (lang_list[i], lang) == 0)
     349                 :          1 :       return 1000-i;
     350                 :            : 
     351                 :         50 :   return 0;
     352                 :            : }
     353                 :            : 
     354                 :            : static void
     355                 :         55 : mime_info_start_element (GMarkupParseContext  *context,
     356                 :            :                          const gchar          *element_name,
     357                 :            :                          const gchar         **attribute_names,
     358                 :            :                          const gchar         **attribute_values,
     359                 :            :                          gpointer              user_data,
     360                 :            :                          GError              **error)
     361                 :            : {
     362                 :            :   int i;
     363                 :            :   const char *lang;
     364                 :         55 :   MimeParser *parser = user_data;
     365                 :            : 
     366         [ +  + ]:         55 :   if (strcmp (element_name, "comment") == 0)
     367                 :            :     {
     368                 :         51 :       lang = "C";
     369         [ +  + ]:         51 :       for (i = 0; attribute_names[i]; i++)
     370         [ +  - ]:         50 :         if (strcmp (attribute_names[i], "xml:lang") == 0)
     371                 :            :           {
     372                 :         50 :             lang = attribute_values[i];
     373                 :         50 :             break;
     374                 :            :           }
     375                 :            : 
     376                 :         51 :       parser->current_lang_level = language_level (lang);
     377                 :         51 :       parser->current_type = MIME_TAG_TYPE_COMMENT;
     378                 :            :     }
     379                 :            :   else
     380                 :          4 :     parser->current_type = MIME_TAG_TYPE_OTHER;
     381                 :         55 : }
     382                 :            : 
     383                 :            : static void
     384                 :         55 : mime_info_end_element (GMarkupParseContext  *context,
     385                 :            :                        const gchar          *element_name,
     386                 :            :                        gpointer              user_data,
     387                 :            :                        GError              **error)
     388                 :            : {
     389                 :         55 :   MimeParser *parser = user_data;
     390                 :            : 
     391                 :         55 :   parser->current_type = MIME_TAG_TYPE_OTHER;
     392                 :         55 : }
     393                 :            : 
     394                 :            : static void
     395                 :        107 : mime_info_text (GMarkupParseContext  *context,
     396                 :            :                 const gchar          *text,
     397                 :            :                 gsize                 text_len,
     398                 :            :                 gpointer              user_data,
     399                 :            :                 GError              **error)
     400                 :            : {
     401                 :        107 :   MimeParser *parser = user_data;
     402                 :            : 
     403         [ +  + ]:        107 :   if (parser->current_type == MIME_TAG_TYPE_COMMENT &&
     404         [ +  + ]:         51 :       parser->current_lang_level > parser->comment_lang_level)
     405                 :            :     {
     406                 :          1 :       g_free (parser->comment);
     407                 :          1 :       parser->comment = g_strndup (text, text_len);
     408                 :          1 :       parser->comment_lang_level = parser->current_lang_level;
     409                 :            :     }
     410                 :        107 : }
     411                 :            : 
     412                 :            : static char *
     413                 :          3 : load_comment_for_mime_helper (const char *dir,
     414                 :            :                               const char *basename)
     415                 :            : {
     416                 :            :   GMarkupParseContext *context;
     417                 :            :   char *filename, *data;
     418                 :            :   gsize len;
     419                 :            :   gboolean res;
     420                 :          3 :   MimeParser parse_data = {0};
     421                 :          3 :   GMarkupParser parser = {
     422                 :            :     mime_info_start_element,
     423                 :            :     mime_info_end_element,
     424                 :            :     mime_info_text,
     425                 :            :     NULL,
     426                 :            :     NULL
     427                 :            :   };
     428                 :            : 
     429                 :          3 :   filename = g_build_filename (dir, basename, NULL);
     430                 :            : 
     431                 :          3 :   res = g_file_get_contents (filename,  &data,  &len,  NULL);
     432                 :          3 :   g_free (filename);
     433         [ +  + ]:          3 :   if (!res)
     434                 :          2 :     return NULL;
     435                 :            : 
     436                 :          1 :   context = g_markup_parse_context_new (&parser, G_MARKUP_DEFAULT_FLAGS, &parse_data, NULL);
     437                 :          1 :   res = g_markup_parse_context_parse (context, data, len, NULL);
     438                 :          1 :   g_free (data);
     439                 :          1 :   g_markup_parse_context_free (context);
     440                 :            : 
     441         [ -  + ]:          1 :   if (!res)
     442                 :          0 :     return NULL;
     443                 :            : 
     444                 :          1 :   return parse_data.comment;
     445                 :            : }
     446                 :            : 
     447                 :            : 
     448                 :            : static char *
     449                 :          1 : load_comment_for_mime (const char *mimetype)
     450                 :            : {
     451                 :            :   const char * const *dirs;
     452                 :            :   char *basename;
     453                 :            :   char *comment;
     454                 :            :   gsize i;
     455                 :            : 
     456                 :          1 :   basename = g_strdup_printf ("%s.xml", mimetype);
     457                 :            : 
     458                 :          1 :   dirs = g_content_type_get_mime_dirs ();
     459         [ +  - ]:          3 :   for (i = 0; dirs[i] != NULL; i++)
     460                 :            :     {
     461                 :          3 :       comment = load_comment_for_mime_helper (dirs[i], basename);
     462         [ +  + ]:          3 :       if (comment)
     463                 :            :         {
     464                 :          1 :           g_free (basename);
     465                 :          1 :           return comment;
     466                 :            :         }
     467                 :            :     }
     468                 :          0 :   g_free (basename);
     469                 :            : 
     470                 :          0 :   return g_strdup_printf (_("%s type"), mimetype);
     471                 :            : }
     472                 :            : 
     473                 :            : /**
     474                 :            :  * g_content_type_get_description:
     475                 :            :  * @type: a content type string
     476                 :            :  *
     477                 :            :  * Gets the human readable description of the content type.
     478                 :            :  *
     479                 :            :  * Returns: a short description of the content type @type. Free the
     480                 :            :  *     returned string with g_free()
     481                 :            :  */
     482                 :            : gchar *
     483                 :          1 : g_content_type_get_description (const gchar *type)
     484                 :            : {
     485                 :            :   static GHashTable *type_comment_cache = NULL;
     486                 :          1 :   gchar *type_copy = NULL;
     487                 :            :   gchar *comment;
     488                 :            : 
     489                 :          1 :   g_return_val_if_fail (type != NULL, NULL);
     490                 :            : 
     491                 :          1 :   G_LOCK (gio_xdgmime);
     492                 :          1 :   g_begin_ignore_leaks ();
     493                 :          1 :   type = xdg_mime_unalias_mime_type (type);
     494                 :          1 :   g_end_ignore_leaks ();
     495                 :            : 
     496         [ +  - ]:          1 :   if (type_comment_cache == NULL)
     497                 :          1 :     type_comment_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
     498                 :            : 
     499                 :          1 :   comment = g_hash_table_lookup (type_comment_cache, type);
     500                 :          1 :   comment = g_strdup (comment);
     501                 :            : 
     502         [ -  + ]:          1 :   if (comment != NULL)
     503                 :            :     {
     504                 :          0 :       G_UNLOCK (gio_xdgmime);
     505                 :          0 :       return g_steal_pointer (&comment);
     506                 :            :     }
     507                 :            : 
     508                 :          1 :   type_copy = g_strdup (type);
     509                 :            : 
     510                 :          1 :   G_UNLOCK (gio_xdgmime);
     511                 :          1 :   comment = load_comment_for_mime (type_copy);
     512                 :          1 :   G_LOCK (gio_xdgmime);
     513                 :            : 
     514                 :          1 :   g_hash_table_insert (type_comment_cache,
     515                 :            :                        g_steal_pointer (&type_copy),
     516                 :          1 :                        g_strdup (comment));
     517                 :          1 :   G_UNLOCK (gio_xdgmime);
     518                 :            : 
     519                 :          1 :   return g_steal_pointer (&comment);
     520                 :            : }
     521                 :            : 
     522                 :            : /**
     523                 :            :  * g_content_type_get_mime_type:
     524                 :            :  * @type: a content type string
     525                 :            :  *
     526                 :            :  * Gets the mime type for the content type, if one is registered.
     527                 :            :  *
     528                 :            :  * Returns: (nullable) (transfer full): the registered mime type for the
     529                 :            :  *     given @type, or %NULL if unknown; free with g_free().
     530                 :            :  */
     531                 :            : char *
     532                 :          2 : g_content_type_get_mime_type (const char *type)
     533                 :            : {
     534                 :          2 :   g_return_val_if_fail (type != NULL, NULL);
     535                 :            : 
     536                 :          2 :   return g_strdup (type);
     537                 :            : }
     538                 :            : 
     539                 :            : static GIcon *
     540                 :        156 : g_content_type_get_icon_internal (const gchar *type,
     541                 :            :                                   gboolean     symbolic)
     542                 :            : {
     543                 :            :   char *mimetype_icon;
     544                 :        156 :   char *generic_mimetype_icon = NULL;
     545                 :            :   char *q;
     546                 :            :   char *icon_names[6];
     547                 :        156 :   int n = 0;
     548                 :            :   GIcon *themed_icon;
     549                 :            :   const char  *xdg_icon;
     550                 :            :   int i;
     551                 :            : 
     552                 :        156 :   g_return_val_if_fail (type != NULL, NULL);
     553                 :            : 
     554                 :        156 :   G_LOCK (gio_xdgmime);
     555                 :        156 :   g_begin_ignore_leaks ();
     556                 :        156 :   xdg_icon = xdg_mime_get_icon (type);
     557                 :        156 :   g_end_ignore_leaks ();
     558                 :        156 :   G_UNLOCK (gio_xdgmime);
     559                 :            : 
     560         [ -  + ]:        156 :   if (xdg_icon)
     561                 :          0 :     icon_names[n++] = g_strdup (xdg_icon);
     562                 :            : 
     563                 :        156 :   mimetype_icon = g_strdup (type);
     564         [ +  + ]:        312 :   while ((q = strchr (mimetype_icon, '/')) != NULL)
     565                 :        156 :     *q = '-';
     566                 :            : 
     567                 :        156 :   icon_names[n++] = mimetype_icon;
     568                 :            : 
     569                 :        156 :   generic_mimetype_icon = g_content_type_get_generic_icon_name (type);
     570         [ +  - ]:        156 :   if (generic_mimetype_icon)
     571                 :        156 :     icon_names[n++] = generic_mimetype_icon;
     572                 :            : 
     573         [ +  + ]:        156 :   if (symbolic)
     574                 :            :     {
     575         [ +  + ]:        234 :       for (i = 0; i < n; i++)
     576                 :            :         {
     577                 :        156 :           icon_names[n + i] = icon_names[i];
     578                 :        156 :           icon_names[i] = g_strconcat (icon_names[i], "-symbolic", NULL);
     579                 :            :         }
     580                 :            : 
     581                 :         78 :       n += n;
     582                 :            :     }
     583                 :            : 
     584                 :        156 :   themed_icon = g_themed_icon_new_from_names (icon_names, n);
     585                 :            : 
     586         [ +  + ]:        624 :   for (i = 0; i < n; i++)
     587                 :        468 :     g_free (icon_names[i]);
     588                 :            : 
     589                 :        156 :   return themed_icon;
     590                 :            : }
     591                 :            : 
     592                 :            : /**
     593                 :            :  * g_content_type_get_icon:
     594                 :            :  * @type: a content type string
     595                 :            :  *
     596                 :            :  * Gets the icon for a content type.
     597                 :            :  *
     598                 :            :  * Returns: (transfer full): #GIcon corresponding to the content type. Free the returned
     599                 :            :  *     object with g_object_unref()
     600                 :            :  */
     601                 :            : GIcon *
     602                 :         78 : g_content_type_get_icon (const gchar *type)
     603                 :            : {
     604                 :         78 :   return g_content_type_get_icon_internal (type, FALSE);
     605                 :            : }
     606                 :            : 
     607                 :            : /**
     608                 :            :  * g_content_type_get_symbolic_icon:
     609                 :            :  * @type: a content type string
     610                 :            :  *
     611                 :            :  * Gets the symbolic icon for a content type.
     612                 :            :  *
     613                 :            :  * Returns: (transfer full): symbolic #GIcon corresponding to the content type.
     614                 :            :  *     Free the returned object with g_object_unref()
     615                 :            :  *
     616                 :            :  * Since: 2.34
     617                 :            :  */
     618                 :            : GIcon *
     619                 :         78 : g_content_type_get_symbolic_icon (const gchar *type)
     620                 :            : {
     621                 :         78 :   return g_content_type_get_icon_internal (type, TRUE);
     622                 :            : }
     623                 :            : 
     624                 :            : /**
     625                 :            :  * g_content_type_get_generic_icon_name:
     626                 :            :  * @type: a content type string
     627                 :            :  *
     628                 :            :  * Gets the generic icon name for a content type.
     629                 :            :  *
     630                 :            :  * See the
     631                 :            :  * [shared-mime-info](http://www.freedesktop.org/wiki/Specifications/shared-mime-info-spec)
     632                 :            :  * specification for more on the generic icon name.
     633                 :            :  *
     634                 :            :  * Returns: (nullable): the registered generic icon name for the given @type,
     635                 :            :  *     or %NULL if unknown. Free with g_free()
     636                 :            :  *
     637                 :            :  * Since: 2.34
     638                 :            :  */
     639                 :            : gchar *
     640                 :        156 : g_content_type_get_generic_icon_name (const gchar *type)
     641                 :            : {
     642                 :            :   const gchar *xdg_icon_name;
     643                 :            :   gchar *icon_name;
     644                 :            : 
     645                 :        156 :   g_return_val_if_fail (type != NULL, NULL);
     646                 :            : 
     647                 :        156 :   G_LOCK (gio_xdgmime);
     648                 :        156 :   g_begin_ignore_leaks ();
     649                 :        156 :   xdg_icon_name = xdg_mime_get_generic_icon (type);
     650                 :        156 :   g_end_ignore_leaks ();
     651                 :        156 :   G_UNLOCK (gio_xdgmime);
     652                 :            : 
     653         [ +  + ]:        156 :   if (!xdg_icon_name)
     654                 :            :     {
     655                 :            :       const char *p;
     656                 :        106 :       const char *suffix = "-x-generic";
     657                 :            : 
     658                 :        106 :       p = strchr (type, '/');
     659         [ -  + ]:        106 :       if (p == NULL)
     660                 :          0 :         p = type + strlen (type);
     661                 :            : 
     662                 :        106 :       icon_name = g_malloc (p - type + strlen (suffix) + 1);
     663                 :        106 :       memcpy (icon_name, type, p - type);
     664                 :        106 :       memcpy (icon_name + (p - type), suffix, strlen (suffix));
     665                 :        106 :       icon_name[(p - type) + strlen (suffix)] = 0;
     666                 :            :     }
     667                 :            :   else
     668                 :            :     {
     669                 :         50 :       icon_name = g_strdup (xdg_icon_name);
     670                 :            :     }
     671                 :            : 
     672                 :        156 :   return icon_name;
     673                 :            : }
     674                 :            : 
     675                 :            : /**
     676                 :            :  * g_content_type_can_be_executable:
     677                 :            :  * @type: a content type string
     678                 :            :  *
     679                 :            :  * Checks if a content type can be executable. Note that for instance
     680                 :            :  * things like text files can be executables (i.e. scripts and batch files).
     681                 :            :  *
     682                 :            :  * Returns: %TRUE if the file type corresponds to a type that
     683                 :            :  *     can be executable, %FALSE otherwise.
     684                 :            :  */
     685                 :            : gboolean
     686                 :          3 : g_content_type_can_be_executable (const gchar *type)
     687                 :            : {
     688                 :          3 :   g_return_val_if_fail (type != NULL, FALSE);
     689                 :            : 
     690   [ +  +  +  + ]:          5 :   if (g_content_type_is_a (type, "application/x-executable")  ||
     691                 :          2 :       g_content_type_is_a (type, "text/plain"))
     692                 :          2 :     return TRUE;
     693                 :            : 
     694                 :          1 :   return FALSE;
     695                 :            : }
     696                 :            : 
     697                 :            : static gboolean
     698                 :          7 : looks_like_text (const guchar *data, gsize data_size)
     699                 :            : {
     700                 :            :   gsize i;
     701                 :            :   char c;
     702                 :            : 
     703         [ +  - ]:         18 :   for (i = 0; i < data_size; i++)
     704                 :            :     {
     705                 :         18 :       c = data[i];
     706                 :            : 
     707         [ +  + ]:         18 :       if (g_ascii_iscntrl (c) &&
     708   [ +  -  +  - ]:          7 :           !g_ascii_isspace (c) &&
     709                 :            :           c != '\b')
     710                 :          7 :         return FALSE;
     711                 :            :     }
     712                 :          0 :   return TRUE;
     713                 :            : }
     714                 :            : 
     715                 :            : /**
     716                 :            :  * g_content_type_from_mime_type:
     717                 :            :  * @mime_type: a mime type string
     718                 :            :  *
     719                 :            :  * Tries to find a content type based on the mime type name.
     720                 :            :  *
     721                 :            :  * Returns: (nullable): Newly allocated string with content type or
     722                 :            :  *     %NULL. Free with g_free()
     723                 :            :  *
     724                 :            :  * Since: 2.18
     725                 :            :  **/
     726                 :            : gchar *
     727                 :        167 : g_content_type_from_mime_type (const gchar *mime_type)
     728                 :            : {
     729                 :            :   char *umime;
     730                 :            : 
     731                 :        167 :   g_return_val_if_fail (mime_type != NULL, NULL);
     732                 :            : 
     733                 :        167 :   G_LOCK (gio_xdgmime);
     734                 :        167 :   g_begin_ignore_leaks ();
     735                 :            :   /* mime type and content type are same on unixes */
     736                 :        167 :   umime = g_strdup (xdg_mime_unalias_mime_type (mime_type));
     737                 :        167 :   g_end_ignore_leaks ();
     738                 :        167 :   G_UNLOCK (gio_xdgmime);
     739                 :            : 
     740                 :        167 :   return umime;
     741                 :            : }
     742                 :            : 
     743                 :            : /**
     744                 :            :  * g_content_type_guess:
     745                 :            :  * @filename: (nullable) (type filename): a path, or %NULL
     746                 :            :  * @data: (nullable) (array length=data_size): a stream of data, or %NULL
     747                 :            :  * @data_size: the size of @data
     748                 :            :  * @result_uncertain: (out) (optional): return location for the certainty
     749                 :            :  *     of the result, or %NULL
     750                 :            :  *
     751                 :            :  * Guesses the content type based on example data. If the function is
     752                 :            :  * uncertain, @result_uncertain will be set to %TRUE. Either @filename
     753                 :            :  * or @data may be %NULL, in which case the guess will be based solely
     754                 :            :  * on the other argument.
     755                 :            :  *
     756                 :            :  * Returns: a string indicating a guessed content type for the
     757                 :            :  *     given data. Free with g_free()
     758                 :            :  */
     759                 :            : gchar *
     760                 :         44 : g_content_type_guess (const gchar  *filename,
     761                 :            :                       const guchar *data,
     762                 :            :                       gsize         data_size,
     763                 :            :                       gboolean     *result_uncertain)
     764                 :            : {
     765                 :            :   char *basename;
     766                 :            :   const char *name_mimetypes[10], *sniffed_mimetype;
     767                 :            :   char *mimetype;
     768                 :            :   int i;
     769                 :            :   int n_name_mimetypes;
     770                 :            :   int sniffed_prio;
     771                 :            : 
     772                 :         44 :   sniffed_prio = 0;
     773                 :         44 :   n_name_mimetypes = 0;
     774                 :         44 :   sniffed_mimetype = XDG_MIME_TYPE_UNKNOWN;
     775                 :            : 
     776         [ +  + ]:         44 :   if (result_uncertain)
     777                 :         33 :     *result_uncertain = FALSE;
     778                 :            : 
     779                 :            :   /* our test suite and potentially other code used -1 in the past, which is
     780                 :            :    * not documented and not allowed; guard against that */
     781                 :         44 :   g_return_val_if_fail (data_size != (gsize) -1, g_strdup (XDG_MIME_TYPE_UNKNOWN));
     782                 :            : 
     783                 :         44 :   G_LOCK (gio_xdgmime);
     784                 :         44 :   g_begin_ignore_leaks ();
     785                 :            : 
     786         [ +  + ]:         44 :   if (filename)
     787                 :            :     {
     788                 :         40 :       i = strlen (filename);
     789   [ +  -  +  + ]:         40 :       if (i > 0 && filename[i - 1] == '/')
     790                 :            :         {
     791                 :          1 :           name_mimetypes[0] = "inode/directory";
     792                 :          1 :           name_mimetypes[1] = NULL;
     793                 :          1 :           n_name_mimetypes = 1;
     794         [ +  - ]:          1 :           if (result_uncertain)
     795                 :          1 :             *result_uncertain = TRUE;
     796                 :            :         }
     797                 :            :       else
     798                 :            :         {
     799                 :         39 :           basename = g_path_get_basename (filename);
     800                 :         39 :           n_name_mimetypes = xdg_mime_get_mime_types_from_file_name (basename, name_mimetypes, 10);
     801                 :         39 :           g_free (basename);
     802                 :            :         }
     803                 :            :     }
     804                 :            : 
     805                 :            :   /* Got an extension match, and no conflicts. This is it. */
     806         [ +  + ]:         44 :   if (n_name_mimetypes == 1)
     807                 :            :     {
     808                 :          5 :       gchar *s = g_strdup (name_mimetypes[0]);
     809                 :          5 :       g_end_ignore_leaks ();
     810                 :          5 :       G_UNLOCK (gio_xdgmime);
     811                 :          5 :       return s;
     812                 :            :     }
     813                 :            : 
     814         [ +  + ]:         39 :   if (data)
     815                 :            :     {
     816                 :         19 :       sniffed_mimetype = xdg_mime_get_mime_type_for_data (data, data_size, &sniffed_prio);
     817   [ +  +  +  - ]:         19 :       if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
     818         [ -  + ]:          7 :           data &&
     819                 :          7 :           looks_like_text (data, data_size))
     820                 :          0 :         sniffed_mimetype = "text/plain";
     821                 :            : 
     822                 :            :       /* For security reasons we don't ever want to sniff desktop files
     823                 :            :        * where we know the filename and it doesn't have a .desktop extension.
     824                 :            :        * This is because desktop files allow executing any application and
     825                 :            :        * we don't want to make it possible to hide them looking like something
     826                 :            :        * else.
     827                 :            :        */
     828         [ +  + ]:         19 :       if (filename != NULL &&
     829         [ +  + ]:         15 :           strcmp (sniffed_mimetype, "application/x-desktop") == 0)
     830                 :          1 :         sniffed_mimetype = "text/plain";
     831                 :            :     }
     832                 :            : 
     833         [ +  + ]:         39 :   if (n_name_mimetypes == 0)
     834                 :            :     {
     835   [ +  +  +  + ]:         35 :       if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
     836                 :            :           result_uncertain)
     837                 :         20 :         *result_uncertain = TRUE;
     838                 :            : 
     839                 :         35 :       mimetype = g_strdup (sniffed_mimetype);
     840                 :            :     }
     841                 :            :   else
     842                 :            :     {
     843                 :          4 :       mimetype = NULL;
     844         [ +  + ]:          4 :       if (sniffed_mimetype != XDG_MIME_TYPE_UNKNOWN)
     845                 :            :         {
     846         [ -  + ]:          3 :           if (sniffed_prio >= 80) /* High priority sniffing match, use that */
     847                 :          0 :             mimetype = g_strdup (sniffed_mimetype);
     848                 :            :           else
     849                 :            :             {
     850                 :            :               /* There are conflicts between the name matches and we
     851                 :            :                * have a sniffed type, use that as a tie breaker.
     852                 :            :                */
     853         [ +  - ]:          6 :               for (i = 0; i < n_name_mimetypes; i++)
     854                 :            :                 {
     855         [ +  + ]:          6 :                   if ( xdg_mime_mime_type_subclass (name_mimetypes[i], sniffed_mimetype))
     856                 :            :                     {
     857                 :            :                       /* This nametype match is derived from (or the same as)
     858                 :            :                        * the sniffed type). This is probably it.
     859                 :            :                        */
     860                 :          3 :                       mimetype = g_strdup (name_mimetypes[i]);
     861                 :          3 :                       break;
     862                 :            :                     }
     863                 :            :                 }
     864                 :            :             }
     865                 :            :         }
     866                 :            : 
     867         [ +  + ]:          4 :       if (mimetype == NULL)
     868                 :            :         {
     869                 :            :           /* Conflicts, and sniffed type was no help or not there.
     870                 :            :            * Guess on the first one
     871                 :            :            */
     872                 :          1 :           mimetype = g_strdup (name_mimetypes[0]);
     873         [ +  - ]:          1 :           if (result_uncertain)
     874                 :          1 :             *result_uncertain = TRUE;
     875                 :            :         }
     876                 :            :     }
     877                 :            : 
     878                 :         39 :   g_end_ignore_leaks ();
     879                 :         39 :   G_UNLOCK (gio_xdgmime);
     880                 :            : 
     881                 :         39 :   return mimetype;
     882                 :            : }
     883                 :            : 
     884                 :            : static void
     885                 :         14 : enumerate_mimetypes_subdir (const char *dir,
     886                 :            :                             const char *prefix,
     887                 :            :                             GHashTable *mimetypes)
     888                 :            : {
     889                 :            :   DIR *d;
     890                 :            :   struct dirent *ent;
     891                 :            :   char *mimetype;
     892                 :            : 
     893                 :         14 :   d = opendir (dir);
     894         [ +  - ]:         14 :   if (d)
     895                 :            :     {
     896         [ +  + ]:       1031 :       while ((ent = readdir (d)) != NULL)
     897                 :            :         {
     898   [ +  -  -  +  :       1003 :           if (g_str_has_suffix (ent->d_name, ".xml"))
             +  +  +  + ]
     899                 :            :             {
     900                 :        851 :               mimetype = g_strdup_printf ("%s/%.*s", prefix, (int) strlen (ent->d_name) - 4, ent->d_name);
     901                 :        851 :               g_hash_table_replace (mimetypes, mimetype, NULL);
     902                 :            :             }
     903                 :            :         }
     904                 :         14 :       closedir (d);
     905                 :            :     }
     906                 :         14 : }
     907                 :            : 
     908                 :            : static void
     909                 :          3 : enumerate_mimetypes_dir (const char *dir,
     910                 :            :                          GHashTable *mimetypes)
     911                 :            : {
     912                 :            :   DIR *d;
     913                 :            :   struct dirent *ent;
     914                 :            :   const char *mimedir;
     915                 :            :   char *name;
     916                 :            : 
     917                 :          3 :   mimedir = dir;
     918                 :            : 
     919                 :          3 :   d = opendir (mimedir);
     920         [ +  + ]:          3 :   if (d)
     921                 :            :     {
     922         [ +  + ]:         29 :       while ((ent = readdir (d)) != NULL)
     923                 :            :         {
     924         [ +  + ]:         27 :           if (strcmp (ent->d_name, "packages") != 0)
     925                 :            :             {
     926                 :         26 :               name = g_build_filename (mimedir, ent->d_name, NULL);
     927         [ +  + ]:         26 :               if (g_file_test (name, G_FILE_TEST_IS_DIR))
     928                 :         14 :                 enumerate_mimetypes_subdir (name, ent->d_name, mimetypes);
     929                 :         26 :               g_free (name);
     930                 :            :             }
     931                 :            :         }
     932                 :          1 :       closedir (d);
     933                 :            :     }
     934                 :          3 : }
     935                 :            : 
     936                 :            : /**
     937                 :            :  * g_content_types_get_registered:
     938                 :            :  *
     939                 :            :  * Gets a list of strings containing all the registered content types
     940                 :            :  * known to the system. The list and its data should be freed using
     941                 :            :  * `g_list_free_full (list, g_free)`.
     942                 :            :  *
     943                 :            :  * Returns: (element-type utf8) (transfer full): list of the registered
     944                 :            :  *     content types
     945                 :            :  */
     946                 :            : GList *
     947                 :          1 : g_content_types_get_registered (void)
     948                 :            : {
     949                 :            :   const char * const *dirs;
     950                 :            :   GHashTable *mimetypes;
     951                 :            :   GHashTableIter iter;
     952                 :            :   gpointer key;
     953                 :            :   gsize i;
     954                 :            :   GList *l;
     955                 :            : 
     956                 :          1 :   mimetypes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
     957                 :            : 
     958                 :          1 :   dirs = g_content_type_get_mime_dirs ();
     959         [ +  + ]:          4 :   for (i = 0; dirs[i] != NULL; i++)
     960                 :          3 :     enumerate_mimetypes_dir (dirs[i], mimetypes);
     961                 :            : 
     962                 :          1 :   l = NULL;
     963                 :          1 :   g_hash_table_iter_init (&iter, mimetypes);
     964         [ +  + ]:        852 :   while (g_hash_table_iter_next (&iter, &key, NULL))
     965                 :            :     {
     966                 :        851 :       l = g_list_prepend (l, key);
     967                 :        851 :       g_hash_table_iter_steal (&iter);
     968                 :            :     }
     969                 :            : 
     970                 :          1 :   g_hash_table_destroy (mimetypes);
     971                 :            : 
     972                 :          1 :   return l;
     973                 :            : }
     974                 :            : 
     975                 :            : 
     976                 :            : /* tree magic data */
     977                 :            : static GList *tree_matches = NULL;
     978                 :            : static gboolean need_reload = FALSE;
     979                 :            : 
     980                 :            : G_LOCK_DEFINE_STATIC (gio_treemagic);
     981                 :            : 
     982                 :            : typedef struct
     983                 :            : {
     984                 :            :   gchar *path;
     985                 :            :   GFileType type;
     986                 :            :   guint match_case : 1;
     987                 :            :   guint executable : 1;
     988                 :            :   guint non_empty  : 1;
     989                 :            :   guint on_disc    : 1;
     990                 :            :   gchar *mimetype;
     991                 :            :   GList *matches;
     992                 :            : } TreeMatchlet;
     993                 :            : 
     994                 :            : typedef struct
     995                 :            : {
     996                 :            :   gchar *contenttype;
     997                 :            :   gint priority;
     998                 :            :   GList *matches;
     999                 :            : } TreeMatch;
    1000                 :            : 
    1001                 :            : 
    1002                 :            : static void
    1003                 :          0 : tree_matchlet_free (TreeMatchlet *matchlet)
    1004                 :            : {
    1005                 :          0 :   g_list_free_full (matchlet->matches, (GDestroyNotify) tree_matchlet_free);
    1006                 :          0 :   g_free (matchlet->path);
    1007                 :          0 :   g_free (matchlet->mimetype);
    1008                 :          0 :   g_slice_free (TreeMatchlet, matchlet);
    1009                 :          0 : }
    1010                 :            : 
    1011                 :            : static void
    1012                 :          0 : tree_match_free (TreeMatch *match)
    1013                 :            : {
    1014                 :          0 :   g_list_free_full (match->matches, (GDestroyNotify) tree_matchlet_free);
    1015                 :          0 :   g_free (match->contenttype);
    1016                 :          0 :   g_slice_free (TreeMatch, match);
    1017                 :          0 : }
    1018                 :            : 
    1019                 :            : static TreeMatch *
    1020                 :         12 : parse_header (gchar *line)
    1021                 :            : {
    1022                 :            :   gint len;
    1023                 :            :   gchar *s;
    1024                 :            :   TreeMatch *match;
    1025                 :            : 
    1026                 :         12 :   len = strlen (line);
    1027                 :            : 
    1028   [ +  -  -  + ]:         12 :   if (line[0] != '[' || line[len - 1] != ']')
    1029                 :          0 :     return NULL;
    1030                 :            : 
    1031                 :         12 :   line[len - 1] = 0;
    1032                 :         12 :   s = strchr (line, ':');
    1033         [ -  + ]:         12 :   if (s == NULL)
    1034                 :          0 :     return NULL;
    1035                 :            : 
    1036                 :         12 :   match = g_slice_new0 (TreeMatch);
    1037                 :         12 :   match->priority = atoi (line + 1);
    1038                 :         12 :   match->contenttype = g_strdup (s + 1);
    1039                 :            : 
    1040                 :         12 :   return match;
    1041                 :            : }
    1042                 :            : 
    1043                 :            : static TreeMatchlet *
    1044                 :         25 : parse_match_line (gchar *line,
    1045                 :            :                   gint  *depth)
    1046                 :            : {
    1047                 :            :   gchar *s, *p;
    1048                 :            :   TreeMatchlet *matchlet;
    1049                 :            :   gchar **parts;
    1050                 :            :   gint i;
    1051                 :            : 
    1052                 :         25 :   matchlet = g_slice_new0 (TreeMatchlet);
    1053                 :            : 
    1054         [ +  - ]:         25 :   if (line[0] == '>')
    1055                 :            :     {
    1056                 :         25 :       *depth = 0;
    1057                 :         25 :       s = line;
    1058                 :            :     }
    1059                 :            :   else
    1060                 :            :     {
    1061                 :          0 :       *depth = atoi (line);
    1062                 :          0 :       s = strchr (line, '>');
    1063         [ #  # ]:          0 :       if (s == NULL)
    1064                 :          0 :         goto handle_error;
    1065                 :            :     }
    1066                 :         25 :   s += 2;
    1067                 :         25 :   p = strchr (s, '"');
    1068         [ -  + ]:         25 :   if (p == NULL)
    1069                 :          0 :     goto handle_error;
    1070                 :         25 :   *p = 0;
    1071                 :            : 
    1072                 :         25 :   matchlet->path = g_strdup (s);
    1073                 :         25 :   s = p + 1;
    1074                 :         25 :   parts = g_strsplit (s, ",", 0);
    1075         [ +  + ]:         25 :   if (strcmp (parts[0], "=file") == 0)
    1076                 :         16 :     matchlet->type = G_FILE_TYPE_REGULAR;
    1077         [ +  + ]:          9 :   else if (strcmp (parts[0], "=directory") == 0)
    1078                 :          8 :     matchlet->type = G_FILE_TYPE_DIRECTORY;
    1079         [ -  + ]:          1 :   else if (strcmp (parts[0], "=link") == 0)
    1080                 :          0 :     matchlet->type = G_FILE_TYPE_SYMBOLIC_LINK;
    1081                 :            :   else
    1082                 :          1 :     matchlet->type = G_FILE_TYPE_UNKNOWN;
    1083         [ +  + ]:         41 :   for (i = 1; parts[i]; i++)
    1084                 :            :     {
    1085         [ +  + ]:         16 :       if (strcmp (parts[i], "executable") == 0)
    1086                 :          1 :         matchlet->executable = 1;
    1087         [ +  + ]:         15 :       else if (strcmp (parts[i], "match-case") == 0)
    1088                 :          7 :         matchlet->match_case = 1;
    1089         [ +  - ]:          8 :       else if (strcmp (parts[i], "non-empty") == 0)
    1090                 :          8 :         matchlet->non_empty = 1;
    1091         [ #  # ]:          0 :       else if (strcmp (parts[i], "on-disc") == 0)
    1092                 :          0 :         matchlet->on_disc = 1;
    1093                 :            :       else
    1094                 :          0 :         matchlet->mimetype = g_strdup (parts[i]);
    1095                 :            :     }
    1096                 :            : 
    1097                 :         25 :   g_strfreev (parts);
    1098                 :            : 
    1099                 :         25 :   return matchlet;
    1100                 :            : 
    1101                 :          0 : handle_error:
    1102                 :          0 :   g_slice_free (TreeMatchlet, matchlet);
    1103                 :          0 :   return NULL;
    1104                 :            : }
    1105                 :            : 
    1106                 :            : static gint
    1107                 :         11 : cmp_match (gconstpointer a, gconstpointer b)
    1108                 :            : {
    1109                 :         11 :   const TreeMatch *aa = (const TreeMatch *)a;
    1110                 :         11 :   const TreeMatch *bb = (const TreeMatch *)b;
    1111                 :            : 
    1112                 :         11 :   return bb->priority - aa->priority;
    1113                 :            : }
    1114                 :            : 
    1115                 :            : static void
    1116                 :         12 : insert_match (TreeMatch *match)
    1117                 :            : {
    1118                 :         12 :   tree_matches = g_list_insert_sorted (tree_matches, match, cmp_match);
    1119                 :         12 : }
    1120                 :            : 
    1121                 :            : static void
    1122                 :         25 : insert_matchlet (TreeMatch    *match,
    1123                 :            :                  TreeMatchlet *matchlet,
    1124                 :            :                  gint          depth)
    1125                 :            : {
    1126         [ +  - ]:         25 :   if (depth == 0)
    1127                 :         25 :     match->matches = g_list_append (match->matches, matchlet);
    1128                 :            :   else
    1129                 :            :     {
    1130                 :            :       GList *last;
    1131                 :            :       TreeMatchlet *m;
    1132                 :            : 
    1133                 :          0 :       last = g_list_last (match->matches);
    1134         [ #  # ]:          0 :       if (!last)
    1135                 :            :         {
    1136                 :          0 :           tree_matchlet_free (matchlet);
    1137                 :          0 :           g_warning ("can't insert tree matchlet at depth %d", depth);
    1138                 :          0 :           return;
    1139                 :            :         }
    1140                 :            : 
    1141                 :          0 :       m = (TreeMatchlet *) last->data;
    1142         [ #  # ]:          0 :       while (--depth > 0)
    1143                 :            :         {
    1144                 :          0 :           last = g_list_last (m->matches);
    1145         [ #  # ]:          0 :           if (!last)
    1146                 :            :             {
    1147                 :          0 :               tree_matchlet_free (matchlet);
    1148                 :          0 :               g_warning ("can't insert tree matchlet at depth %d", depth);
    1149                 :          0 :               return;
    1150                 :            :             }
    1151                 :            : 
    1152                 :          0 :           m = (TreeMatchlet *) last->data;
    1153                 :            :         }
    1154                 :          0 :       m->matches = g_list_append (m->matches, matchlet);
    1155                 :            :     }
    1156                 :            : }
    1157                 :            : 
    1158                 :            : static void
    1159                 :          3 : read_tree_magic_from_directory (const gchar *prefix)
    1160                 :            : {
    1161                 :            :   gchar *filename;
    1162                 :            :   gchar *text;
    1163                 :            :   gsize len;
    1164                 :            :   gchar **lines;
    1165                 :            :   gsize i;
    1166                 :            :   TreeMatch *match;
    1167                 :            :   TreeMatchlet *matchlet;
    1168                 :            :   gint depth;
    1169                 :            : 
    1170                 :          3 :   filename = g_build_filename (prefix, "treemagic", NULL);
    1171                 :            : 
    1172         [ +  + ]:          3 :   if (g_file_get_contents (filename, &text, &len, NULL))
    1173                 :            :     {
    1174         [ +  - ]:          1 :       if (strcmp (text, "MIME-TreeMagic") == 0)
    1175                 :            :         {
    1176                 :          1 :           lines = g_strsplit (text + strlen ("MIME-TreeMagic") + 2, "\n", 0);
    1177                 :          1 :           match = NULL;
    1178   [ +  -  +  + ]:         38 :           for (i = 0; lines[i] && lines[i][0]; i++)
    1179                 :            :             {
    1180   [ +  +  +  - ]:         37 :               if (lines[i][0] == '[' && (match = parse_header (lines[i])) != NULL)
    1181                 :            :                 {
    1182                 :         12 :                   insert_match (match);
    1183                 :            :                 }
    1184         [ +  - ]:         25 :               else if (match != NULL)
    1185                 :            :                 {
    1186                 :         25 :                   matchlet = parse_match_line (lines[i], &depth);
    1187         [ -  + ]:         25 :                   if (matchlet == NULL)
    1188                 :            :                     {
    1189                 :          0 :                       g_warning ("%s: body corrupt; skipping", filename);
    1190                 :          0 :                       break;
    1191                 :            :                     }
    1192                 :         25 :                   insert_matchlet (match, matchlet, depth);
    1193                 :            :                 }
    1194                 :            :               else
    1195                 :            :                 {
    1196                 :          0 :                   g_warning ("%s: header corrupt; skipping", filename);
    1197                 :          0 :                   break;
    1198                 :            :                 }
    1199                 :            :             }
    1200                 :            : 
    1201                 :          1 :           g_strfreev (lines);
    1202                 :            :         }
    1203                 :            :       else
    1204                 :          0 :         g_warning ("%s: header not found, skipping", filename);
    1205                 :            : 
    1206                 :          1 :       g_free (text);
    1207                 :            :     }
    1208                 :            : 
    1209                 :          3 :   g_free (filename);
    1210                 :          3 : }
    1211                 :            : 
    1212                 :            : static void
    1213                 :          2 : tree_magic_schedule_reload (void)
    1214                 :            : {
    1215                 :          2 :   need_reload = TRUE;
    1216                 :          2 : }
    1217                 :            : 
    1218                 :            : static void
    1219                 :          0 : xdg_mime_reload (void *user_data)
    1220                 :            : {
    1221                 :          0 :   tree_magic_schedule_reload ();
    1222                 :          0 : }
    1223                 :            : 
    1224                 :            : static void
    1225                 :          1 : tree_magic_shutdown (void)
    1226                 :            : {
    1227                 :          1 :   g_list_free_full (tree_matches, (GDestroyNotify) tree_match_free);
    1228                 :          1 :   tree_matches = NULL;
    1229                 :          1 : }
    1230                 :            : 
    1231                 :            : static void
    1232                 :          4 : tree_magic_init (void)
    1233                 :            : {
    1234                 :            :   static gboolean initialized = FALSE;
    1235                 :            :   gsize i;
    1236                 :            : 
    1237         [ +  + ]:          4 :   if (!initialized)
    1238                 :            :     {
    1239                 :          1 :       initialized = TRUE;
    1240                 :            : 
    1241                 :          1 :       xdg_mime_register_reload_callback (xdg_mime_reload, NULL, NULL);
    1242                 :          1 :       need_reload = TRUE;
    1243                 :            :     }
    1244                 :            : 
    1245         [ +  + ]:          4 :   if (need_reload)
    1246                 :            :     {
    1247                 :            :       const char * const *dirs;
    1248                 :            : 
    1249                 :          1 :       need_reload = FALSE;
    1250                 :            : 
    1251                 :          1 :       tree_magic_shutdown ();
    1252                 :            : 
    1253                 :          1 :       dirs = g_content_type_get_mime_dirs ();
    1254         [ +  + ]:          4 :       for (i = 0; dirs[i] != NULL; i++)
    1255                 :          3 :         read_tree_magic_from_directory (dirs[i]);
    1256                 :            :     }
    1257                 :          4 : }
    1258                 :            : 
    1259                 :            : /* a filtering enumerator */
    1260                 :            : 
    1261                 :            : typedef struct
    1262                 :            : {
    1263                 :            :   gchar *path;
    1264                 :            :   gint depth;
    1265                 :            :   gboolean ignore_case;
    1266                 :            :   gchar **components;
    1267                 :            :   gchar **case_components;
    1268                 :            :   GFileEnumerator **enumerators;
    1269                 :            :   GFile **children;
    1270                 :            : } Enumerator;
    1271                 :            : 
    1272                 :            : static gboolean
    1273                 :         99 : component_match (Enumerator  *e,
    1274                 :            :                  gint         depth,
    1275                 :            :                  const gchar *name)
    1276                 :            : {
    1277                 :            :   gchar *case_folded, *key, *utf8_name;
    1278                 :            :   gboolean found;
    1279                 :            : 
    1280         [ +  + ]:         99 :   if (strcmp (name, e->components[depth]) == 0)
    1281                 :          2 :     return TRUE;
    1282                 :            : 
    1283         [ +  + ]:         97 :   if (!e->ignore_case)
    1284                 :         27 :     return FALSE;
    1285                 :            : 
    1286                 :         70 :   utf8_name = g_filename_to_utf8 (name, -1, NULL, NULL, NULL);
    1287         [ +  + ]:         70 :   if (utf8_name == NULL)
    1288                 :         18 :     utf8_name = g_utf8_make_valid (name, -1);
    1289                 :            : 
    1290                 :         70 :   case_folded = g_utf8_casefold (utf8_name, -1);
    1291                 :         70 :   key = g_utf8_collate_key (case_folded, -1);
    1292                 :            : 
    1293                 :         70 :   found = strcmp (key, e->case_components[depth]) == 0;
    1294                 :            : 
    1295                 :         70 :   g_free (utf8_name);
    1296                 :         70 :   g_free (case_folded);
    1297                 :         70 :   g_free (key);
    1298                 :            : 
    1299                 :         70 :   return found;
    1300                 :            : }
    1301                 :            : 
    1302                 :            : static GFile *
    1303                 :        155 : next_match_recurse (Enumerator *e,
    1304                 :            :                     gint        depth)
    1305                 :            : {
    1306                 :            :   GFile *file;
    1307                 :            :   GFileInfo *info;
    1308                 :            :   const gchar *name;
    1309                 :            : 
    1310                 :            :   while (TRUE)
    1311                 :            :     {
    1312         [ +  + ]:        251 :       if (e->enumerators[depth] == NULL)
    1313                 :            :         {
    1314         [ +  + ]:        152 :           if (depth > 0)
    1315                 :            :             {
    1316                 :         56 :               file = next_match_recurse (e, depth - 1);
    1317         [ -  + ]:         56 :               if (file)
    1318                 :            :                 {
    1319                 :          0 :                   e->children[depth] = file;
    1320                 :          0 :                   e->enumerators[depth] = g_file_enumerate_children (file,
    1321                 :            :                                                                      G_FILE_ATTRIBUTE_STANDARD_NAME,
    1322                 :            :                                                                      G_FILE_QUERY_INFO_NONE,
    1323                 :            :                                                                      NULL,
    1324                 :            :                                                                      NULL);
    1325                 :            :                 }
    1326                 :            :             }
    1327         [ +  - ]:        152 :           if (e->enumerators[depth] == NULL)
    1328                 :        152 :             return NULL;
    1329                 :            :         }
    1330                 :            : 
    1331         [ +  + ]:        195 :       while ((info = g_file_enumerator_next_file (e->enumerators[depth], NULL, NULL)))
    1332                 :            :         {
    1333                 :         99 :           name = g_file_info_get_name (info);
    1334         [ +  + ]:         99 :           if (component_match (e, depth, name))
    1335                 :            :             {
    1336                 :          3 :               file = g_file_get_child (e->children[depth], name);
    1337                 :          3 :               g_object_unref (info);
    1338                 :          3 :               return file;
    1339                 :            :             }
    1340                 :         96 :           g_object_unref (info);
    1341                 :            :         }
    1342                 :            : 
    1343                 :         96 :       g_object_unref (e->enumerators[depth]);
    1344                 :         96 :       e->enumerators[depth] = NULL;
    1345                 :         96 :       g_object_unref (e->children[depth]);
    1346                 :         96 :       e->children[depth] = NULL;
    1347                 :            :     }
    1348                 :            : }
    1349                 :            : 
    1350                 :            : static GFile *
    1351                 :         99 : enumerator_next (Enumerator *e)
    1352                 :            : {
    1353                 :         99 :   return next_match_recurse (e, e->depth - 1);
    1354                 :            : }
    1355                 :            : 
    1356                 :            : static Enumerator *
    1357                 :         99 : enumerator_new (GFile      *root,
    1358                 :            :                 const char *path,
    1359                 :            :                 gboolean    ignore_case)
    1360                 :            : {
    1361                 :            :   Enumerator *e;
    1362                 :            :   gint i;
    1363                 :            :   gchar *case_folded;
    1364                 :            : 
    1365                 :         99 :   e = g_new0 (Enumerator, 1);
    1366                 :         99 :   e->path = g_strdup (path);
    1367                 :         99 :   e->ignore_case = ignore_case;
    1368                 :            : 
    1369                 :         99 :   e->components = g_strsplit (e->path, G_DIR_SEPARATOR_S, -1);
    1370                 :         99 :   e->depth = g_strv_length (e->components);
    1371         [ +  + ]:         99 :   if (e->ignore_case)
    1372                 :            :     {
    1373                 :         71 :       e->case_components = g_new0 (char *, e->depth + 1);
    1374         [ +  + ]:        182 :       for (i = 0; e->components[i]; i++)
    1375                 :            :         {
    1376                 :        111 :           case_folded = g_utf8_casefold (e->components[i], -1);
    1377                 :        111 :           e->case_components[i] = g_utf8_collate_key (case_folded, -1);
    1378                 :        111 :           g_free (case_folded);
    1379                 :            :         }
    1380                 :            :     }
    1381                 :            : 
    1382                 :         99 :   e->children = g_new0 (GFile *, e->depth);
    1383                 :         99 :   e->children[0] = g_object_ref (root);
    1384                 :         99 :   e->enumerators = g_new0 (GFileEnumerator *, e->depth);
    1385                 :         99 :   e->enumerators[0] = g_file_enumerate_children (root,
    1386                 :            :                                                  G_FILE_ATTRIBUTE_STANDARD_NAME,
    1387                 :            :                                                  G_FILE_QUERY_INFO_NONE,
    1388                 :            :                                                  NULL,
    1389                 :            :                                                  NULL);
    1390                 :            : 
    1391                 :         99 :   return e;
    1392                 :            : }
    1393                 :            : 
    1394                 :            : static void
    1395                 :         99 : enumerator_free (Enumerator *e)
    1396                 :            : {
    1397                 :            :   gint i;
    1398                 :            : 
    1399         [ +  + ]:        254 :   for (i = 0; i < e->depth; i++)
    1400                 :            :     {
    1401         [ +  + ]:        155 :       if (e->enumerators[i])
    1402                 :          3 :         g_object_unref (e->enumerators[i]);
    1403         [ +  + ]:        155 :       if (e->children[i])
    1404                 :          3 :         g_object_unref (e->children[i]);
    1405                 :            :     }
    1406                 :            : 
    1407                 :         99 :   g_free (e->enumerators);
    1408                 :         99 :   g_free (e->children);
    1409                 :         99 :   g_strfreev (e->components);
    1410         [ +  + ]:         99 :   if (e->case_components)
    1411                 :         71 :     g_strfreev (e->case_components);
    1412                 :         99 :   g_free (e->path);
    1413                 :         99 :   g_free (e);
    1414                 :         99 : }
    1415                 :            : 
    1416                 :            : static gboolean
    1417                 :         99 : matchlet_match (TreeMatchlet *matchlet,
    1418                 :            :                 GFile        *root)
    1419                 :            : {
    1420                 :            :   GFile *file;
    1421                 :            :   GFileInfo *info;
    1422                 :            :   gboolean result;
    1423                 :            :   const gchar *attrs;
    1424                 :            :   Enumerator *e;
    1425                 :            :   GList *l;
    1426                 :            : 
    1427                 :         99 :   e = enumerator_new (root, matchlet->path, !matchlet->match_case);
    1428                 :            : 
    1429                 :            :   do
    1430                 :            :     {
    1431                 :         99 :       file = enumerator_next (e);
    1432         [ +  + ]:         99 :       if (!file)
    1433                 :            :         {
    1434                 :         96 :           enumerator_free (e);
    1435                 :         96 :           return FALSE;
    1436                 :            :         }
    1437                 :            : 
    1438         [ -  + ]:          3 :       if (matchlet->mimetype)
    1439                 :          0 :         attrs = G_FILE_ATTRIBUTE_STANDARD_TYPE ","
    1440                 :            :                 G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE ","
    1441                 :            :                 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE;
    1442                 :            :       else
    1443                 :          3 :         attrs = G_FILE_ATTRIBUTE_STANDARD_TYPE ","
    1444                 :            :                 G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE;
    1445                 :          3 :       info = g_file_query_info (file,
    1446                 :            :                                 attrs,
    1447                 :            :                                 G_FILE_QUERY_INFO_NONE,
    1448                 :            :                                 NULL,
    1449                 :            :                                 NULL);
    1450         [ +  - ]:          3 :       if (info)
    1451                 :            :         {
    1452                 :          3 :           result = TRUE;
    1453                 :            : 
    1454         [ +  - ]:          3 :           if (matchlet->type != G_FILE_TYPE_UNKNOWN &&
    1455         [ -  + ]:          3 :               g_file_info_get_file_type (info) != matchlet->type)
    1456                 :          0 :             result = FALSE;
    1457                 :            : 
    1458   [ +  +  -  + ]:          4 :           if (matchlet->executable &&
    1459                 :          1 :               !g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
    1460                 :          0 :             result = FALSE;
    1461                 :            :         }
    1462                 :            :       else
    1463                 :          0 :         result = FALSE;
    1464                 :            : 
    1465   [ +  -  +  + ]:          3 :       if (result && matchlet->non_empty)
    1466                 :            :         {
    1467                 :            :           GFileEnumerator *child_enum;
    1468                 :            :           GFileInfo *child_info;
    1469                 :            : 
    1470                 :          1 :           child_enum = g_file_enumerate_children (file,
    1471                 :            :                                                   G_FILE_ATTRIBUTE_STANDARD_NAME,
    1472                 :            :                                                   G_FILE_QUERY_INFO_NONE,
    1473                 :            :                                                   NULL,
    1474                 :            :                                                   NULL);
    1475                 :            : 
    1476         [ +  - ]:          1 :           if (child_enum)
    1477                 :            :             {
    1478                 :          1 :               child_info = g_file_enumerator_next_file (child_enum, NULL, NULL);
    1479         [ +  - ]:          1 :               if (child_info)
    1480                 :          1 :                 g_object_unref (child_info);
    1481                 :            :               else
    1482                 :          0 :                 result = FALSE;
    1483                 :          1 :               g_object_unref (child_enum);
    1484                 :            :             }
    1485                 :            :           else
    1486                 :          0 :             result = FALSE;
    1487                 :            :         }
    1488                 :            : 
    1489   [ +  -  -  + ]:          3 :       if (result && matchlet->mimetype)
    1490                 :            :         {
    1491         [ #  # ]:          0 :           if (strcmp (matchlet->mimetype, g_file_info_get_content_type (info)) != 0)
    1492                 :          0 :             result = FALSE;
    1493                 :            :         }
    1494                 :            : 
    1495         [ +  - ]:          3 :       if (info)
    1496                 :          3 :         g_object_unref (info);
    1497                 :          3 :       g_object_unref (file);
    1498                 :            :     }
    1499         [ -  + ]:          3 :   while (!result);
    1500                 :            : 
    1501                 :          3 :   enumerator_free (e);
    1502                 :            : 
    1503         [ +  - ]:          3 :   if (!matchlet->matches)
    1504                 :          3 :     return TRUE;
    1505                 :            : 
    1506         [ #  # ]:          0 :   for (l = matchlet->matches; l; l = l->next)
    1507                 :            :     {
    1508                 :            :       TreeMatchlet *submatchlet;
    1509                 :            : 
    1510                 :          0 :       submatchlet = l->data;
    1511         [ #  # ]:          0 :       if (matchlet_match (submatchlet, root))
    1512                 :          0 :         return TRUE;
    1513                 :            :     }
    1514                 :            : 
    1515                 :          0 :   return FALSE;
    1516                 :            : }
    1517                 :            : 
    1518                 :            : static void
    1519                 :         48 : match_match (TreeMatch    *match,
    1520                 :            :              GFile        *root,
    1521                 :            :              GPtrArray    *types)
    1522                 :            : {
    1523                 :            :   GList *l;
    1524                 :            : 
    1525         [ +  + ]:        144 :   for (l = match->matches; l; l = l->next)
    1526                 :            :     {
    1527                 :         99 :       TreeMatchlet *matchlet = l->data;
    1528         [ +  + ]:         99 :       if (matchlet_match (matchlet, root))
    1529                 :            :         {
    1530                 :          6 :           g_ptr_array_add (types, g_strdup (match->contenttype));
    1531                 :          3 :           break;
    1532                 :            :         }
    1533                 :            :     }
    1534                 :         48 : }
    1535                 :            : 
    1536                 :            : /**
    1537                 :            :  * g_content_type_guess_for_tree:
    1538                 :            :  * @root: the root of the tree to guess a type for
    1539                 :            :  *
    1540                 :            :  * Tries to guess the type of the tree with root @root, by
    1541                 :            :  * looking at the files it contains. The result is an array
    1542                 :            :  * of content types, with the best guess coming first.
    1543                 :            :  *
    1544                 :            :  * The types returned all have the form x-content/foo, e.g.
    1545                 :            :  * x-content/audio-cdda (for audio CDs) or x-content/image-dcf
    1546                 :            :  * (for a camera memory card). See the
    1547                 :            :  * [shared-mime-info](http://www.freedesktop.org/wiki/Specifications/shared-mime-info-spec)
    1548                 :            :  * specification for more on x-content types.
    1549                 :            :  *
    1550                 :            :  * This function is useful in the implementation of
    1551                 :            :  * g_mount_guess_content_type().
    1552                 :            :  *
    1553                 :            :  * Returns: (transfer full) (array zero-terminated=1): an %NULL-terminated
    1554                 :            :  *     array of zero or more content types. Free with g_strfreev()
    1555                 :            :  *
    1556                 :            :  * Since: 2.18
    1557                 :            :  */
    1558                 :            : gchar **
    1559                 :          4 : g_content_type_guess_for_tree (GFile *root)
    1560                 :            : {
    1561                 :            :   GPtrArray *types;
    1562                 :            :   GList *l;
    1563                 :            : 
    1564                 :          4 :   types = g_ptr_array_new ();
    1565                 :            : 
    1566                 :          4 :   G_LOCK (gio_treemagic);
    1567                 :            : 
    1568                 :          4 :   tree_magic_init ();
    1569         [ +  + ]:         52 :   for (l = tree_matches; l; l = l->next)
    1570                 :            :     {
    1571                 :         48 :       TreeMatch *match = l->data;
    1572                 :         48 :       match_match (match, root, types);
    1573                 :            :     }
    1574                 :            : 
    1575                 :          4 :   G_UNLOCK (gio_treemagic);
    1576                 :            : 
    1577                 :          4 :   g_ptr_array_add (types, NULL);
    1578                 :            : 
    1579                 :          4 :   return (gchar **)g_ptr_array_free (types, FALSE);
    1580                 :            : }

Generated by: LCOV version 1.14