resources: compiler: Allow stripping blanks from xml data
[platform/upstream/glib.git] / gio / gresource.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright © 2011 Red Hat, Inc
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Authors: Alexander Larsson <alexl@redhat.com>
21  */
22
23 #include "config.h"
24
25 #include <string.h>
26
27 #include "gresource.h"
28 #include <gvdb/gvdb-reader.h>
29 #include <gi18n.h>
30 #include <gio/gmemoryinputstream.h>
31 #include <gio/gzlibdecompressor.h>
32 #include <gio/gconverterinputstream.h>
33
34 struct _GResource
35 {
36   int ref_count;
37
38   GvdbTable *table;
39 };
40
41 G_DEFINE_BOXED_TYPE (GResource, g_resource, g_resource_ref, g_resource_unref)
42
43 /**
44  * SECTION:gresource
45  * @short_description: Resource framework
46  * @include: gio/gio.h
47  *
48  * Applications and libraries often contain binary or textual data that is really part of the
49  * application, rather than user data. For instance #GtkBuilder .ui files, splashscreen images,
50  * GMenu markup xml, CSS files, icons, etc. These are often shipped as files in <filename>$datadir/appname</filename>, or
51  * manually included as literal strings in the code.
52  *
53  * The #GResource API and the <link linkend="glib-compile-schemas">glib-compile-resources</link> program
54  * provide a convenient and efficient alternative to this which has some nice properties. You
55  * maintain the files as normal files, so its easy to edit them, but during the build the files
56  * are combined into a binary bundle that is linked into the executable. This means that loading
57  * the resource files are efficient (as they are already in memory, shared with other instances) and
58  * simple (no need to check for things like I/O errors or locate the files in the filesystem). It
59  * also makes it easier to create relocatable applications.
60  *
61  * Resource files can also be marked as compresses. Such files will be included in the resource bundle
62  * in a compressed form, but will be automatically uncompressed when the resource is used. This
63  * is very useful e.g. for larger text files that are parsed once (or rarely) and then thrown away.
64  *
65  * Resource files can also be marked to be preprocessed, by setting the value of the
66  * <literal>preprocess</literal> attribute to a comma-separated list of preprocessing options.
67  * The only option currently supported is 
68  * <literal>xml-stripblanks</literal> which will use <literal>xmllint</literal> to strip
69  * ignorable whitespace from the xml file. For this to work, the <envar>XMLLINT</envar>
70  * environment variable must be set to the full path to the xmllint executable; 
71  * otherwise the preprocessing step is skipped.
72  *
73  * Resource bundles are created by the <link linkend="glib-compile-schemas">glib-compile-resources</link> program
74  * which takes an xml file that describes the bundle, and a set of files that the xml references. These
75  * are combined into a binary resource bundle.
76  *
77  * <example id="resource-example"><title>Example resource description</title>
78  * <programlisting><![CDATA[
79  * <?xml version="1.0" encoding="UTF-8"?>
80  * <gresources>
81  *   <gresource prefix="/org/gtk/Example">
82  *     <file>data/splashscreen.png</file>
83  *     <file compressed="true">dialog.ui</file>
84  *     <file preprocess="xml-stripblanks">menumarkup.xml</file>
85  *   </gresource>
86  * </gresources>
87  * ]]></programlisting></example>
88  *
89  * This will create a resource bundle with the following files:
90  * <programlisting><![CDATA[
91  * /org/gtk/Example/data/splashscreen.png
92  * /org/gtk/Example/dialog.ui
93  * /org/gtk/Example/menumarkup.xml
94  * ]]></programlisting>
95  *
96  * Note that all resources in the process share the same namespace, so use java-style
97  * path prefixes (like in the above example) to avoid conflicts.
98  *
99  * You can then use <link linkend="glib-compile-schemas">glib-compile-resources</link> to compile the xml to a
100  * binary bundle that you can load with g_resource_load(). However, its more common to use the --generate-source and
101  * --generate-header arguments to create a source file and header to link directly into your application.
102  *
103  * Once a #GResource has been created and registered all the data in it can be accessed globally in the process by
104  * using API calls like g_resources_open_stream() to stream the data or g_resources_lookup_data() to get a direct pointer
105  * to the data. You can also use uris like "resource:///org/gtk/Example/data/splashscreen.png" with #GFile to access
106  * the resource data.
107  *
108  * There are two forms of the generated source, the default version uses the compiler support for constructor
109  * and destructor functions (where availible) to automatically create and register the #GResource on startup
110  * or library load time. If you pass --manual-register two functions to register/unregister the resource is instead
111  * created. This requires an explicit initialization call in your application/library, but it works on all platforms,
112  * even on the minor ones where this is not availible. (Constructor support is availible for at least Win32, MacOS and Linux.)
113  *
114  * Note that resource data can point directly into the data segment of e.g. a library, so if you are unloading libraries
115  * during runtime you need to be very careful with keeping around pointers to data from a resource, as this goes away
116  * when the library is unloaded. However, in practice this is not generally a problem, since most resource accesses
117  * is for your own resources, and resource data is often used once, during parsing, and then released.
118  *
119  * Since: 2.32
120  */
121
122 /**
123  * g_resource_error_quark:
124  *
125  * Gets the #GResource Error Quark.
126  *
127  * Return value: a #GQuark.
128  *
129  * Since: 2.32
130  */
131 GQuark
132 g_resource_error_quark (void)
133 {
134   return g_quark_from_static_string ("g-resource-error-quark");
135 }
136
137 /**
138  * g_resource_ref:
139  * @resource: A #GResource.
140  *
141  * Atomically increments the reference count of @array by one. This
142  * function is MT-safe and may be called from any thread.
143  *
144  * Returns: The passed in #GResource.
145  *
146  * Since: 2.32
147  **/
148 GResource *
149 g_resource_ref (GResource *resource)
150 {
151   g_atomic_int_inc (&resource->ref_count);
152   return resource;
153 }
154
155 /**
156  * g_resource_unref:
157  * @resource: A #GResource.
158  *
159  * Atomically decrements the reference count of @resource by one. If the
160  * reference count drops to 0, all memory allocated by the array is
161  * released. This function is MT-safe and may be called from any
162  * thread.
163  *
164  * Since: 2.32
165  **/
166 void
167 g_resource_unref (GResource *resource)
168 {
169   if (g_atomic_int_dec_and_test (&resource->ref_count))
170     {
171       gvdb_table_unref (resource->table);
172       g_free (resource);
173     }
174 }
175
176 /**
177  * g_resource_new_from_data:
178  * @data: A #GBytes.
179  * @error: return location for a #GError, or %NULL.
180  *
181  * Creates a GResource from a reference to the binary resource bundle.
182  * This will keep a reference to @data while the resource lives, so
183  * the data should not be modified or freed.
184  *
185  * If you want to use this resource in the global resource namespace you need
186  * to register it with g_resources_register().
187  *
188  * Return value: (transfer full): a new #GResource, or %NULL on error.
189  *
190  * Since: 2.32
191  **/
192 GResource *
193 g_resource_new_from_data (GBytes *data,
194                           GError **error)
195 {
196   GResource *resource;
197   GvdbTable *table;
198
199   table = gvdb_table_new_from_data (g_bytes_get_data (data, NULL),
200                                     g_bytes_get_size (data),
201                                     TRUE,
202                                     g_bytes_ref (data),
203                                     (GvdbRefFunc)g_bytes_ref,
204                                     (GDestroyNotify)g_bytes_unref,
205                                     error);
206
207   if (table == NULL)
208     return NULL;
209
210   resource = g_new0 (GResource, 1);
211   resource->ref_count = 1;
212   resource->table = table;
213
214   return resource;
215 }
216
217 /**
218  * g_resource_load:
219  * @filename: (type filename): the path of a filename to load, in the GLib filename encoding.
220  * @error: return location for a #GError, or %NULL.
221  *
222  * Loads a binary resource bundle and creates a #GResource representation of it, allowing
223  * you to query it for data.
224  *
225  * If you want to use this resource in the global resource namespace you need
226  * to register it with g_resources_register().
227  *
228  * Return value: (transfer full): a new #GResource, or %NULL on error.
229  *
230  * Since: 2.32
231  **/
232 GResource *
233 g_resource_load (const gchar *filename,
234                  GError **error)
235 {
236   GResource *resource;
237   GvdbTable *table;
238
239   table = gvdb_table_new (filename, FALSE, error);
240   if (table == NULL)
241     return NULL;
242
243   resource = g_new0 (GResource, 1);
244   resource->table = table;
245
246   return resource;
247 }
248
249 static gboolean do_lookup (GResource *resource,
250                            const char *path,
251                            GResourceLookupFlags lookup_flags,
252                            gsize *size,
253                            guint32 *flags,
254                            const void **data,
255                            gsize *data_size,
256                            GError **error)
257 {
258   char *free_path = NULL;
259   gsize path_len;
260   gboolean res = FALSE;
261   GVariant *value;
262
263   path_len = strlen (path);
264   if (path[path_len-1] == '/')
265     {
266       path = free_path = g_strdup (path);
267       free_path[path_len-1] = 0;
268     }
269
270   value = gvdb_table_get_value (resource->table, path);
271
272   if (value == NULL)
273     {
274       g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
275                    _("The resource at '%s' does not exist"),
276                    path);
277     }
278   else
279     {
280       guint32 _size, _flags;
281       GVariant *array;
282
283       g_variant_get (value, "(uu@ay)",
284                      &_size,
285                      &_flags,
286                      &array);
287
288       if (!res)
289         {
290           if (size)
291             *size = _size;
292           if (flags)
293             *flags = _flags;
294           if (data)
295             *data = g_variant_get_data (array);
296           if (data_size)
297             {
298               /* Don't report trailing newline that non-compressed files has */
299               if (_flags & G_RESOURCE_FLAGS_COMPRESSED)
300                 *data_size = g_variant_get_size (array);
301               else
302                 *data_size = g_variant_get_size (array) - 1;
303             }
304           res = TRUE;
305         }
306     }
307
308   g_free (free_path);
309   return res;
310 }
311
312 /**
313  * g_resource_open_stream:
314  * @resource: A #GResource.
315  * @path: A pathname inside the resource.
316  * @lookup_flags: A #GResourceLookupFlags.
317  * @error: return location for a #GError, or %NULL.
318  *
319  * Looks for a file at the specified @path in the resource and
320  * returns a #GInputStream that lets you read the data.
321  *
322  * @lookup_flags controls the behaviour of the lookup.
323  *
324  * Returns: (transfer full): #GInputStream or %NULL on error.
325  *     Free the returned object with g_object_unref().
326  *
327  * Since: 2.32
328  **/
329 GInputStream *
330 g_resource_open_stream (GResource *resource,
331                         const char *path,
332                         GResourceLookupFlags lookup_flags,
333                         GError **error)
334 {
335   const void *data;
336   gsize data_size;
337   guint32 flags;
338   GInputStream *stream, *stream2;
339
340   if (!do_lookup (resource, path, lookup_flags, NULL, &flags, &data, &data_size, error))
341     return NULL;
342
343   stream = g_memory_input_stream_new_from_data (data, data_size, NULL);
344   g_object_set_data_full (G_OBJECT (stream), "g-resource",
345                           g_resource_ref (resource),
346                           (GDestroyNotify)g_resource_unref);
347
348   if (flags & G_RESOURCE_FLAGS_COMPRESSED)
349     {
350       GZlibDecompressor *decompressor =
351         g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_ZLIB);
352
353       stream2 = g_converter_input_stream_new (stream, G_CONVERTER (decompressor));
354       g_object_unref (decompressor);
355       g_object_unref (stream);
356       stream = stream2;
357     }
358
359   return stream;
360 }
361
362 /**
363  * g_resource_lookup_data:
364  * @resource: A #GResource.
365  * @path: A pathname inside the resource.
366  * @lookup_flags: A #GResourceLookupFlags.
367  * @error: return location for a #GError, or %NULL.
368  *
369  * Looks for a file at the specified @path in the resource and
370  * returns a #GBytes that lets you directly access the data in
371  * memory.
372  *
373  * The data is always followed by a zero byte, so you
374  * can safely use the data as a C string. However, that byte
375  * is not included in the size of the GBytes.
376  *
377  * For uncompressed resource files this is a pointer directly into
378  * the resource bundle, which is typically in some readonly data section
379  * in the program binary. For compressed files we allocate memory on
380  * the heap and automatically uncompress the data.
381  *
382  * @lookup_flags controls the behaviour of the lookup.
383  *
384  * Returns: (transfer full): #GBytes or %NULL on error.
385  *     Free the returned object with g_bytes_unref().
386  *
387  * Since: 2.32
388  **/
389 GBytes *
390 g_resource_lookup_data (GResource *resource,
391                         const char *path,
392                         GResourceLookupFlags lookup_flags,
393                         GError **error)
394 {
395   const void *data;
396   guint32 flags;
397   gsize data_size;
398   gsize size;
399
400   if (!do_lookup (resource, path, lookup_flags, &size, &flags, &data, &data_size, error))
401     return NULL;
402
403   if (flags & G_RESOURCE_FLAGS_COMPRESSED)
404     {
405       char *uncompressed, *d;
406       const char *s;
407       GConverterResult res;
408       gsize d_size, s_size;
409       gsize bytes_read, bytes_written;
410
411
412       GZlibDecompressor *decompressor =
413         g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_ZLIB);
414
415       uncompressed = g_malloc (size + 1);
416
417       s = data;
418       s_size = data_size;
419       d = uncompressed;
420       d_size = size;
421
422       do
423         {
424           res = g_converter_convert (G_CONVERTER (decompressor),
425                                      s, s_size,
426                                      d, d_size,
427                                      G_CONVERTER_INPUT_AT_END,
428                                      &bytes_read,
429                                      &bytes_written,
430                                      NULL);
431           if (res == G_CONVERTER_ERROR)
432             {
433               g_free (uncompressed);
434               g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_INTERNAL,
435                            _("The resource at '%s' failed to decompress"),
436                            path);
437               return NULL;
438
439             }
440           s += bytes_read;
441           s_size -= bytes_read;
442           d += bytes_written;
443           d_size -= bytes_written;
444         }
445       while (res != G_CONVERTER_FINISHED);
446
447       uncompressed[size] = 0; /* Zero terminate */
448
449       return g_bytes_new_take (uncompressed, size);
450     }
451   else
452     return g_bytes_new_with_free_func (data, data_size, (GDestroyNotify)g_resource_unref, g_resource_ref (resource));
453 }
454
455 /**
456  * g_resource_get_info:
457  * @resource: A #GResource.
458  * @path: A pathname inside the resource.
459  * @lookup_flags: A #GResourceLookupFlags.
460  * @size:  (out) (allow-none): a location to place the length of the contents of the file,
461  *    or %NULL if the length is not needed
462  * @flags:  (out) (allow-none): a location to place the flags about the file,
463  *    or %NULL if the length is not needed
464  * @error: return location for a #GError, or %NULL.
465  *
466  * Looks for a file at the specified @path in the resource and
467  * if found returns information about it.
468  *
469  * @lookup_flags controls the behaviour of the lookup.
470  *
471  * Returns: %TRUE if the file was found. %FALSE if there were errors.
472  *
473  * Since: 2.32
474  **/
475 gboolean
476 g_resource_get_info (GResource *resource,
477                      const char *path,
478                      GResourceLookupFlags lookup_flags,
479                      gsize *size,
480                      guint32 *flags,
481                      GError **error)
482 {
483   return do_lookup (resource, path, lookup_flags, size, flags, NULL, NULL, error);
484 }
485
486 /**
487  * g_resource_enumerate_children:
488  * @resource: A #GResource.
489  * @path: A pathname inside the resource.
490  * @lookup_flags: A #GResourceLookupFlags.
491  * @error: return location for a #GError, or %NULL.
492  *
493  * Returns all the names of children at the specified @path in the resource.
494  * The return result is a %NULL terminated list of strings which should
495  * be released with g_strfreev().
496  *
497  * @lookup_flags controls the behaviour of the lookup.
498  *
499  * Returns: (array zero-terminated=1) (transfer full): an array of constant strings
500  *
501  * Since: 2.32
502  **/
503 char **
504 g_resource_enumerate_children (GResource *resource,
505                                const char *path,
506                                GResourceLookupFlags lookup_flags,
507                                GError **error)
508 {
509   gchar **children;
510   gsize path_len;
511   char *path_with_slash;
512
513   if (*path == 0)
514     {
515       g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
516                    _("The resource at '%s' does not exist"),
517                    path);
518       return NULL;
519     }
520
521   path_len = strlen (path);
522   if (path[path_len-1] != '/')
523     path_with_slash = g_strconcat (path, "/", NULL);
524   else
525     path_with_slash = g_strdup (path);
526
527   children = gvdb_table_list (resource->table, path_with_slash);
528   g_free (path_with_slash);
529
530   if (children == NULL)
531     {
532       g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
533                    _("The resource at '%s' does not exist"),
534                    path);
535       return NULL;
536     }
537
538   return children;
539 }
540
541 static GRWLock resources_lock;
542 static GList *registered_resources;
543
544 /**
545  * g_resources_register:
546  * @resource: A #GResource.
547  *
548  * Registers the resource with the process-global set of resources.
549  * Once a resource is registered the files in it can be accessed
550  * with the global resource lookup functions like g_resources_lookup_data().
551  *
552  * Since: 2.32
553  **/
554 void
555 g_resources_register (GResource *resource)
556 {
557   g_rw_lock_writer_lock (&resources_lock);
558
559   registered_resources = g_list_prepend (registered_resources,
560                                         g_resource_ref (resource));
561
562   g_rw_lock_writer_unlock (&resources_lock);
563 }
564
565 /**
566  * g_resources_unregister:
567  * @resource: A #GResource.
568  *
569  * Unregisters the resource from the process-global set of resources.
570  *
571  * Since: 2.32
572  **/
573 void
574 g_resources_unregister (GResource *resource)
575 {
576   g_rw_lock_writer_lock (&resources_lock);
577
578   if (g_list_find (registered_resources, resource) == NULL)
579     {
580       g_warning ("Tried to remove not registred resource");
581     }
582   else
583     {
584       registered_resources = g_list_remove (registered_resources,
585                                            resource);
586       g_resource_unref (resource);
587     }
588
589   g_rw_lock_writer_unlock (&resources_lock);
590 }
591
592 /**
593  * g_resources_open_stream:
594  * @path: A pathname inside the resource.
595  * @lookup_flags: A #GResourceLookupFlags.
596  * @error: return location for a #GError, or %NULL.
597  *
598  * Looks for a file at the specified @path in the set of
599  * globally registred resources and returns a #GInputStream
600  * that lets you read the data.
601  *
602  * @lookup_flags controls the behaviour of the lookup.
603  *
604  * Returns: (transfer full): #GInputStream or %NULL on error.
605  *     Free the returned object with g_object_unref().
606  *
607  * Since: 2.32
608  **/
609 GInputStream *
610 g_resources_open_stream (const char *path,
611                          GResourceLookupFlags lookup_flags,
612                          GError **error)
613 {
614   GInputStream *res = NULL;
615   GList *l;
616   GInputStream *stream;
617
618   g_rw_lock_reader_lock (&resources_lock);
619
620   for (l = registered_resources; l != NULL; l = l->next)
621     {
622       GResource *r = l->data;
623       GError *my_error = NULL;
624
625       stream = g_resource_open_stream (r, path, lookup_flags, &my_error);
626       if (stream == NULL &&
627           g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
628         {
629           g_clear_error (&my_error);
630         }
631       else
632         {
633           if (stream == NULL)
634             g_propagate_error (error, my_error);
635           res = stream;
636           break;
637         }
638     }
639
640   if (l == NULL)
641     g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
642                  _("The resource at '%s' does not exist"),
643                  path);
644
645   g_rw_lock_reader_unlock (&resources_lock);
646
647   return res;
648 }
649
650 /**
651  * g_resources_lookup_data:
652  * @path: A pathname inside the resource.
653  * @lookup_flags: A #GResourceLookupFlags.
654  * @error: return location for a #GError, or %NULL.
655  *
656  * Looks for a file at the specified @path in the set of
657  * globally registred resources and returns a #GBytes that
658  * lets you directly access the data in memory.
659  *
660  * The data is always followed by a zero byte, so you
661  * can safely use the data as a C string. However, that byte
662  * is not included in the size of the GBytes.
663  *
664  * For uncompressed resource files this is a pointer directly into
665  * the resource bundle, which is typically in some readonly data section
666  * in the program binary. For compressed files we allocate memory on
667  * the heap and automatically uncompress the data.
668  *
669  * @lookup_flags controls the behaviour of the lookup.
670  *
671  * Returns: (transfer full): #GBytes or %NULL on error.
672  *     Free the returned object with g_bytes_unref().
673  *
674  * Since: 2.32
675  **/
676 GBytes *
677 g_resources_lookup_data (const char *path,
678                          GResourceLookupFlags lookup_flags,
679                          GError **error)
680 {
681   GBytes *res = NULL;
682   GList *l;
683   GBytes *data;
684
685   g_rw_lock_reader_lock (&resources_lock);
686
687   for (l = registered_resources; l != NULL; l = l->next)
688     {
689       GResource *r = l->data;
690       GError *my_error = NULL;
691
692       data = g_resource_lookup_data (r, path, lookup_flags, &my_error);
693       if (data == NULL &&
694           g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
695         {
696           g_clear_error (&my_error);
697         }
698       else
699         {
700           if (data == NULL)
701             g_propagate_error (error, my_error);
702           res = data;
703           break;
704         }
705     }
706
707   if (l == NULL)
708     g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
709                  _("The resource at '%s' does not exist"),
710                  path);
711
712   g_rw_lock_reader_unlock (&resources_lock);
713
714   return res;
715 }
716
717 /**
718  * g_resources_enumerate_children:
719  * @path: A pathname inside the resource.
720  * @lookup_flags: A #GResourceLookupFlags.
721  * @error: return location for a #GError, or %NULL.
722  *
723  * Returns all the names of children at the specified @path in the set of
724  * globally registred resources.
725  * The return result is a %NULL terminated list of strings which should
726  * be released with g_strfreev().
727  *
728  * @lookup_flags controls the behaviour of the lookup.
729  *
730  * Returns: (array zero-terminated=1) (transfer full): an array of constant strings
731  *
732  * Since: 2.32
733  **/
734 char **
735 g_resources_enumerate_children (const char *path,
736                                 GResourceLookupFlags lookup_flags,
737                                 GError **error)
738 {
739   GHashTable *hash = NULL;
740   GList *l;
741   char **children;
742   int i;
743
744   g_rw_lock_reader_lock (&resources_lock);
745
746   for (l = registered_resources; l != NULL; l = l->next)
747     {
748       GResource *r = l->data;
749
750       children = g_resource_enumerate_children (r, path, 0, NULL);
751
752       if (children != NULL)
753         {
754           if (hash == NULL)
755             hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
756
757           for (i = 0; children[i] != NULL; i++)
758             g_hash_table_insert (hash, children[i], children[i]);
759           g_free (children);
760         }
761     }
762
763   g_rw_lock_reader_unlock (&resources_lock);
764
765   if (hash == NULL)
766     {
767       g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
768                    _("The resource at '%s' does not exist"),
769                    path);
770       return NULL;
771     }
772   else
773     {
774       GHashTableIter iter;
775       const char *key;
776       guint n_children;
777       n_children = g_hash_table_size (hash);
778       children = g_new (char *, n_children + 1);
779       i = 0;
780
781       g_hash_table_iter_init (&iter, hash);
782       while (g_hash_table_iter_next (&iter, (gpointer *)&key, NULL))
783         children[i++] = g_strdup (key);
784       children[i++] = NULL;
785
786       g_hash_table_destroy (hash);
787
788       return children;
789     }
790 }
791
792 /**
793  * g_resources_get_info:
794  * @path: A pathname inside the resource.
795  * @lookup_flags: A #GResourceLookupFlags.
796  * @size:  (out) (allow-none): a location to place the length of the contents of the file,
797  *    or %NULL if the length is not needed
798  * @flags:  (out) (allow-none): a location to place the flags about the file,
799  *    or %NULL if the length is not needed
800  * @error: return location for a #GError, or %NULL.
801  *
802  * Looks for a file at the specified @path in the set of
803  * globally registred resources and if found returns information about it.
804  *
805  * @lookup_flags controls the behaviour of the lookup.
806  *
807  * Returns: %TRUE if the file was found. %FALSE if there were errors.
808  *
809  * Since: 2.32
810  **/
811 gboolean
812 g_resources_get_info (const char   *path,
813                       GResourceLookupFlags lookup_flags,
814                       gsize        *size,
815                       guint32      *flags,
816                       GError      **error)
817 {
818   gboolean res = FALSE;
819   GList *l;
820   gboolean r_res;
821
822   g_rw_lock_reader_lock (&resources_lock);
823
824   for (l = registered_resources; l != NULL; l = l->next)
825     {
826       GResource *r = l->data;
827       GError *my_error = NULL;
828
829       r_res = g_resource_get_info (r, path, lookup_flags, size, flags, &my_error);
830       if (!r_res &&
831           g_error_matches (my_error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND))
832         {
833           g_clear_error (&my_error);
834         }
835       else
836         {
837           if (!r_res)
838             g_propagate_error (error, my_error);
839           res = r_res;
840           break;
841         }
842     }
843
844   if (l == NULL)
845     g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
846                  _("The resource at '%s' does not exist"),
847                  path);
848
849   g_rw_lock_reader_unlock (&resources_lock);
850
851   return res;
852 }