(root)/
glib-2.79.0/
gio/
gresourcefile.c
       1  /* GIO - GLib Input, Output and Streaming Library
       2   *
       3   * Copyright (C) 2006-2007 Red Hat, Inc.
       4   *
       5   * SPDX-License-Identifier: LGPL-2.1-or-later
       6   *
       7   * This library is free software; you can redistribute it and/or
       8   * modify it under the terms of the GNU Lesser General Public
       9   * License as published by the Free Software Foundation; either
      10   * version 2.1 of the License, or (at your option) any later version.
      11   *
      12   * This library is distributed in the hope that it will be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15   * Lesser General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU Lesser General
      18   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      19   *
      20   * Author: Alexander Larsson <alexl@redhat.com>
      21   */
      22  
      23  #include "config.h"
      24  
      25  #include <string.h>
      26  
      27  #include "gresource.h"
      28  #include "gresourcefile.h"
      29  #include "gfileattribute.h"
      30  #include <gfileattribute-priv.h>
      31  #include <gfileinfo-priv.h>
      32  #include "gfile.h"
      33  #include "gfilemonitor.h"
      34  #include "gseekable.h"
      35  #include "gfileinputstream.h"
      36  #include "gfileinfo.h"
      37  #include "gfileenumerator.h"
      38  #include "gcontenttype.h"
      39  #include "gioerror.h"
      40  #include <glib/gstdio.h>
      41  #include "glibintl.h"
      42  
      43  struct _GResourceFile
      44  {
      45    GObject parent_instance;
      46  
      47    char *path;
      48  };
      49  
      50  struct _GResourceFileEnumerator
      51  {
      52    GFileEnumerator parent;
      53  
      54    GFileAttributeMatcher *matcher;
      55    char *path;
      56    char *attributes;
      57    GFileQueryInfoFlags flags;
      58    int index;
      59  
      60    char **children;
      61  };
      62  
      63  struct _GResourceFileEnumeratorClass
      64  {
      65    GFileEnumeratorClass parent_class;
      66  };
      67  
      68  typedef struct _GResourceFileEnumerator        GResourceFileEnumerator;
      69  typedef struct _GResourceFileEnumeratorClass   GResourceFileEnumeratorClass;
      70  
      71  static void g_resource_file_file_iface_init (GFileIface *iface);
      72  
      73  static GFileAttributeInfoList *resource_writable_attributes = NULL;
      74  static GFileAttributeInfoList *resource_writable_namespaces = NULL;
      75  
      76  static GType _g_resource_file_enumerator_get_type (void);
      77  
      78  #define G_TYPE_RESOURCE_FILE_ENUMERATOR         (_g_resource_file_enumerator_get_type ())
      79  #define G_RESOURCE_FILE_ENUMERATOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_RESOURCE_FILE_ENUMERATOR, GResourceFileEnumerator))
      80  #define G_RESOURCE_FILE_ENUMERATOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_RESOURCE_FILE_ENUMERATOR, GResourceFileEnumeratorClass))
      81  #define G_IS_RESOURCE_FILE_ENUMERATOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_RESOURCE_FILE_ENUMERATOR))
      82  #define G_IS_RESOURCE_FILE_ENUMERATOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_RESOURCE_FILE_ENUMERATOR))
      83  #define G_RESOURCE_FILE_ENUMERATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_RESOURCE_FILE_ENUMERATOR, GResourceFileEnumeratorClass))
      84  
      85  #define G_TYPE_RESOURCE_FILE_INPUT_STREAM         (_g_resource_file_input_stream_get_type ())
      86  #define G_RESOURCE_FILE_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_RESOURCE_FILE_INPUT_STREAM, GResourceFileInputStream))
      87  #define G_RESOURCE_FILE_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_RESOURCE_FILE_INPUT_STREAM, GResourceFileInputStreamClass))
      88  #define G_IS_RESOURCE_FILE_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_RESOURCE_FILE_INPUT_STREAM))
      89  #define G_IS_RESOURCE_FILE_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_RESOURCE_FILE_INPUT_STREAM))
      90  #define G_RESOURCE_FILE_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_RESOURCE_FILE_INPUT_STREAM, GResourceFileInputStreamClass))
      91  
      92  typedef struct _GResourceFileInputStream         GResourceFileInputStream;
      93  typedef struct _GResourceFileInputStreamClass    GResourceFileInputStreamClass;
      94  
      95  #define g_resource_file_get_type _g_resource_file_get_type
      96  G_DEFINE_TYPE_WITH_CODE (GResourceFile, g_resource_file, G_TYPE_OBJECT,
      97  			 G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
      98  						g_resource_file_file_iface_init))
      99  
     100  #define g_resource_file_enumerator_get_type _g_resource_file_enumerator_get_type
     101  G_DEFINE_TYPE (GResourceFileEnumerator, g_resource_file_enumerator, G_TYPE_FILE_ENUMERATOR)
     102  
     103  static GFileEnumerator *_g_resource_file_enumerator_new (GResourceFile *file,
     104  							 const char           *attributes,
     105  							 GFileQueryInfoFlags   flags,
     106  							 GCancellable         *cancellable,
     107  							 GError              **error);
     108  
     109  
     110  static GType              _g_resource_file_input_stream_get_type (void) G_GNUC_CONST;
     111  
     112  static GFileInputStream *_g_resource_file_input_stream_new (GInputStream *stream, GFile *file);
     113  
     114  
     115  static void
     116  g_resource_file_finalize (GObject *object)
     117  {
     118    GResourceFile *resource;
     119  
     120    resource = G_RESOURCE_FILE (object);
     121  
     122    g_free (resource->path);
     123  
     124    G_OBJECT_CLASS (g_resource_file_parent_class)->finalize (object);
     125  }
     126  
     127  static void
     128  g_resource_file_class_init (GResourceFileClass *klass)
     129  {
     130    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     131  
     132    gobject_class->finalize = g_resource_file_finalize;
     133  
     134    resource_writable_attributes = g_file_attribute_info_list_new ();
     135    resource_writable_namespaces = g_file_attribute_info_list_new ();
     136  }
     137  
     138  static void
     139  g_resource_file_init (GResourceFile *resource)
     140  {
     141  }
     142  
     143  static inline gchar *
     144  scan_backwards (const gchar *begin,
     145                  const gchar *end,
     146                  gchar        c)
     147  {
     148    while (end >= begin)
     149      {
     150        if (*end == c)
     151          return (gchar *)end;
     152        end--;
     153      }
     154  
     155    return NULL;
     156  }
     157  
     158  static inline void
     159  pop_to_previous_part (const gchar  *begin,
     160                        gchar       **out)
     161  {
     162    if (*out > begin)
     163      *out = scan_backwards (begin, *out - 1, '/');
     164  }
     165  
     166  /*
     167   * canonicalize_filename:
     168   * @in: the path to be canonicalized
     169   *
     170   * The path @in may contain non-canonical path pieces such as "../"
     171   * or duplicated "/". This will resolve those into a form that only
     172   * contains a single / at a time and resolves all "../". The resulting
     173   * path must also start with a /.
     174   *
     175   * Returns: the canonical form of the path
     176   */
     177  static char *
     178  canonicalize_filename (const char *in)
     179  {
     180    gchar *bptr;
     181    char *out;
     182  
     183    bptr = out = g_malloc (strlen (in) + 2);
     184    *out = '/';
     185  
     186    while (*in != 0)
     187      {
     188        g_assert (*out == '/');
     189  
     190        /* move past slashes */
     191        while (*in == '/')
     192          in++;
     193  
     194        /* Handle ./ ../ .\0 ..\0 */
     195        if (*in == '.')
     196          {
     197            /* If this is ../ or ..\0 move up */
     198            if (in[1] == '.' && (in[2] == '/' || in[2] == 0))
     199              {
     200                pop_to_previous_part (bptr, &out);
     201                in += 2;
     202                continue;
     203              }
     204  
     205            /* If this is ./ skip past it */
     206            if (in[1] == '/' || in[1] == 0)
     207              {
     208                in += 1;
     209                continue;
     210              }
     211          }
     212  
     213        /* Scan to the next path piece */
     214        while (*in != 0 && *in != '/')
     215          *(++out) = *(in++);
     216  
     217        /* Add trailing /, compress the rest on the next go round. */
     218        if (*in == '/')
     219          *(++out) = *(in++);
     220      }
     221  
     222    /* Trim trailing / from path */
     223    if (out > bptr && *out == '/')
     224      *out = 0;
     225    else
     226      *(++out) = 0;
     227  
     228    return bptr;
     229  }
     230  
     231  static GFile *
     232  g_resource_file_new_for_path (const char *path)
     233  {
     234    GResourceFile *resource = g_object_new (G_TYPE_RESOURCE_FILE, NULL);
     235  
     236    resource->path = canonicalize_filename (path);
     237  
     238    return G_FILE (resource);
     239  }
     240  
     241  /* Will return %NULL if @uri is malformed */
     242  GFile *
     243  _g_resource_file_new (const char *uri)
     244  {
     245    GFile *resource;
     246    char *path;
     247  
     248    path = g_uri_unescape_string (uri + strlen ("resource:"), NULL);
     249    if (path == NULL)
     250      return NULL;
     251  
     252    resource = g_resource_file_new_for_path (path);
     253    g_free (path);
     254  
     255    return G_FILE (resource);
     256  }
     257  
     258  static gboolean
     259  g_resource_file_is_native (GFile *file)
     260  {
     261    return FALSE;
     262  }
     263  
     264  static gboolean
     265  g_resource_file_has_uri_scheme (GFile      *file,
     266  				const char *uri_scheme)
     267  {
     268    return g_ascii_strcasecmp (uri_scheme, "resource") == 0;
     269  }
     270  
     271  static char *
     272  g_resource_file_get_uri_scheme (GFile *file)
     273  {
     274    return g_strdup ("resource");
     275  }
     276  
     277  static char *
     278  g_resource_file_get_basename (GFile *file)
     279  {
     280    gchar *base;
     281  
     282    base = strrchr (G_RESOURCE_FILE (file)->path, '/');
     283    return g_strdup (base + 1);
     284  }
     285  
     286  static char *
     287  g_resource_file_get_path (GFile *file)
     288  {
     289    return NULL;
     290  }
     291  
     292  static char *
     293  g_resource_file_get_uri (GFile *file)
     294  {
     295    char *escaped, *res;
     296    escaped = g_uri_escape_string (G_RESOURCE_FILE (file)->path, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
     297    res = g_strconcat ("resource://", escaped, NULL);
     298    g_free (escaped);
     299    return res;
     300  }
     301  
     302  static char *
     303  g_resource_file_get_parse_name (GFile *file)
     304  {
     305    return g_resource_file_get_uri (file);
     306  }
     307  
     308  static GFile *
     309  g_resource_file_get_parent (GFile *file)
     310  {
     311    GResourceFile *resource = G_RESOURCE_FILE (file);
     312    GResourceFile *parent;
     313    gchar *end;
     314  
     315    end = strrchr (resource->path, '/');
     316  
     317    if (end == G_RESOURCE_FILE (file)->path)
     318      return NULL;
     319  
     320    parent = g_object_new (G_TYPE_RESOURCE_FILE, NULL);
     321    parent->path = g_strndup (resource->path,
     322  			    end - resource->path);
     323  
     324    return G_FILE (parent);
     325  }
     326  
     327  static GFile *
     328  g_resource_file_dup (GFile *file)
     329  {
     330    GResourceFile *resource = G_RESOURCE_FILE (file);
     331  
     332    return g_resource_file_new_for_path (resource->path);
     333  }
     334  
     335  static guint
     336  g_resource_file_hash (GFile *file)
     337  {
     338    GResourceFile *resource = G_RESOURCE_FILE (file);
     339  
     340    return g_str_hash (resource->path);
     341  }
     342  
     343  static gboolean
     344  g_resource_file_equal (GFile *file1,
     345  		       GFile *file2)
     346  {
     347    GResourceFile *resource1 = G_RESOURCE_FILE (file1);
     348    GResourceFile *resource2 = G_RESOURCE_FILE (file2);
     349  
     350    return g_str_equal (resource1->path, resource2->path);
     351  }
     352  
     353  static const char *
     354  match_prefix (const char *path,
     355  	      const char *prefix)
     356  {
     357    int prefix_len;
     358  
     359    prefix_len = strlen (prefix);
     360    if (strncmp (path, prefix, prefix_len) != 0)
     361      return NULL;
     362  
     363    /* Handle the case where prefix is the root, so that
     364     * the IS_DIR_SEPRARATOR check below works */
     365    if (prefix_len > 0 &&
     366        prefix[prefix_len-1] == '/')
     367      prefix_len--;
     368  
     369    return path + prefix_len;
     370  }
     371  
     372  static gboolean
     373  g_resource_file_prefix_matches (GFile *parent,
     374  				GFile *descendant)
     375  {
     376    GResourceFile *parent_resource = G_RESOURCE_FILE (parent);
     377    GResourceFile *descendant_resource = G_RESOURCE_FILE (descendant);
     378    const char *remainder;
     379  
     380    remainder = match_prefix (descendant_resource->path, parent_resource->path);
     381    if (remainder != NULL && *remainder == '/')
     382      return TRUE;
     383    return FALSE;
     384  }
     385  
     386  static char *
     387  g_resource_file_get_relative_path (GFile *parent,
     388  				   GFile *descendant)
     389  {
     390    GResourceFile *parent_resource = G_RESOURCE_FILE (parent);
     391    GResourceFile *descendant_resource = G_RESOURCE_FILE (descendant);
     392    const char *remainder;
     393  
     394    remainder = match_prefix (descendant_resource->path, parent_resource->path);
     395  
     396    if (remainder != NULL && *remainder == '/')
     397      return g_strdup (remainder + 1);
     398    return NULL;
     399  }
     400  
     401  static GFile *
     402  g_resource_file_resolve_relative_path (GFile      *file,
     403  				       const char *relative_path)
     404  {
     405    GResourceFile *resource = G_RESOURCE_FILE (file);
     406    char *filename;
     407    GFile *child;
     408  
     409    if (relative_path[0] == '/')
     410      return g_resource_file_new_for_path (relative_path);
     411  
     412    filename = g_build_path ("/", resource->path, relative_path, NULL);
     413    child = g_resource_file_new_for_path (filename);
     414    g_free (filename);
     415  
     416    return child;
     417  }
     418  
     419  static GFileEnumerator *
     420  g_resource_file_enumerate_children (GFile                *file,
     421  				    const char           *attributes,
     422  				    GFileQueryInfoFlags   flags,
     423  				    GCancellable         *cancellable,
     424  				    GError              **error)
     425  {
     426    GResourceFile *resource = G_RESOURCE_FILE (file);
     427    return _g_resource_file_enumerator_new (resource,
     428  					  attributes, flags,
     429  					  cancellable, error);
     430  }
     431  
     432  static GFile *
     433  g_resource_file_get_child_for_display_name (GFile        *file,
     434  					    const char   *display_name,
     435  					    GError      **error)
     436  {
     437    GFile *new_file;
     438  
     439    new_file = g_file_get_child (file, display_name);
     440  
     441    return new_file;
     442  }
     443  
     444  static GFileInfo *
     445  g_resource_file_query_info (GFile                *file,
     446  			    const char           *attributes,
     447  			    GFileQueryInfoFlags   flags,
     448  			    GCancellable         *cancellable,
     449  			    GError              **error)
     450  {
     451    GResourceFile *resource = G_RESOURCE_FILE (file);
     452    GError *my_error = NULL;
     453    GFileInfo *info;
     454    GFileAttributeMatcher *matcher;
     455    gboolean res;
     456    gsize size = 0;
     457    guint32 resource_flags = 0;
     458    char **children;
     459    gboolean is_dir;
     460    char *base;
     461  
     462    is_dir = FALSE;
     463    children = g_resources_enumerate_children (resource->path, 0, NULL);
     464    if (children != NULL)
     465      {
     466        g_strfreev (children);
     467        is_dir = TRUE;
     468      }
     469  
     470    /* root is always there */
     471    if (strcmp ("/", resource->path) == 0)
     472      is_dir = TRUE;
     473  
     474    if (!is_dir)
     475      {
     476        res = g_resources_get_info (resource->path, 0, &size, &resource_flags, &my_error);
     477        if (!res)
     478  	{
     479  	  if (g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
     480  	    {
     481  	      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
     482  			   _("The resource at “%s” does not exist"),
     483  			   resource->path);
     484  	    }
     485  	  else
     486  	    g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
     487                                   my_error->message);
     488  	  g_clear_error (&my_error);
     489  	  return FALSE;
     490  	}
     491      }
     492  
     493    matcher = g_file_attribute_matcher_new (attributes);
     494  
     495    info = g_file_info_new ();
     496    base = g_resource_file_get_basename (file);
     497    g_file_info_set_name (info, base);
     498    g_file_info_set_display_name (info, base);
     499  
     500    _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_READ, TRUE);
     501    _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_WRITE, FALSE);
     502    _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_EXECUTE, FALSE);
     503    _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME, FALSE);
     504    _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE, FALSE);
     505    _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH, FALSE);
     506  
     507    if (is_dir)
     508      {
     509        g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
     510      }
     511    else
     512      {
     513        GBytes *bytes;
     514        char *content_type;
     515  
     516        g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
     517        g_file_info_set_size (info, size);
     518  
     519        if ((_g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE) ||
     520             ((~resource_flags & G_RESOURCE_FLAGS_COMPRESSED) && 
     521              _g_file_attribute_matcher_matches_id (matcher, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE))) &&
     522            (bytes = g_resources_lookup_data (resource->path, 0, NULL)))
     523          {
     524            const guchar *data;
     525            gsize data_size;
     526  
     527            data = g_bytes_get_data (bytes, &data_size);
     528            content_type = g_content_type_guess (base, data, data_size, NULL);
     529  
     530            g_bytes_unref (bytes);
     531          }
     532        else
     533          content_type = NULL;
     534  
     535        if (content_type)
     536          {
     537            _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_CONTENT_TYPE, content_type);
     538            _g_file_info_set_attribute_string_by_id (info, G_FILE_ATTRIBUTE_ID_STANDARD_FAST_CONTENT_TYPE, content_type);
     539  
     540            g_free (content_type);
     541          }
     542      }
     543  
     544    g_free (base);
     545    g_file_attribute_matcher_unref (matcher);
     546  
     547    return info;
     548  }
     549  
     550  static GFileInfo *
     551  g_resource_file_query_filesystem_info (GFile         *file,
     552                                         const char    *attributes,
     553                                         GCancellable  *cancellable,
     554                                         GError       **error)
     555  {
     556    GFileInfo *info;
     557    GFileAttributeMatcher *matcher;
     558  
     559    info = g_file_info_new ();
     560  
     561    matcher = g_file_attribute_matcher_new (attributes);
     562    if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE))
     563      g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "resource");
     564  
     565    if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY))
     566      g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
     567  
     568    g_file_attribute_matcher_unref (matcher);
     569  
     570    return info;
     571  }
     572  
     573  static GFileAttributeInfoList *
     574  g_resource_file_query_settable_attributes (GFile         *file,
     575  					   GCancellable  *cancellable,
     576  					   GError       **error)
     577  {
     578    return g_file_attribute_info_list_ref (resource_writable_attributes);
     579  }
     580  
     581  static GFileAttributeInfoList *
     582  g_resource_file_query_writable_namespaces (GFile         *file,
     583  					   GCancellable  *cancellable,
     584  					   GError       **error)
     585  {
     586    return g_file_attribute_info_list_ref (resource_writable_namespaces);
     587  }
     588  
     589  static GFileInputStream *
     590  g_resource_file_read (GFile         *file,
     591  		      GCancellable  *cancellable,
     592  		      GError       **error)
     593  {
     594    GResourceFile *resource = G_RESOURCE_FILE (file);
     595    GError *my_error = NULL;
     596    GInputStream *stream;
     597    GFileInputStream *res;
     598  
     599    stream = g_resources_open_stream (resource->path, 0, &my_error);
     600  
     601    if (stream == NULL)
     602      {
     603        if (g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
     604  	{
     605  	  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
     606  		       _("The resource at “%s” does not exist"),
     607  		       resource->path);
     608  	}
     609        else
     610  	g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
     611                               my_error->message);
     612        g_clear_error (&my_error);
     613        return NULL;
     614      }
     615  
     616    res = _g_resource_file_input_stream_new (stream, file);
     617    g_object_unref (stream);
     618    return res;
     619  }
     620  
     621  typedef GFileMonitor GResourceFileMonitor;
     622  typedef GFileMonitorClass GResourceFileMonitorClass;
     623  
     624  GType g_resource_file_monitor_get_type (void);
     625  
     626  G_DEFINE_TYPE (GResourceFileMonitor, g_resource_file_monitor, G_TYPE_FILE_MONITOR)
     627  
     628  static gboolean
     629  g_resource_file_monitor_cancel (GFileMonitor *monitor)
     630  {
     631    return TRUE;
     632  }
     633  
     634  static void
     635  g_resource_file_monitor_init (GResourceFileMonitor *monitor)
     636  {
     637  }
     638  
     639  static void
     640  g_resource_file_monitor_class_init (GResourceFileMonitorClass *class)
     641  {
     642    class->cancel = g_resource_file_monitor_cancel;
     643  }
     644  
     645  static GFileMonitor *
     646  g_resource_file_monitor_file (GFile              *file,
     647                                GFileMonitorFlags   flags,
     648                                GCancellable       *cancellable,
     649                                GError            **error)
     650  {
     651    return g_object_new (g_resource_file_monitor_get_type (), NULL);
     652  }
     653  
     654  static GFile *
     655  g_resource_file_set_display_name (GFile         *file,
     656                                    const char    *display_name,
     657                                    GCancellable  *cancellable,
     658                                    GError       **error)
     659  {
     660    g_set_error_literal (error,
     661                         G_IO_ERROR,
     662                         G_IO_ERROR_NOT_SUPPORTED,
     663                         _("Resource files cannot be renamed"));
     664    return NULL;
     665  }
     666  
     667  static void
     668  g_resource_file_file_iface_init (GFileIface *iface)
     669  {
     670    iface->dup = g_resource_file_dup;
     671    iface->hash = g_resource_file_hash;
     672    iface->equal = g_resource_file_equal;
     673    iface->is_native = g_resource_file_is_native;
     674    iface->has_uri_scheme = g_resource_file_has_uri_scheme;
     675    iface->get_uri_scheme = g_resource_file_get_uri_scheme;
     676    iface->get_basename = g_resource_file_get_basename;
     677    iface->get_path = g_resource_file_get_path;
     678    iface->get_uri = g_resource_file_get_uri;
     679    iface->get_parse_name = g_resource_file_get_parse_name;
     680    iface->get_parent = g_resource_file_get_parent;
     681    iface->prefix_matches = g_resource_file_prefix_matches;
     682    iface->get_relative_path = g_resource_file_get_relative_path;
     683    iface->resolve_relative_path = g_resource_file_resolve_relative_path;
     684    iface->get_child_for_display_name = g_resource_file_get_child_for_display_name;
     685    iface->set_display_name = g_resource_file_set_display_name;
     686    iface->enumerate_children = g_resource_file_enumerate_children;
     687    iface->query_info = g_resource_file_query_info;
     688    iface->query_filesystem_info = g_resource_file_query_filesystem_info;
     689    iface->query_settable_attributes = g_resource_file_query_settable_attributes;
     690    iface->query_writable_namespaces = g_resource_file_query_writable_namespaces;
     691    iface->read_fn = g_resource_file_read;
     692    iface->monitor_file = g_resource_file_monitor_file;
     693  
     694    iface->supports_thread_contexts = TRUE;
     695  }
     696  
     697  static GFileInfo *g_resource_file_enumerator_next_file (GFileEnumerator  *enumerator,
     698  							GCancellable     *cancellable,
     699  							GError          **error);
     700  static gboolean   g_resource_file_enumerator_close     (GFileEnumerator  *enumerator,
     701  							GCancellable     *cancellable,
     702  							GError          **error);
     703  
     704  static void
     705  g_resource_file_enumerator_finalize (GObject *object)
     706  {
     707    GResourceFileEnumerator *resource;
     708  
     709    resource = G_RESOURCE_FILE_ENUMERATOR (object);
     710  
     711    g_strfreev (resource->children);
     712    g_free (resource->path);
     713    g_free (resource->attributes);
     714  
     715    G_OBJECT_CLASS (g_resource_file_enumerator_parent_class)->finalize (object);
     716  }
     717  
     718  static void
     719  g_resource_file_enumerator_class_init (GResourceFileEnumeratorClass *klass)
     720  {
     721    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     722    GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass);
     723  
     724    gobject_class->finalize = g_resource_file_enumerator_finalize;
     725  
     726    enumerator_class->next_file = g_resource_file_enumerator_next_file;
     727    enumerator_class->close_fn = g_resource_file_enumerator_close;
     728  }
     729  
     730  static void
     731  g_resource_file_enumerator_init (GResourceFileEnumerator *resource)
     732  {
     733  }
     734  
     735  static GFileEnumerator *
     736  _g_resource_file_enumerator_new (GResourceFile *file,
     737  				 const char           *attributes,
     738  				 GFileQueryInfoFlags   flags,
     739  				 GCancellable         *cancellable,
     740  				 GError              **error)
     741  {
     742    GResourceFileEnumerator *resource;
     743    char **children;
     744    gboolean res;
     745  
     746    children = g_resources_enumerate_children (file->path, 0, NULL);
     747    if (children == NULL &&
     748        strcmp ("/", file->path) != 0)
     749      {
     750        res = g_resources_get_info (file->path, 0, NULL, NULL, NULL);
     751        if (res)
     752  	g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,
     753  		     _("The resource at “%s” is not a directory"),
     754  		     file->path);
     755        else
     756  	g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
     757  		     _("The resource at “%s” does not exist"),
     758  		     file->path);
     759        return NULL;
     760      }
     761  
     762    resource = g_object_new (G_TYPE_RESOURCE_FILE_ENUMERATOR,
     763  			   "container", file,
     764  			   NULL);
     765  
     766    resource->children = children;
     767    resource->path = g_strdup (file->path);
     768    resource->attributes = g_strdup (attributes);
     769    resource->flags = flags;
     770  
     771    return G_FILE_ENUMERATOR (resource);
     772  }
     773  
     774  static GFileInfo *
     775  g_resource_file_enumerator_next_file (GFileEnumerator  *enumerator,
     776  				      GCancellable     *cancellable,
     777  				      GError          **error)
     778  {
     779    GResourceFileEnumerator *resource = G_RESOURCE_FILE_ENUMERATOR (enumerator);
     780    char *path;
     781    GFileInfo *info;
     782    GFile *file;
     783  
     784    if (resource->children == NULL ||
     785        resource->children[resource->index] == NULL)
     786      return NULL;
     787  
     788    path = g_build_path ("/", resource->path, resource->children[resource->index++], NULL);
     789    file = g_resource_file_new_for_path (path);
     790    g_free (path);
     791  
     792    info = g_file_query_info (file,
     793  			    resource->attributes,
     794  			    resource->flags,
     795  			    cancellable,
     796  			    error);
     797  
     798    g_object_unref (file);
     799  
     800    return info;
     801  }
     802  
     803  static gboolean
     804  g_resource_file_enumerator_close (GFileEnumerator  *enumerator,
     805  			       GCancellable     *cancellable,
     806  			       GError          **error)
     807  {
     808    return TRUE;
     809  }
     810  
     811  
     812  struct _GResourceFileInputStream
     813  {
     814    GFileInputStream parent_instance;
     815    GInputStream *stream;
     816    GFile *file;
     817  };
     818  
     819  struct _GResourceFileInputStreamClass
     820  {
     821    GFileInputStreamClass parent_class;
     822  };
     823  
     824  #define g_resource_file_input_stream_get_type _g_resource_file_input_stream_get_type
     825  G_DEFINE_TYPE (GResourceFileInputStream, g_resource_file_input_stream, G_TYPE_FILE_INPUT_STREAM)
     826  
     827  static gssize     g_resource_file_input_stream_read       (GInputStream      *stream,
     828  							   void              *buffer,
     829  							   gsize              count,
     830  							   GCancellable      *cancellable,
     831  							   GError           **error);
     832  static gssize     g_resource_file_input_stream_skip       (GInputStream      *stream,
     833  							   gsize              count,
     834  							   GCancellable      *cancellable,
     835  							   GError           **error);
     836  static gboolean   g_resource_file_input_stream_close      (GInputStream      *stream,
     837  							   GCancellable      *cancellable,
     838  							   GError           **error);
     839  static goffset    g_resource_file_input_stream_tell       (GFileInputStream  *stream);
     840  static gboolean   g_resource_file_input_stream_can_seek   (GFileInputStream  *stream);
     841  static gboolean   g_resource_file_input_stream_seek       (GFileInputStream  *stream,
     842  							   goffset            offset,
     843  							   GSeekType          type,
     844  							   GCancellable      *cancellable,
     845  							   GError           **error);
     846  static GFileInfo *g_resource_file_input_stream_query_info (GFileInputStream  *stream,
     847  							   const char        *attributes,
     848  							   GCancellable      *cancellable,
     849  							   GError           **error);
     850  
     851  static void
     852  g_resource_file_input_stream_finalize (GObject *object)
     853  {
     854    GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (object);
     855  
     856    g_object_unref (file->stream);
     857    g_object_unref (file->file);
     858    G_OBJECT_CLASS (g_resource_file_input_stream_parent_class)->finalize (object);
     859  }
     860  
     861  static void
     862  g_resource_file_input_stream_class_init (GResourceFileInputStreamClass *klass)
     863  {
     864    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     865    GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
     866    GFileInputStreamClass *file_stream_class = G_FILE_INPUT_STREAM_CLASS (klass);
     867  
     868    gobject_class->finalize = g_resource_file_input_stream_finalize;
     869  
     870    stream_class->read_fn = g_resource_file_input_stream_read;
     871    stream_class->skip = g_resource_file_input_stream_skip;
     872    stream_class->close_fn = g_resource_file_input_stream_close;
     873    file_stream_class->tell = g_resource_file_input_stream_tell;
     874    file_stream_class->can_seek = g_resource_file_input_stream_can_seek;
     875    file_stream_class->seek = g_resource_file_input_stream_seek;
     876    file_stream_class->query_info = g_resource_file_input_stream_query_info;
     877  }
     878  
     879  static void
     880  g_resource_file_input_stream_init (GResourceFileInputStream *info)
     881  {
     882  }
     883  
     884  static GFileInputStream *
     885  _g_resource_file_input_stream_new (GInputStream *in_stream, GFile *file)
     886  {
     887    GResourceFileInputStream *stream;
     888  
     889    stream = g_object_new (G_TYPE_RESOURCE_FILE_INPUT_STREAM, NULL);
     890    stream->stream = g_object_ref (in_stream);
     891    stream->file = g_object_ref (file);
     892  
     893    return G_FILE_INPUT_STREAM (stream);
     894  }
     895  
     896  static gssize
     897  g_resource_file_input_stream_read (GInputStream  *stream,
     898  				   void          *buffer,
     899  				   gsize          count,
     900  				   GCancellable  *cancellable,
     901  				   GError       **error)
     902  {
     903    GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
     904    return g_input_stream_read (file->stream,
     905  			      buffer, count, cancellable, error);
     906  }
     907  
     908  static gssize
     909  g_resource_file_input_stream_skip (GInputStream  *stream,
     910  				   gsize          count,
     911  				   GCancellable  *cancellable,
     912  				   GError       **error)
     913  {
     914    GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
     915    return g_input_stream_skip (file->stream,
     916  			      count, cancellable, error);
     917  }
     918  
     919  static gboolean
     920  g_resource_file_input_stream_close (GInputStream  *stream,
     921  				    GCancellable  *cancellable,
     922  				    GError       **error)
     923  {
     924    GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
     925    return g_input_stream_close (file->stream,
     926  			       cancellable, error);
     927  }
     928  
     929  
     930  static goffset
     931  g_resource_file_input_stream_tell (GFileInputStream *stream)
     932  {
     933    GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
     934  
     935    if (!G_IS_SEEKABLE (file->stream))
     936        return 0;
     937  
     938    return g_seekable_tell (G_SEEKABLE (file->stream));
     939  }
     940  
     941  static gboolean
     942  g_resource_file_input_stream_can_seek (GFileInputStream *stream)
     943  {
     944    GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
     945  
     946    return G_IS_SEEKABLE (file->stream) && g_seekable_can_seek (G_SEEKABLE (file->stream));
     947  }
     948  
     949  static gboolean
     950  g_resource_file_input_stream_seek (GFileInputStream  *stream,
     951  				   goffset            offset,
     952  				   GSeekType          type,
     953  				   GCancellable      *cancellable,
     954  				   GError           **error)
     955  {
     956    GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
     957  
     958    if (!G_IS_SEEKABLE (file->stream))
     959      {
     960        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
     961  			   _("Input stream doesn’t implement seek"));
     962        return FALSE;
     963      }
     964  
     965    return g_seekable_seek (G_SEEKABLE (file->stream),
     966  			  offset, type, cancellable, error);
     967  }
     968  
     969  static GFileInfo *
     970  g_resource_file_input_stream_query_info (GFileInputStream  *stream,
     971  					 const char        *attributes,
     972  					 GCancellable      *cancellable,
     973  					 GError           **error)
     974  {
     975    GResourceFileInputStream *file = G_RESOURCE_FILE_INPUT_STREAM (stream);
     976  
     977    return g_file_query_info (file->file, attributes, 0, cancellable, error);
     978  }