LCOV - code coverage report
Current view: top level - gio - gcontenttype-fdo.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 87.2 % 572 499
Test Date: 2024-11-26 05:23:01 Functions: 93.9 % 49 46
Branches: - 0 0

             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                 :             : #include <dirent.h>
      40                 :             : 
      41                 :             : #define XDG_PREFIX _gio_xdg
      42                 :             : #include "xdgmime/xdgmime.h"
      43                 :             : 
      44                 :             : static void tree_magic_schedule_reload (void);
      45                 :             : 
      46                 :             : /* We lock this mutex whenever we modify global state in this module.
      47                 :             :  * Taking and releasing this lock should always be associated with a pair of
      48                 :             :  * g_begin_ignore_leaks()/g_end_ignore_leaks() calls, as any call into xdgmime
      49                 :             :  * could trigger xdg_mime_init(), which makes a number of one-time allocations
      50                 :             :  * which GLib can never free as it doesn’t know when is suitable to call
      51                 :             :  * xdg_mime_shutdown(). */
      52                 :             : G_LOCK_DEFINE_STATIC (gio_xdgmime);
      53                 :             : 
      54                 :             : gsize
      55                 :          12 : _g_unix_content_type_get_sniff_len (void)
      56                 :             : {
      57                 :             :   gsize size;
      58                 :             : 
      59                 :          12 :   G_LOCK (gio_xdgmime);
      60                 :          12 :   g_begin_ignore_leaks ();
      61                 :          12 :   size = xdg_mime_get_max_buffer_extents ();
      62                 :          12 :   g_end_ignore_leaks ();
      63                 :          12 :   G_UNLOCK (gio_xdgmime);
      64                 :             : 
      65                 :          12 :   return size;
      66                 :             : }
      67                 :             : 
      68                 :             : gchar *
      69                 :        9760 : _g_unix_content_type_unalias (const gchar *type)
      70                 :             : {
      71                 :             :   gchar *res;
      72                 :             : 
      73                 :        9760 :   G_LOCK (gio_xdgmime);
      74                 :        9760 :   g_begin_ignore_leaks ();
      75                 :        9760 :   res = g_strdup (xdg_mime_unalias_mime_type (type));
      76                 :        9760 :   g_end_ignore_leaks ();
      77                 :        9760 :   G_UNLOCK (gio_xdgmime);
      78                 :             : 
      79                 :        9760 :   return res;
      80                 :             : }
      81                 :             : 
      82                 :             : gchar **
      83                 :          79 : _g_unix_content_type_get_parents (const gchar *type)
      84                 :             : {
      85                 :             :   const gchar *umime;
      86                 :             :   gchar **parents;
      87                 :             :   GPtrArray *array;
      88                 :             :   int i;
      89                 :             : 
      90                 :          79 :   array = g_ptr_array_new ();
      91                 :             : 
      92                 :          79 :   G_LOCK (gio_xdgmime);
      93                 :          79 :   g_begin_ignore_leaks ();
      94                 :             : 
      95                 :          79 :   umime = xdg_mime_unalias_mime_type (type);
      96                 :             : 
      97                 :          79 :   g_ptr_array_add (array, g_strdup (umime));
      98                 :             : 
      99                 :          79 :   parents = xdg_mime_list_mime_parents (umime);
     100                 :          85 :   for (i = 0; parents && parents[i] != NULL; i++)
     101                 :          12 :     g_ptr_array_add (array, g_strdup (parents[i]));
     102                 :             : 
     103                 :          79 :   free (parents);
     104                 :             : 
     105                 :          79 :   g_end_ignore_leaks ();
     106                 :          79 :   G_UNLOCK (gio_xdgmime);
     107                 :             : 
     108                 :          79 :   g_ptr_array_add (array, NULL);
     109                 :             : 
     110                 :          79 :   return (gchar **)g_ptr_array_free (array, FALSE);
     111                 :             : }
     112                 :             : 
     113                 :             : G_LOCK_DEFINE_STATIC (global_mime_dirs);
     114                 :             : static gchar **global_mime_dirs = NULL;
     115                 :             : 
     116                 :             : static void
     117                 :           2 : _g_content_type_set_mime_dirs_locked (const char * const *dirs)
     118                 :             : {
     119                 :           2 :   g_clear_pointer (&global_mime_dirs, g_strfreev);
     120                 :             : 
     121                 :           2 :   if (dirs != NULL)
     122                 :             :     {
     123                 :           0 :       global_mime_dirs = g_strdupv ((gchar **) dirs);
     124                 :             :     }
     125                 :             :   else
     126                 :             :     {
     127                 :           2 :       GPtrArray *mime_dirs = g_ptr_array_new_with_free_func (g_free);
     128                 :           2 :       const gchar * const *system_dirs = g_get_system_data_dirs ();
     129                 :             : 
     130                 :           2 :       g_ptr_array_add (mime_dirs, g_build_filename (g_get_user_data_dir (), "mime", NULL));
     131                 :           6 :       for (; *system_dirs != NULL; system_dirs++)
     132                 :           4 :         g_ptr_array_add (mime_dirs, g_build_filename (*system_dirs, "mime", NULL));
     133                 :           2 :       g_ptr_array_add (mime_dirs, NULL);  /* NULL terminator */
     134                 :             : 
     135                 :           2 :       global_mime_dirs = (gchar **) g_ptr_array_free (mime_dirs, FALSE);
     136                 :             :     }
     137                 :             : 
     138                 :           2 :   xdg_mime_set_dirs ((const gchar * const *) global_mime_dirs);
     139                 :           2 :   tree_magic_schedule_reload ();
     140                 :           2 : }
     141                 :             : 
     142                 :             : /*< private >*/
     143                 :             : void
     144                 :           1 : g_content_type_set_mime_dirs_impl (const gchar * const *dirs)
     145                 :             : {
     146                 :           1 :   G_LOCK (global_mime_dirs);
     147                 :           1 :   _g_content_type_set_mime_dirs_locked (dirs);
     148                 :           1 :   G_UNLOCK (global_mime_dirs);
     149                 :           1 : }
     150                 :             : 
     151                 :             : /*< private >*/
     152                 :             : const gchar * const *
     153                 :           3 : g_content_type_get_mime_dirs_impl (void)
     154                 :             : {
     155                 :             :   const gchar * const *mime_dirs;
     156                 :             : 
     157                 :           3 :   G_LOCK (global_mime_dirs);
     158                 :             : 
     159                 :           3 :   if (global_mime_dirs == NULL)
     160                 :           1 :     _g_content_type_set_mime_dirs_locked (NULL);
     161                 :             : 
     162                 :           3 :   mime_dirs = (const gchar * const *) global_mime_dirs;
     163                 :             : 
     164                 :           3 :   G_UNLOCK (global_mime_dirs);
     165                 :             : 
     166                 :           3 :   g_assert (mime_dirs != NULL);
     167                 :           3 :   return mime_dirs;
     168                 :             : }
     169                 :             : 
     170                 :             : gboolean
     171                 :         370 : g_content_type_equals_impl (const gchar *type1,
     172                 :             :                             const gchar *type2)
     173                 :             : {
     174                 :             :   gboolean res;
     175                 :             : 
     176                 :         370 :   g_return_val_if_fail (type1 != NULL, FALSE);
     177                 :         370 :   g_return_val_if_fail (type2 != NULL, FALSE);
     178                 :             : 
     179                 :         370 :   G_LOCK (gio_xdgmime);
     180                 :         370 :   g_begin_ignore_leaks ();
     181                 :         370 :   res = xdg_mime_mime_type_equal (type1, type2);
     182                 :         370 :   g_end_ignore_leaks ();
     183                 :         370 :   G_UNLOCK (gio_xdgmime);
     184                 :             : 
     185                 :         370 :   return res;
     186                 :             : }
     187                 :             : 
     188                 :             : gboolean
     189                 :          10 : g_content_type_is_a_impl (const gchar *type,
     190                 :             :                           const gchar *supertype)
     191                 :             : {
     192                 :             :   gboolean res;
     193                 :             : 
     194                 :          10 :   g_return_val_if_fail (type != NULL, FALSE);
     195                 :          10 :   g_return_val_if_fail (supertype != NULL, FALSE);
     196                 :             : 
     197                 :          10 :   G_LOCK (gio_xdgmime);
     198                 :          10 :   g_begin_ignore_leaks ();
     199                 :          10 :   res = xdg_mime_mime_type_subclass (type, supertype);
     200                 :          10 :   g_end_ignore_leaks ();
     201                 :          10 :   G_UNLOCK (gio_xdgmime);
     202                 :             : 
     203                 :          10 :   return res;
     204                 :             : }
     205                 :             : 
     206                 :             : gboolean
     207                 :           1 : g_content_type_is_mime_type_impl (const gchar *type,
     208                 :             :                                   const gchar *mime_type)
     209                 :             : {
     210                 :           1 :   return g_content_type_is_a (type, mime_type);
     211                 :             : }
     212                 :             : 
     213                 :             : gboolean
     214                 :           1 : g_content_type_is_unknown_impl (const gchar *type)
     215                 :             : {
     216                 :           1 :   g_return_val_if_fail (type != NULL, FALSE);
     217                 :             : 
     218                 :           1 :   return strcmp (XDG_MIME_TYPE_UNKNOWN, type) == 0;
     219                 :             : }
     220                 :             : 
     221                 :             : 
     222                 :             : typedef enum {
     223                 :             :   MIME_TAG_TYPE_OTHER,
     224                 :             :   MIME_TAG_TYPE_COMMENT
     225                 :             : } MimeTagType;
     226                 :             : 
     227                 :             : typedef struct {
     228                 :             :   int current_type;
     229                 :             :   int current_lang_level;
     230                 :             :   int comment_lang_level;
     231                 :             :   char *comment;
     232                 :             : } MimeParser;
     233                 :             : 
     234                 :             : 
     235                 :             : static int
     236                 :          51 : language_level (const char *lang)
     237                 :             : {
     238                 :             :   const char * const *lang_list;
     239                 :             :   int i;
     240                 :             : 
     241                 :             :   /* The returned list is sorted from most desirable to least
     242                 :             :      desirable and always contains the default locale "C". */
     243                 :          51 :   lang_list = g_get_language_names ();
     244                 :             : 
     245                 :         202 :   for (i = 0; lang_list[i]; i++)
     246                 :         152 :     if (strcmp (lang_list[i], lang) == 0)
     247                 :           1 :       return 1000-i;
     248                 :             : 
     249                 :          50 :   return 0;
     250                 :             : }
     251                 :             : 
     252                 :             : static void
     253                 :          55 : mime_info_start_element (GMarkupParseContext  *context,
     254                 :             :                          const gchar          *element_name,
     255                 :             :                          const gchar         **attribute_names,
     256                 :             :                          const gchar         **attribute_values,
     257                 :             :                          gpointer              user_data,
     258                 :             :                          GError              **error)
     259                 :             : {
     260                 :             :   int i;
     261                 :             :   const char *lang;
     262                 :          55 :   MimeParser *parser = user_data;
     263                 :             : 
     264                 :          55 :   if (strcmp (element_name, "comment") == 0)
     265                 :             :     {
     266                 :          51 :       lang = "C";
     267                 :          51 :       for (i = 0; attribute_names[i]; i++)
     268                 :          50 :         if (strcmp (attribute_names[i], "xml:lang") == 0)
     269                 :             :           {
     270                 :          50 :             lang = attribute_values[i];
     271                 :          50 :             break;
     272                 :             :           }
     273                 :             : 
     274                 :          51 :       parser->current_lang_level = language_level (lang);
     275                 :          51 :       parser->current_type = MIME_TAG_TYPE_COMMENT;
     276                 :             :     }
     277                 :             :   else
     278                 :           4 :     parser->current_type = MIME_TAG_TYPE_OTHER;
     279                 :          55 : }
     280                 :             : 
     281                 :             : static void
     282                 :          55 : mime_info_end_element (GMarkupParseContext  *context,
     283                 :             :                        const gchar          *element_name,
     284                 :             :                        gpointer              user_data,
     285                 :             :                        GError              **error)
     286                 :             : {
     287                 :          55 :   MimeParser *parser = user_data;
     288                 :             : 
     289                 :          55 :   parser->current_type = MIME_TAG_TYPE_OTHER;
     290                 :          55 : }
     291                 :             : 
     292                 :             : static void
     293                 :         107 : mime_info_text (GMarkupParseContext  *context,
     294                 :             :                 const gchar          *text,
     295                 :             :                 gsize                 text_len,
     296                 :             :                 gpointer              user_data,
     297                 :             :                 GError              **error)
     298                 :             : {
     299                 :         107 :   MimeParser *parser = user_data;
     300                 :             : 
     301                 :         107 :   if (parser->current_type == MIME_TAG_TYPE_COMMENT &&
     302                 :          51 :       parser->current_lang_level > parser->comment_lang_level)
     303                 :             :     {
     304                 :           1 :       g_free (parser->comment);
     305                 :           1 :       parser->comment = g_strndup (text, text_len);
     306                 :           1 :       parser->comment_lang_level = parser->current_lang_level;
     307                 :             :     }
     308                 :         107 : }
     309                 :             : 
     310                 :             : static char *
     311                 :           3 : load_comment_for_mime_helper (const char *dir,
     312                 :             :                               const char *basename)
     313                 :             : {
     314                 :             :   GMarkupParseContext *context;
     315                 :             :   char *filename, *data;
     316                 :             :   gsize len;
     317                 :             :   gboolean res;
     318                 :           3 :   MimeParser parse_data = {0};
     319                 :           3 :   GMarkupParser parser = {
     320                 :             :     mime_info_start_element,
     321                 :             :     mime_info_end_element,
     322                 :             :     mime_info_text,
     323                 :             :     NULL,
     324                 :             :     NULL
     325                 :             :   };
     326                 :             : 
     327                 :           3 :   filename = g_build_filename (dir, basename, NULL);
     328                 :             : 
     329                 :           3 :   res = g_file_get_contents (filename,  &data,  &len,  NULL);
     330                 :           3 :   g_free (filename);
     331                 :           3 :   if (!res)
     332                 :           2 :     return NULL;
     333                 :             : 
     334                 :           1 :   context = g_markup_parse_context_new (&parser, G_MARKUP_DEFAULT_FLAGS, &parse_data, NULL);
     335                 :           1 :   res = g_markup_parse_context_parse (context, data, len, NULL);
     336                 :           1 :   g_free (data);
     337                 :           1 :   g_markup_parse_context_free (context);
     338                 :             : 
     339                 :           1 :   if (!res)
     340                 :           0 :     return NULL;
     341                 :             : 
     342                 :           1 :   return parse_data.comment;
     343                 :             : }
     344                 :             : 
     345                 :             : 
     346                 :             : static char *
     347                 :           1 : load_comment_for_mime (const char *mimetype)
     348                 :             : {
     349                 :             :   const char * const *dirs;
     350                 :             :   char *basename;
     351                 :             :   char *comment;
     352                 :             :   gsize i;
     353                 :             : 
     354                 :           1 :   basename = g_strdup_printf ("%s.xml", mimetype);
     355                 :             : 
     356                 :           1 :   dirs = g_content_type_get_mime_dirs ();
     357                 :           3 :   for (i = 0; dirs[i] != NULL; i++)
     358                 :             :     {
     359                 :           3 :       comment = load_comment_for_mime_helper (dirs[i], basename);
     360                 :           3 :       if (comment)
     361                 :             :         {
     362                 :           1 :           g_free (basename);
     363                 :           1 :           return comment;
     364                 :             :         }
     365                 :             :     }
     366                 :           0 :   g_free (basename);
     367                 :             : 
     368                 :           0 :   return g_strdup_printf (_("%s type"), mimetype);
     369                 :             : }
     370                 :             : 
     371                 :             : gchar *
     372                 :           1 : g_content_type_get_description_impl (const gchar *type)
     373                 :             : {
     374                 :             :   static GHashTable *type_comment_cache = NULL;
     375                 :           1 :   gchar *type_copy = NULL;
     376                 :             :   gchar *comment;
     377                 :             : 
     378                 :           1 :   g_return_val_if_fail (type != NULL, NULL);
     379                 :             : 
     380                 :           1 :   G_LOCK (gio_xdgmime);
     381                 :           1 :   g_begin_ignore_leaks ();
     382                 :           1 :   type = xdg_mime_unalias_mime_type (type);
     383                 :           1 :   g_end_ignore_leaks ();
     384                 :             : 
     385                 :           1 :   if (type_comment_cache == NULL)
     386                 :           1 :     type_comment_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
     387                 :             : 
     388                 :           1 :   comment = g_hash_table_lookup (type_comment_cache, type);
     389                 :           1 :   comment = g_strdup (comment);
     390                 :             : 
     391                 :           1 :   if (comment != NULL)
     392                 :             :     {
     393                 :           0 :       G_UNLOCK (gio_xdgmime);
     394                 :           0 :       return g_steal_pointer (&comment);
     395                 :             :     }
     396                 :             : 
     397                 :           1 :   type_copy = g_strdup (type);
     398                 :             : 
     399                 :           1 :   G_UNLOCK (gio_xdgmime);
     400                 :           1 :   comment = load_comment_for_mime (type_copy);
     401                 :           1 :   G_LOCK (gio_xdgmime);
     402                 :             : 
     403                 :           1 :   g_hash_table_insert (type_comment_cache,
     404                 :             :                        g_steal_pointer (&type_copy),
     405                 :           1 :                        g_strdup (comment));
     406                 :           1 :   G_UNLOCK (gio_xdgmime);
     407                 :             : 
     408                 :           1 :   return g_steal_pointer (&comment);
     409                 :             : }
     410                 :             : 
     411                 :             : char *
     412                 :           2 : g_content_type_get_mime_type_impl (const char *type)
     413                 :             : {
     414                 :           2 :   g_return_val_if_fail (type != NULL, NULL);
     415                 :             : 
     416                 :           2 :   return g_strdup (type);
     417                 :             : }
     418                 :             : 
     419                 :             : static GIcon *
     420                 :         156 : g_content_type_get_icon_internal (const gchar *type,
     421                 :             :                                   gboolean     symbolic)
     422                 :             : {
     423                 :             :   char *mimetype_icon;
     424                 :         156 :   char *generic_mimetype_icon = NULL;
     425                 :             :   char *q;
     426                 :             :   char *icon_names[6];
     427                 :         156 :   int n = 0;
     428                 :             :   GIcon *themed_icon;
     429                 :             :   const char  *xdg_icon;
     430                 :             :   int i;
     431                 :             : 
     432                 :         156 :   g_return_val_if_fail (type != NULL, NULL);
     433                 :             : 
     434                 :         156 :   G_LOCK (gio_xdgmime);
     435                 :         156 :   g_begin_ignore_leaks ();
     436                 :         156 :   xdg_icon = xdg_mime_get_icon (type);
     437                 :         156 :   g_end_ignore_leaks ();
     438                 :         156 :   G_UNLOCK (gio_xdgmime);
     439                 :             : 
     440                 :         156 :   if (xdg_icon)
     441                 :           0 :     icon_names[n++] = g_strdup (xdg_icon);
     442                 :             : 
     443                 :         156 :   mimetype_icon = g_strdup (type);
     444                 :         312 :   while ((q = strchr (mimetype_icon, '/')) != NULL)
     445                 :         156 :     *q = '-';
     446                 :             : 
     447                 :         156 :   icon_names[n++] = mimetype_icon;
     448                 :             : 
     449                 :         156 :   generic_mimetype_icon = g_content_type_get_generic_icon_name (type);
     450                 :         156 :   if (generic_mimetype_icon)
     451                 :         156 :     icon_names[n++] = generic_mimetype_icon;
     452                 :             : 
     453                 :         156 :   if (symbolic)
     454                 :             :     {
     455                 :         234 :       for (i = 0; i < n; i++)
     456                 :             :         {
     457                 :         156 :           icon_names[n + i] = icon_names[i];
     458                 :         156 :           icon_names[i] = g_strconcat (icon_names[i], "-symbolic", NULL);
     459                 :             :         }
     460                 :             : 
     461                 :          78 :       n += n;
     462                 :             :     }
     463                 :             : 
     464                 :         156 :   themed_icon = g_themed_icon_new_from_names (icon_names, n);
     465                 :             : 
     466                 :         624 :   for (i = 0; i < n; i++)
     467                 :         468 :     g_free (icon_names[i]);
     468                 :             : 
     469                 :         156 :   return themed_icon;
     470                 :             : }
     471                 :             : 
     472                 :             : GIcon *
     473                 :          78 : g_content_type_get_icon_impl (const gchar *type)
     474                 :             : {
     475                 :          78 :   return g_content_type_get_icon_internal (type, FALSE);
     476                 :             : }
     477                 :             : 
     478                 :             : GIcon *
     479                 :          78 : g_content_type_get_symbolic_icon_impl (const gchar *type)
     480                 :             : {
     481                 :          78 :   return g_content_type_get_icon_internal (type, TRUE);
     482                 :             : }
     483                 :             : 
     484                 :             : gchar *
     485                 :         156 : g_content_type_get_generic_icon_name_impl (const gchar *type)
     486                 :             : {
     487                 :             :   const gchar *xdg_icon_name;
     488                 :             :   gchar *icon_name;
     489                 :             : 
     490                 :         156 :   g_return_val_if_fail (type != NULL, NULL);
     491                 :             : 
     492                 :         156 :   G_LOCK (gio_xdgmime);
     493                 :         156 :   g_begin_ignore_leaks ();
     494                 :         156 :   xdg_icon_name = xdg_mime_get_generic_icon (type);
     495                 :         156 :   g_end_ignore_leaks ();
     496                 :         156 :   G_UNLOCK (gio_xdgmime);
     497                 :             : 
     498                 :         156 :   if (!xdg_icon_name)
     499                 :             :     {
     500                 :             :       const char *p;
     501                 :         106 :       const char *suffix = "-x-generic";
     502                 :             : 
     503                 :         106 :       p = strchr (type, '/');
     504                 :         106 :       if (p == NULL)
     505                 :           0 :         p = type + strlen (type);
     506                 :             : 
     507                 :         106 :       icon_name = g_malloc (p - type + strlen (suffix) + 1);
     508                 :         106 :       memcpy (icon_name, type, p - type);
     509                 :         106 :       memcpy (icon_name + (p - type), suffix, strlen (suffix));
     510                 :         106 :       icon_name[(p - type) + strlen (suffix)] = 0;
     511                 :             :     }
     512                 :             :   else
     513                 :             :     {
     514                 :          50 :       icon_name = g_strdup (xdg_icon_name);
     515                 :             :     }
     516                 :             : 
     517                 :         156 :   return icon_name;
     518                 :             : }
     519                 :             : 
     520                 :             : gboolean
     521                 :           3 : g_content_type_can_be_executable_impl (const gchar *type)
     522                 :             : {
     523                 :           3 :   g_return_val_if_fail (type != NULL, FALSE);
     524                 :             : 
     525                 :           5 :   if (g_content_type_is_a (type, "application/x-executable")  ||
     526                 :           2 :       g_content_type_is_a (type, "text/plain"))
     527                 :           2 :     return TRUE;
     528                 :             : 
     529                 :           1 :   return FALSE;
     530                 :             : }
     531                 :             : 
     532                 :             : static gboolean
     533                 :           7 : looks_like_text (const guchar *data, gsize data_size)
     534                 :             : {
     535                 :             :   gsize i;
     536                 :             :   char c;
     537                 :             : 
     538                 :          18 :   for (i = 0; i < data_size; i++)
     539                 :             :     {
     540                 :          18 :       c = data[i];
     541                 :             : 
     542                 :          18 :       if (g_ascii_iscntrl (c) &&
     543                 :           7 :           !g_ascii_isspace (c) &&
     544                 :             :           c != '\b')
     545                 :           7 :         return FALSE;
     546                 :             :     }
     547                 :           0 :   return TRUE;
     548                 :             : }
     549                 :             : 
     550                 :             : gchar *
     551                 :         167 : g_content_type_from_mime_type_impl (const gchar *mime_type)
     552                 :             : {
     553                 :             :   char *umime;
     554                 :             : 
     555                 :         167 :   g_return_val_if_fail (mime_type != NULL, NULL);
     556                 :             : 
     557                 :         167 :   G_LOCK (gio_xdgmime);
     558                 :         167 :   g_begin_ignore_leaks ();
     559                 :             :   /* mime type and content type are same on unixes */
     560                 :         167 :   umime = g_strdup (xdg_mime_unalias_mime_type (mime_type));
     561                 :         167 :   g_end_ignore_leaks ();
     562                 :         167 :   G_UNLOCK (gio_xdgmime);
     563                 :             : 
     564                 :         167 :   return umime;
     565                 :             : }
     566                 :             : 
     567                 :             : gchar *
     568                 :          50 : g_content_type_guess_impl (const gchar  *filename,
     569                 :             :                            const guchar *data,
     570                 :             :                            gsize         data_size,
     571                 :             :                            gboolean     *result_uncertain)
     572                 :             : {
     573                 :             :   char *basename;
     574                 :             :   const char *name_mimetypes[10], *sniffed_mimetype;
     575                 :             :   char *mimetype;
     576                 :             :   int i;
     577                 :             :   int n_name_mimetypes;
     578                 :             :   int sniffed_prio;
     579                 :             : 
     580                 :          50 :   sniffed_prio = 0;
     581                 :          50 :   n_name_mimetypes = 0;
     582                 :          50 :   sniffed_mimetype = XDG_MIME_TYPE_UNKNOWN;
     583                 :             : 
     584                 :          50 :   if (result_uncertain)
     585                 :          37 :     *result_uncertain = FALSE;
     586                 :             : 
     587                 :             :   /* our test suite and potentially other code used -1 in the past, which is
     588                 :             :    * not documented and not allowed; guard against that */
     589                 :          50 :   g_return_val_if_fail (data_size != (gsize) -1, g_strdup (XDG_MIME_TYPE_UNKNOWN));
     590                 :             : 
     591                 :          50 :   G_LOCK (gio_xdgmime);
     592                 :          50 :   g_begin_ignore_leaks ();
     593                 :             : 
     594                 :          50 :   if (filename)
     595                 :             :     {
     596                 :          46 :       i = strlen (filename);
     597                 :          46 :       if (i > 0 && filename[i - 1] == '/')
     598                 :             :         {
     599                 :           1 :           name_mimetypes[0] = "inode/directory";
     600                 :           1 :           name_mimetypes[1] = NULL;
     601                 :           1 :           n_name_mimetypes = 1;
     602                 :           1 :           if (result_uncertain)
     603                 :           1 :             *result_uncertain = TRUE;
     604                 :             :         }
     605                 :             :       else
     606                 :             :         {
     607                 :          45 :           basename = g_path_get_basename (filename);
     608                 :          45 :           n_name_mimetypes = xdg_mime_get_mime_types_from_file_name (basename, name_mimetypes, 10);
     609                 :          45 :           g_free (basename);
     610                 :             :         }
     611                 :             :     }
     612                 :             : 
     613                 :             :   /* Got an extension match, and no conflicts. This is it. */
     614                 :          50 :   if (n_name_mimetypes == 1)
     615                 :             :     {
     616                 :           5 :       gchar *s = g_strdup (name_mimetypes[0]);
     617                 :           5 :       g_end_ignore_leaks ();
     618                 :           5 :       G_UNLOCK (gio_xdgmime);
     619                 :           5 :       return s;
     620                 :             :     }
     621                 :             : 
     622                 :          45 :   if (data)
     623                 :             :     {
     624                 :          21 :       sniffed_mimetype = xdg_mime_get_mime_type_for_data (data, data_size, &sniffed_prio);
     625                 :          21 :       if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
     626                 :           7 :           data &&
     627                 :           7 :           looks_like_text (data, data_size))
     628                 :           0 :         sniffed_mimetype = "text/plain";
     629                 :             : 
     630                 :             :       /* For security reasons we don't ever want to sniff desktop files
     631                 :             :        * where we know the filename and it doesn't have a .desktop extension.
     632                 :             :        * This is because desktop files allow executing any application and
     633                 :             :        * we don't want to make it possible to hide them looking like something
     634                 :             :        * else.
     635                 :             :        */
     636                 :          21 :       if (filename != NULL &&
     637                 :          17 :           strcmp (sniffed_mimetype, "application/x-desktop") == 0)
     638                 :           1 :         sniffed_mimetype = "text/plain";
     639                 :             :     }
     640                 :             : 
     641                 :          45 :   if (n_name_mimetypes == 0)
     642                 :             :     {
     643                 :          41 :       if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
     644                 :             :           result_uncertain)
     645                 :          24 :         *result_uncertain = TRUE;
     646                 :             : 
     647                 :          41 :       mimetype = g_strdup (sniffed_mimetype);
     648                 :             :     }
     649                 :             :   else
     650                 :             :     {
     651                 :           4 :       mimetype = NULL;
     652                 :           4 :       if (sniffed_mimetype != XDG_MIME_TYPE_UNKNOWN)
     653                 :             :         {
     654                 :           3 :           if (sniffed_prio >= 80) /* High priority sniffing match, use that */
     655                 :           0 :             mimetype = g_strdup (sniffed_mimetype);
     656                 :             :           else
     657                 :             :             {
     658                 :             :               /* There are conflicts between the name matches and we
     659                 :             :                * have a sniffed type, use that as a tie breaker.
     660                 :             :                */
     661                 :           6 :               for (i = 0; i < n_name_mimetypes; i++)
     662                 :             :                 {
     663                 :           6 :                   if ( xdg_mime_mime_type_subclass (name_mimetypes[i], sniffed_mimetype))
     664                 :             :                     {
     665                 :             :                       /* This nametype match is derived from (or the same as)
     666                 :             :                        * the sniffed type). This is probably it.
     667                 :             :                        */
     668                 :           3 :                       mimetype = g_strdup (name_mimetypes[i]);
     669                 :           3 :                       break;
     670                 :             :                     }
     671                 :             :                 }
     672                 :             :             }
     673                 :             :         }
     674                 :             : 
     675                 :           4 :       if (mimetype == NULL)
     676                 :             :         {
     677                 :             :           /* Conflicts, and sniffed type was no help or not there.
     678                 :             :            * Guess on the first one
     679                 :             :            */
     680                 :           1 :           mimetype = g_strdup (name_mimetypes[0]);
     681                 :           1 :           if (result_uncertain)
     682                 :           1 :             *result_uncertain = TRUE;
     683                 :             :         }
     684                 :             :     }
     685                 :             : 
     686                 :          45 :   g_end_ignore_leaks ();
     687                 :          45 :   G_UNLOCK (gio_xdgmime);
     688                 :             : 
     689                 :          45 :   return mimetype;
     690                 :             : }
     691                 :             : 
     692                 :             : static void
     693                 :          14 : enumerate_mimetypes_subdir (const char *dir,
     694                 :             :                             const char *prefix,
     695                 :             :                             GHashTable *mimetypes)
     696                 :             : {
     697                 :             :   DIR *d;
     698                 :             :   struct dirent *ent;
     699                 :             :   char *mimetype;
     700                 :             : 
     701                 :          14 :   d = opendir (dir);
     702                 :          14 :   if (d)
     703                 :             :     {
     704                 :        1033 :       while ((ent = readdir (d)) != NULL)
     705                 :             :         {
     706                 :        1005 :           if (g_str_has_suffix (ent->d_name, ".xml"))
     707                 :             :             {
     708                 :         851 :               mimetype = g_strdup_printf ("%s/%.*s", prefix, (int) strlen (ent->d_name) - 4, ent->d_name);
     709                 :         851 :               g_hash_table_replace (mimetypes, mimetype, NULL);
     710                 :             :             }
     711                 :             :         }
     712                 :          14 :       closedir (d);
     713                 :             :     }
     714                 :          14 : }
     715                 :             : 
     716                 :             : static void
     717                 :           3 : enumerate_mimetypes_dir (const char *dir,
     718                 :             :                          GHashTable *mimetypes)
     719                 :             : {
     720                 :             :   DIR *d;
     721                 :             :   struct dirent *ent;
     722                 :             :   const char *mimedir;
     723                 :             :   char *name;
     724                 :             : 
     725                 :           3 :   mimedir = dir;
     726                 :             : 
     727                 :           3 :   d = opendir (mimedir);
     728                 :           3 :   if (d)
     729                 :             :     {
     730                 :          29 :       while ((ent = readdir (d)) != NULL)
     731                 :             :         {
     732                 :          27 :           if (strcmp (ent->d_name, "packages") != 0)
     733                 :             :             {
     734                 :          26 :               name = g_build_filename (mimedir, ent->d_name, NULL);
     735                 :          26 :               if (g_file_test (name, G_FILE_TEST_IS_DIR))
     736                 :          14 :                 enumerate_mimetypes_subdir (name, ent->d_name, mimetypes);
     737                 :          26 :               g_free (name);
     738                 :             :             }
     739                 :             :         }
     740                 :           1 :       closedir (d);
     741                 :             :     }
     742                 :           3 : }
     743                 :             : 
     744                 :             : GList *
     745                 :           1 : g_content_types_get_registered_impl (void)
     746                 :             : {
     747                 :             :   const char * const *dirs;
     748                 :             :   GHashTable *mimetypes;
     749                 :             :   GHashTableIter iter;
     750                 :             :   gpointer key;
     751                 :             :   gsize i;
     752                 :             :   GList *l;
     753                 :             : 
     754                 :           1 :   mimetypes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
     755                 :             : 
     756                 :           1 :   dirs = g_content_type_get_mime_dirs ();
     757                 :           4 :   for (i = 0; dirs[i] != NULL; i++)
     758                 :           3 :     enumerate_mimetypes_dir (dirs[i], mimetypes);
     759                 :             : 
     760                 :           1 :   l = NULL;
     761                 :           1 :   g_hash_table_iter_init (&iter, mimetypes);
     762                 :         852 :   while (g_hash_table_iter_next (&iter, &key, NULL))
     763                 :             :     {
     764                 :         851 :       l = g_list_prepend (l, key);
     765                 :         851 :       g_hash_table_iter_steal (&iter);
     766                 :             :     }
     767                 :             : 
     768                 :           1 :   g_hash_table_destroy (mimetypes);
     769                 :             : 
     770                 :           1 :   return l;
     771                 :             : }
     772                 :             : 
     773                 :             : 
     774                 :             : /* tree magic data */
     775                 :             : static GList *tree_matches = NULL;
     776                 :             : static gboolean need_reload = FALSE;
     777                 :             : 
     778                 :             : G_LOCK_DEFINE_STATIC (gio_treemagic);
     779                 :             : 
     780                 :             : typedef struct
     781                 :             : {
     782                 :             :   gchar *path;
     783                 :             :   GFileType type;
     784                 :             :   guint match_case : 1;
     785                 :             :   guint executable : 1;
     786                 :             :   guint non_empty  : 1;
     787                 :             :   guint on_disc    : 1;
     788                 :             :   gchar *mimetype;
     789                 :             :   GList *matches;
     790                 :             : } TreeMatchlet;
     791                 :             : 
     792                 :             : typedef struct
     793                 :             : {
     794                 :             :   gchar *contenttype;
     795                 :             :   gint priority;
     796                 :             :   GList *matches;
     797                 :             : } TreeMatch;
     798                 :             : 
     799                 :             : 
     800                 :             : static void
     801                 :           0 : tree_matchlet_free (TreeMatchlet *matchlet)
     802                 :             : {
     803                 :           0 :   g_list_free_full (matchlet->matches, (GDestroyNotify) tree_matchlet_free);
     804                 :           0 :   g_free (matchlet->path);
     805                 :           0 :   g_free (matchlet->mimetype);
     806                 :           0 :   g_slice_free (TreeMatchlet, matchlet);
     807                 :           0 : }
     808                 :             : 
     809                 :             : static void
     810                 :           0 : tree_match_free (TreeMatch *match)
     811                 :             : {
     812                 :           0 :   g_list_free_full (match->matches, (GDestroyNotify) tree_matchlet_free);
     813                 :           0 :   g_free (match->contenttype);
     814                 :           0 :   g_slice_free (TreeMatch, match);
     815                 :           0 : }
     816                 :             : 
     817                 :             : static TreeMatch *
     818                 :          12 : parse_header (gchar *line)
     819                 :             : {
     820                 :             :   gint len;
     821                 :             :   gchar *s;
     822                 :             :   TreeMatch *match;
     823                 :             : 
     824                 :          12 :   len = strlen (line);
     825                 :             : 
     826                 :          12 :   if (line[0] != '[' || line[len - 1] != ']')
     827                 :           0 :     return NULL;
     828                 :             : 
     829                 :          12 :   line[len - 1] = 0;
     830                 :          12 :   s = strchr (line, ':');
     831                 :          12 :   if (s == NULL)
     832                 :           0 :     return NULL;
     833                 :             : 
     834                 :          12 :   match = g_slice_new0 (TreeMatch);
     835                 :          12 :   match->priority = atoi (line + 1);
     836                 :          12 :   match->contenttype = g_strdup (s + 1);
     837                 :             : 
     838                 :          12 :   return match;
     839                 :             : }
     840                 :             : 
     841                 :             : static TreeMatchlet *
     842                 :          25 : parse_match_line (gchar *line,
     843                 :             :                   gint  *depth)
     844                 :             : {
     845                 :             :   gchar *s, *p;
     846                 :             :   TreeMatchlet *matchlet;
     847                 :             :   gchar **parts;
     848                 :             :   gint i;
     849                 :             : 
     850                 :          25 :   matchlet = g_slice_new0 (TreeMatchlet);
     851                 :             : 
     852                 :          25 :   if (line[0] == '>')
     853                 :             :     {
     854                 :          25 :       *depth = 0;
     855                 :          25 :       s = line;
     856                 :             :     }
     857                 :             :   else
     858                 :             :     {
     859                 :           0 :       *depth = atoi (line);
     860                 :           0 :       s = strchr (line, '>');
     861                 :           0 :       if (s == NULL)
     862                 :           0 :         goto handle_error;
     863                 :             :     }
     864                 :          25 :   s += 2;
     865                 :          25 :   p = strchr (s, '"');
     866                 :          25 :   if (p == NULL)
     867                 :           0 :     goto handle_error;
     868                 :          25 :   *p = 0;
     869                 :             : 
     870                 :          25 :   matchlet->path = g_strdup (s);
     871                 :          25 :   s = p + 1;
     872                 :          25 :   parts = g_strsplit (s, ",", 0);
     873                 :          25 :   if (strcmp (parts[0], "=file") == 0)
     874                 :          16 :     matchlet->type = G_FILE_TYPE_REGULAR;
     875                 :           9 :   else if (strcmp (parts[0], "=directory") == 0)
     876                 :           8 :     matchlet->type = G_FILE_TYPE_DIRECTORY;
     877                 :           1 :   else if (strcmp (parts[0], "=link") == 0)
     878                 :           0 :     matchlet->type = G_FILE_TYPE_SYMBOLIC_LINK;
     879                 :             :   else
     880                 :           1 :     matchlet->type = G_FILE_TYPE_UNKNOWN;
     881                 :          41 :   for (i = 1; parts[i]; i++)
     882                 :             :     {
     883                 :          16 :       if (strcmp (parts[i], "executable") == 0)
     884                 :           1 :         matchlet->executable = 1;
     885                 :          15 :       else if (strcmp (parts[i], "match-case") == 0)
     886                 :           7 :         matchlet->match_case = 1;
     887                 :           8 :       else if (strcmp (parts[i], "non-empty") == 0)
     888                 :           8 :         matchlet->non_empty = 1;
     889                 :           0 :       else if (strcmp (parts[i], "on-disc") == 0)
     890                 :           0 :         matchlet->on_disc = 1;
     891                 :             :       else
     892                 :           0 :         matchlet->mimetype = g_strdup (parts[i]);
     893                 :             :     }
     894                 :             : 
     895                 :          25 :   g_strfreev (parts);
     896                 :             : 
     897                 :          25 :   return matchlet;
     898                 :             : 
     899                 :           0 : handle_error:
     900                 :           0 :   g_slice_free (TreeMatchlet, matchlet);
     901                 :           0 :   return NULL;
     902                 :             : }
     903                 :             : 
     904                 :             : static gint
     905                 :          11 : cmp_match (gconstpointer a, gconstpointer b)
     906                 :             : {
     907                 :          11 :   const TreeMatch *aa = (const TreeMatch *)a;
     908                 :          11 :   const TreeMatch *bb = (const TreeMatch *)b;
     909                 :             : 
     910                 :          11 :   return bb->priority - aa->priority;
     911                 :             : }
     912                 :             : 
     913                 :             : static void
     914                 :          12 : insert_match (TreeMatch *match)
     915                 :             : {
     916                 :          12 :   tree_matches = g_list_insert_sorted (tree_matches, match, cmp_match);
     917                 :          12 : }
     918                 :             : 
     919                 :             : static void
     920                 :          25 : insert_matchlet (TreeMatch    *match,
     921                 :             :                  TreeMatchlet *matchlet,
     922                 :             :                  gint          depth)
     923                 :             : {
     924                 :          25 :   if (depth == 0)
     925                 :          25 :     match->matches = g_list_append (match->matches, matchlet);
     926                 :             :   else
     927                 :             :     {
     928                 :             :       GList *last;
     929                 :             :       TreeMatchlet *m;
     930                 :             : 
     931                 :           0 :       last = g_list_last (match->matches);
     932                 :           0 :       if (!last)
     933                 :             :         {
     934                 :           0 :           tree_matchlet_free (matchlet);
     935                 :           0 :           g_warning ("can't insert tree matchlet at depth %d", depth);
     936                 :           0 :           return;
     937                 :             :         }
     938                 :             : 
     939                 :           0 :       m = (TreeMatchlet *) last->data;
     940                 :           0 :       while (--depth > 0)
     941                 :             :         {
     942                 :           0 :           last = g_list_last (m->matches);
     943                 :           0 :           if (!last)
     944                 :             :             {
     945                 :           0 :               tree_matchlet_free (matchlet);
     946                 :           0 :               g_warning ("can't insert tree matchlet at depth %d", depth);
     947                 :           0 :               return;
     948                 :             :             }
     949                 :             : 
     950                 :           0 :           m = (TreeMatchlet *) last->data;
     951                 :             :         }
     952                 :           0 :       m->matches = g_list_append (m->matches, matchlet);
     953                 :             :     }
     954                 :             : }
     955                 :             : 
     956                 :             : static void
     957                 :           3 : read_tree_magic_from_directory (const gchar *prefix)
     958                 :             : {
     959                 :             :   gchar *filename;
     960                 :             :   gchar *text;
     961                 :             :   gsize len;
     962                 :             :   gchar **lines;
     963                 :             :   gsize i;
     964                 :             :   TreeMatch *match;
     965                 :             :   TreeMatchlet *matchlet;
     966                 :             :   gint depth;
     967                 :             : 
     968                 :           3 :   filename = g_build_filename (prefix, "treemagic", NULL);
     969                 :             : 
     970                 :           3 :   if (g_file_get_contents (filename, &text, &len, NULL))
     971                 :             :     {
     972                 :           1 :       if (strcmp (text, "MIME-TreeMagic") == 0)
     973                 :             :         {
     974                 :           1 :           lines = g_strsplit (text + strlen ("MIME-TreeMagic") + 2, "\n", 0);
     975                 :           1 :           match = NULL;
     976                 :          38 :           for (i = 0; lines[i] && lines[i][0]; i++)
     977                 :             :             {
     978                 :          37 :               if (lines[i][0] == '[' && (match = parse_header (lines[i])) != NULL)
     979                 :             :                 {
     980                 :          12 :                   insert_match (match);
     981                 :             :                 }
     982                 :          25 :               else if (match != NULL)
     983                 :             :                 {
     984                 :          25 :                   matchlet = parse_match_line (lines[i], &depth);
     985                 :          25 :                   if (matchlet == NULL)
     986                 :             :                     {
     987                 :           0 :                       g_warning ("%s: body corrupt; skipping", filename);
     988                 :           0 :                       break;
     989                 :             :                     }
     990                 :          25 :                   insert_matchlet (match, matchlet, depth);
     991                 :             :                 }
     992                 :             :               else
     993                 :             :                 {
     994                 :           0 :                   g_warning ("%s: header corrupt; skipping", filename);
     995                 :           0 :                   break;
     996                 :             :                 }
     997                 :             :             }
     998                 :             : 
     999                 :           1 :           g_strfreev (lines);
    1000                 :             :         }
    1001                 :             :       else
    1002                 :           0 :         g_warning ("%s: header not found, skipping", filename);
    1003                 :             : 
    1004                 :           1 :       g_free (text);
    1005                 :             :     }
    1006                 :             : 
    1007                 :           3 :   g_free (filename);
    1008                 :           3 : }
    1009                 :             : 
    1010                 :             : static void
    1011                 :           2 : tree_magic_schedule_reload (void)
    1012                 :             : {
    1013                 :           2 :   need_reload = TRUE;
    1014                 :           2 : }
    1015                 :             : 
    1016                 :             : static void
    1017                 :           0 : xdg_mime_reload (void *user_data)
    1018                 :             : {
    1019                 :           0 :   tree_magic_schedule_reload ();
    1020                 :           0 : }
    1021                 :             : 
    1022                 :             : static void
    1023                 :           1 : tree_magic_shutdown (void)
    1024                 :             : {
    1025                 :           1 :   g_list_free_full (tree_matches, (GDestroyNotify) tree_match_free);
    1026                 :           1 :   tree_matches = NULL;
    1027                 :           1 : }
    1028                 :             : 
    1029                 :             : static void
    1030                 :           4 : tree_magic_init (void)
    1031                 :             : {
    1032                 :             :   static gboolean initialized = FALSE;
    1033                 :             :   gsize i;
    1034                 :             : 
    1035                 :           4 :   if (!initialized)
    1036                 :             :     {
    1037                 :           1 :       initialized = TRUE;
    1038                 :             : 
    1039                 :           1 :       xdg_mime_register_reload_callback (xdg_mime_reload, NULL, NULL);
    1040                 :           1 :       need_reload = TRUE;
    1041                 :             :     }
    1042                 :             : 
    1043                 :           4 :   if (need_reload)
    1044                 :             :     {
    1045                 :             :       const char * const *dirs;
    1046                 :             : 
    1047                 :           1 :       need_reload = FALSE;
    1048                 :             : 
    1049                 :           1 :       tree_magic_shutdown ();
    1050                 :             : 
    1051                 :           1 :       dirs = g_content_type_get_mime_dirs ();
    1052                 :           4 :       for (i = 0; dirs[i] != NULL; i++)
    1053                 :           3 :         read_tree_magic_from_directory (dirs[i]);
    1054                 :             :     }
    1055                 :           4 : }
    1056                 :             : 
    1057                 :             : /* a filtering enumerator */
    1058                 :             : 
    1059                 :             : typedef struct
    1060                 :             : {
    1061                 :             :   gchar *path;
    1062                 :             :   gint depth;
    1063                 :             :   gboolean ignore_case;
    1064                 :             :   gchar **components;
    1065                 :             :   gchar **case_components;
    1066                 :             :   GFileEnumerator **enumerators;
    1067                 :             :   GFile **children;
    1068                 :             : } Enumerator;
    1069                 :             : 
    1070                 :             : static gboolean
    1071                 :          99 : component_match (Enumerator  *e,
    1072                 :             :                  gint         depth,
    1073                 :             :                  const gchar *name)
    1074                 :             : {
    1075                 :             :   gchar *case_folded, *key, *utf8_name;
    1076                 :             :   gboolean found;
    1077                 :             : 
    1078                 :          99 :   if (strcmp (name, e->components[depth]) == 0)
    1079                 :           2 :     return TRUE;
    1080                 :             : 
    1081                 :          97 :   if (!e->ignore_case)
    1082                 :          27 :     return FALSE;
    1083                 :             : 
    1084                 :          70 :   utf8_name = g_filename_to_utf8 (name, -1, NULL, NULL, NULL);
    1085                 :          70 :   if (utf8_name == NULL)
    1086                 :          18 :     utf8_name = g_utf8_make_valid (name, -1);
    1087                 :             : 
    1088                 :          70 :   case_folded = g_utf8_casefold (utf8_name, -1);
    1089                 :          70 :   key = g_utf8_collate_key (case_folded, -1);
    1090                 :             : 
    1091                 :          70 :   found = strcmp (key, e->case_components[depth]) == 0;
    1092                 :             : 
    1093                 :          70 :   g_free (utf8_name);
    1094                 :          70 :   g_free (case_folded);
    1095                 :          70 :   g_free (key);
    1096                 :             : 
    1097                 :          70 :   return found;
    1098                 :             : }
    1099                 :             : 
    1100                 :             : static GFile *
    1101                 :         155 : next_match_recurse (Enumerator *e,
    1102                 :             :                     gint        depth)
    1103                 :             : {
    1104                 :             :   GFile *file;
    1105                 :             :   GFileInfo *info;
    1106                 :             :   const gchar *name;
    1107                 :             : 
    1108                 :             :   while (TRUE)
    1109                 :             :     {
    1110                 :         251 :       if (e->enumerators[depth] == NULL)
    1111                 :             :         {
    1112                 :         152 :           if (depth > 0)
    1113                 :             :             {
    1114                 :          56 :               file = next_match_recurse (e, depth - 1);
    1115                 :          56 :               if (file)
    1116                 :             :                 {
    1117                 :           0 :                   e->children[depth] = file;
    1118                 :           0 :                   e->enumerators[depth] = g_file_enumerate_children (file,
    1119                 :             :                                                                      G_FILE_ATTRIBUTE_STANDARD_NAME,
    1120                 :             :                                                                      G_FILE_QUERY_INFO_NONE,
    1121                 :             :                                                                      NULL,
    1122                 :             :                                                                      NULL);
    1123                 :             :                 }
    1124                 :             :             }
    1125                 :         152 :           if (e->enumerators[depth] == NULL)
    1126                 :         152 :             return NULL;
    1127                 :             :         }
    1128                 :             : 
    1129                 :         195 :       while ((info = g_file_enumerator_next_file (e->enumerators[depth], NULL, NULL)))
    1130                 :             :         {
    1131                 :          99 :           name = g_file_info_get_name (info);
    1132                 :          99 :           if (component_match (e, depth, name))
    1133                 :             :             {
    1134                 :           3 :               file = g_file_get_child (e->children[depth], name);
    1135                 :           3 :               g_object_unref (info);
    1136                 :           3 :               return file;
    1137                 :             :             }
    1138                 :          96 :           g_object_unref (info);
    1139                 :             :         }
    1140                 :             : 
    1141                 :          96 :       g_object_unref (e->enumerators[depth]);
    1142                 :          96 :       e->enumerators[depth] = NULL;
    1143                 :          96 :       g_object_unref (e->children[depth]);
    1144                 :          96 :       e->children[depth] = NULL;
    1145                 :             :     }
    1146                 :             : }
    1147                 :             : 
    1148                 :             : static GFile *
    1149                 :          99 : enumerator_next (Enumerator *e)
    1150                 :             : {
    1151                 :          99 :   return next_match_recurse (e, e->depth - 1);
    1152                 :             : }
    1153                 :             : 
    1154                 :             : static Enumerator *
    1155                 :          99 : enumerator_new (GFile      *root,
    1156                 :             :                 const char *path,
    1157                 :             :                 gboolean    ignore_case)
    1158                 :             : {
    1159                 :             :   Enumerator *e;
    1160                 :             :   gint i;
    1161                 :             :   gchar *case_folded;
    1162                 :             : 
    1163                 :          99 :   e = g_new0 (Enumerator, 1);
    1164                 :          99 :   e->path = g_strdup (path);
    1165                 :          99 :   e->ignore_case = ignore_case;
    1166                 :             : 
    1167                 :          99 :   e->components = g_strsplit (e->path, G_DIR_SEPARATOR_S, -1);
    1168                 :          99 :   e->depth = g_strv_length (e->components);
    1169                 :          99 :   if (e->ignore_case)
    1170                 :             :     {
    1171                 :          71 :       e->case_components = g_new0 (char *, e->depth + 1);
    1172                 :         182 :       for (i = 0; e->components[i]; i++)
    1173                 :             :         {
    1174                 :         111 :           case_folded = g_utf8_casefold (e->components[i], -1);
    1175                 :         111 :           e->case_components[i] = g_utf8_collate_key (case_folded, -1);
    1176                 :         111 :           g_free (case_folded);
    1177                 :             :         }
    1178                 :             :     }
    1179                 :             : 
    1180                 :          99 :   e->children = g_new0 (GFile *, e->depth);
    1181                 :          99 :   e->children[0] = g_object_ref (root);
    1182                 :          99 :   e->enumerators = g_new0 (GFileEnumerator *, e->depth);
    1183                 :          99 :   e->enumerators[0] = g_file_enumerate_children (root,
    1184                 :             :                                                  G_FILE_ATTRIBUTE_STANDARD_NAME,
    1185                 :             :                                                  G_FILE_QUERY_INFO_NONE,
    1186                 :             :                                                  NULL,
    1187                 :             :                                                  NULL);
    1188                 :             : 
    1189                 :          99 :   return e;
    1190                 :             : }
    1191                 :             : 
    1192                 :             : static void
    1193                 :          99 : enumerator_free (Enumerator *e)
    1194                 :             : {
    1195                 :             :   gint i;
    1196                 :             : 
    1197                 :         254 :   for (i = 0; i < e->depth; i++)
    1198                 :             :     {
    1199                 :         155 :       if (e->enumerators[i])
    1200                 :           3 :         g_object_unref (e->enumerators[i]);
    1201                 :         155 :       if (e->children[i])
    1202                 :           3 :         g_object_unref (e->children[i]);
    1203                 :             :     }
    1204                 :             : 
    1205                 :          99 :   g_free (e->enumerators);
    1206                 :          99 :   g_free (e->children);
    1207                 :          99 :   g_strfreev (e->components);
    1208                 :          99 :   if (e->case_components)
    1209                 :          71 :     g_strfreev (e->case_components);
    1210                 :          99 :   g_free (e->path);
    1211                 :          99 :   g_free (e);
    1212                 :          99 : }
    1213                 :             : 
    1214                 :             : static gboolean
    1215                 :          99 : matchlet_match (TreeMatchlet *matchlet,
    1216                 :             :                 GFile        *root)
    1217                 :             : {
    1218                 :             :   GFile *file;
    1219                 :             :   GFileInfo *info;
    1220                 :             :   gboolean result;
    1221                 :             :   const gchar *attrs;
    1222                 :             :   Enumerator *e;
    1223                 :             :   GList *l;
    1224                 :             : 
    1225                 :          99 :   e = enumerator_new (root, matchlet->path, !matchlet->match_case);
    1226                 :             : 
    1227                 :             :   do
    1228                 :             :     {
    1229                 :          99 :       file = enumerator_next (e);
    1230                 :          99 :       if (!file)
    1231                 :             :         {
    1232                 :          96 :           enumerator_free (e);
    1233                 :          96 :           return FALSE;
    1234                 :             :         }
    1235                 :             : 
    1236                 :           3 :       if (matchlet->mimetype)
    1237                 :           0 :         attrs = G_FILE_ATTRIBUTE_STANDARD_TYPE ","
    1238                 :             :                 G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE ","
    1239                 :             :                 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE;
    1240                 :             :       else
    1241                 :           3 :         attrs = G_FILE_ATTRIBUTE_STANDARD_TYPE ","
    1242                 :             :                 G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE;
    1243                 :           3 :       info = g_file_query_info (file,
    1244                 :             :                                 attrs,
    1245                 :             :                                 G_FILE_QUERY_INFO_NONE,
    1246                 :             :                                 NULL,
    1247                 :             :                                 NULL);
    1248                 :           3 :       if (info)
    1249                 :             :         {
    1250                 :           3 :           result = TRUE;
    1251                 :             : 
    1252                 :           3 :           if (matchlet->type != G_FILE_TYPE_UNKNOWN &&
    1253                 :           3 :               g_file_info_get_file_type (info) != matchlet->type)
    1254                 :           0 :             result = FALSE;
    1255                 :             : 
    1256                 :           4 :           if (matchlet->executable &&
    1257                 :           1 :               !g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
    1258                 :           0 :             result = FALSE;
    1259                 :             :         }
    1260                 :             :       else
    1261                 :           0 :         result = FALSE;
    1262                 :             : 
    1263                 :           3 :       if (result && matchlet->non_empty)
    1264                 :             :         {
    1265                 :             :           GFileEnumerator *child_enum;
    1266                 :             :           GFileInfo *child_info;
    1267                 :             : 
    1268                 :           1 :           child_enum = g_file_enumerate_children (file,
    1269                 :             :                                                   G_FILE_ATTRIBUTE_STANDARD_NAME,
    1270                 :             :                                                   G_FILE_QUERY_INFO_NONE,
    1271                 :             :                                                   NULL,
    1272                 :             :                                                   NULL);
    1273                 :             : 
    1274                 :           1 :           if (child_enum)
    1275                 :             :             {
    1276                 :           1 :               child_info = g_file_enumerator_next_file (child_enum, NULL, NULL);
    1277                 :           1 :               if (child_info)
    1278                 :           1 :                 g_object_unref (child_info);
    1279                 :             :               else
    1280                 :           0 :                 result = FALSE;
    1281                 :           1 :               g_object_unref (child_enum);
    1282                 :             :             }
    1283                 :             :           else
    1284                 :           0 :             result = FALSE;
    1285                 :             :         }
    1286                 :             : 
    1287                 :           3 :       if (result && matchlet->mimetype)
    1288                 :             :         {
    1289                 :           0 :           if (strcmp (matchlet->mimetype, g_file_info_get_content_type (info)) != 0)
    1290                 :           0 :             result = FALSE;
    1291                 :             :         }
    1292                 :             : 
    1293                 :           3 :       if (info)
    1294                 :           3 :         g_object_unref (info);
    1295                 :           3 :       g_object_unref (file);
    1296                 :             :     }
    1297                 :           3 :   while (!result);
    1298                 :             : 
    1299                 :           3 :   enumerator_free (e);
    1300                 :             : 
    1301                 :           3 :   if (!matchlet->matches)
    1302                 :           3 :     return TRUE;
    1303                 :             : 
    1304                 :           0 :   for (l = matchlet->matches; l; l = l->next)
    1305                 :             :     {
    1306                 :             :       TreeMatchlet *submatchlet;
    1307                 :             : 
    1308                 :           0 :       submatchlet = l->data;
    1309                 :           0 :       if (matchlet_match (submatchlet, root))
    1310                 :           0 :         return TRUE;
    1311                 :             :     }
    1312                 :             : 
    1313                 :           0 :   return FALSE;
    1314                 :             : }
    1315                 :             : 
    1316                 :             : static void
    1317                 :          48 : match_match (TreeMatch    *match,
    1318                 :             :              GFile        *root,
    1319                 :             :              GPtrArray    *types)
    1320                 :             : {
    1321                 :             :   GList *l;
    1322                 :             : 
    1323                 :         144 :   for (l = match->matches; l; l = l->next)
    1324                 :             :     {
    1325                 :          99 :       TreeMatchlet *matchlet = l->data;
    1326                 :          99 :       if (matchlet_match (matchlet, root))
    1327                 :             :         {
    1328                 :           6 :           g_ptr_array_add (types, g_strdup (match->contenttype));
    1329                 :           3 :           break;
    1330                 :             :         }
    1331                 :             :     }
    1332                 :          48 : }
    1333                 :             : 
    1334                 :             : gchar **
    1335                 :           4 : g_content_type_guess_for_tree_impl (GFile *root)
    1336                 :             : {
    1337                 :             :   GPtrArray *types;
    1338                 :             :   GList *l;
    1339                 :             : 
    1340                 :           4 :   types = g_ptr_array_new ();
    1341                 :             : 
    1342                 :           4 :   G_LOCK (gio_treemagic);
    1343                 :             : 
    1344                 :           4 :   tree_magic_init ();
    1345                 :          52 :   for (l = tree_matches; l; l = l->next)
    1346                 :             :     {
    1347                 :          48 :       TreeMatch *match = l->data;
    1348                 :          48 :       match_match (match, root, types);
    1349                 :             :     }
    1350                 :             : 
    1351                 :           4 :   G_UNLOCK (gio_treemagic);
    1352                 :             : 
    1353                 :           4 :   g_ptr_array_add (types, NULL);
    1354                 :             : 
    1355                 :           4 :   return (gchar **)g_ptr_array_free (types, FALSE);
    1356                 :             : }
        

Generated by: LCOV version 2.0-1