Imported Upstream version 2.66.6
[platform/upstream/glib.git] / gio / gresource-tool.c
1 /*
2  * Copyright © 2012 Red Hat, Inc
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16  *
17  * Author: Matthias Clasen
18  */
19
20 #include "config.h"
21
22 #include <stdlib.h>
23 #include <stdio.h>
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <string.h>
29 #include <locale.h>
30
31 #ifdef HAVE_LIBELF
32 #include <libelf.h>
33 #include <gelf.h>
34 #endif
35
36 #ifdef HAVE_MMAP
37 #include <sys/mman.h>
38 #endif
39
40 #include <gio/gio.h>
41 #include <glib/gstdio.h>
42 #include <gi18n.h>
43
44 #include "glib/glib-private.h"
45
46 #if defined(HAVE_LIBELF) && defined(HAVE_MMAP)
47 #define USE_LIBELF
48 #endif
49
50 /* GResource functions {{{1 */
51 static GResource *
52 get_resource (const gchar *file)
53 {
54   gchar *content;
55   gsize size;
56   GResource *resource;
57   GBytes *data;
58
59   resource = NULL;
60
61   if (g_file_get_contents (file, &content, &size, NULL))
62     {
63       data = g_bytes_new_take (content, size);
64       resource = g_resource_new_from_data (data, NULL);
65       g_bytes_unref (data);
66     }
67
68   return resource;
69 }
70
71 static void
72 list_resource (GResource   *resource,
73                const gchar *path,
74                const gchar *section,
75                const gchar *prefix,
76                gboolean     details)
77 {
78   gchar **children;
79   gsize size;
80   guint32 flags;
81   gint i;
82   gchar *child;
83   GError *error = NULL;
84   gint len;
85
86   children = g_resource_enumerate_children (resource, path, 0, &error);
87   if (error)
88     {
89       g_printerr ("%s\n", error->message);
90       g_error_free (error);
91       return;
92     }
93   for (i = 0; children[i]; i++)
94     {
95       child = g_strconcat (path, children[i], NULL);
96
97       len = MIN (strlen (child), strlen (prefix));
98       if (strncmp (child, prefix, len) != 0)
99         {
100           g_free (child);
101           continue;
102         }
103
104       if (g_resource_get_info (resource, child, 0, &size, &flags, NULL))
105         {
106           if (details)
107             g_print ("%s%s%6"G_GSIZE_FORMAT " %s %s\n", section, section[0] ? " " : "", size, (flags & G_RESOURCE_FLAGS_COMPRESSED) ? "c" : "u", child);
108           else
109             g_print ("%s\n", child);
110         }
111       else
112         list_resource (resource, child, section, prefix, details);
113
114       g_free (child);
115     }
116   g_strfreev (children);
117 }
118
119 static void
120 extract_resource (GResource   *resource,
121                   const gchar *path)
122 {
123   GBytes *bytes;
124
125   bytes = g_resource_lookup_data (resource, path, 0, NULL);
126   if (bytes != NULL)
127     {
128       gconstpointer data;
129       gsize size, written;
130
131       data = g_bytes_get_data (bytes, &size);
132       written = fwrite (data, 1, size, stdout);
133       if (written < size)
134         g_printerr ("Data truncated\n");
135       g_bytes_unref (bytes);
136     }
137 }
138
139 /* Elf functions {{{1 */
140
141 #ifdef USE_LIBELF
142
143 static Elf *
144 get_elf (const gchar *file,
145          gint        *fd)
146 {
147   Elf *elf;
148
149   if (elf_version (EV_CURRENT) == EV_NONE )
150     return NULL;
151
152   *fd = g_open (file, O_RDONLY, 0);
153   if (*fd < 0)
154     return NULL;
155
156   elf = elf_begin (*fd, ELF_C_READ, NULL);
157   if (elf == NULL)
158     {
159       g_close (*fd, NULL);
160       *fd = -1;
161       return NULL;
162     }
163
164   if (elf_kind (elf) != ELF_K_ELF)
165     {
166       g_close (*fd, NULL);
167       *fd = -1;
168       return NULL;
169     }
170
171   return elf;
172 }
173
174 typedef gboolean (*SectionCallback) (GElf_Shdr   *shdr,
175                                      const gchar *name,
176                                      gpointer     data);
177
178 static void
179 elf_foreach_resource_section (Elf             *elf,
180                               SectionCallback  callback,
181                               gpointer         data)
182 {
183   size_t shstrndx, shnum;
184   size_t scnidx;
185   Elf_Scn *scn;
186   GElf_Shdr *shdr, shdr_mem;
187   const gchar *section_name;
188
189   elf_getshdrstrndx (elf, &shstrndx);
190   g_assert (shstrndx >= 0);
191
192   elf_getshdrnum (elf, &shnum);
193   g_assert (shnum >= 0);
194
195   for (scnidx = 1; scnidx < shnum; scnidx++)
196     {
197       scn = elf_getscn (elf, scnidx);
198       if (scn == NULL)
199         continue;
200
201       shdr = gelf_getshdr (scn, &shdr_mem);
202       if (shdr == NULL)
203         continue;
204
205       if (shdr->sh_type != SHT_PROGBITS)
206         continue;
207
208       section_name = elf_strptr (elf, shstrndx, shdr->sh_name);
209       if (section_name == NULL ||
210           !g_str_has_prefix (section_name, ".gresource."))
211         continue;
212
213       if (!callback (shdr, section_name + strlen (".gresource."), data))
214         break;
215     }
216 }
217
218 static GResource *
219 resource_from_section (GElf_Shdr *shdr,
220                        int        fd)
221 {
222   gsize page_size, page_offset;
223   char *contents;
224   GResource *resource;
225
226   resource = NULL;
227
228   page_size = sysconf(_SC_PAGE_SIZE);
229   page_offset = shdr->sh_offset % page_size;
230   contents = mmap (NULL,  shdr->sh_size + page_offset,
231                    PROT_READ, MAP_PRIVATE, fd, shdr->sh_offset - page_offset);
232   if (contents != MAP_FAILED)
233     {
234       GBytes *bytes;
235       GError *error = NULL;
236
237       bytes = g_bytes_new_static (contents + page_offset, shdr->sh_size);
238       resource = g_resource_new_from_data (bytes, &error);
239       g_bytes_unref (bytes);
240       if (error)
241         {
242           g_printerr ("%s\n", error->message);
243           g_error_free (error);
244         }
245     }
246   else
247     {
248       g_printerr ("Can't mmap resource section");
249     }
250
251   return resource;
252 }
253
254 typedef struct
255 {
256   int fd;
257   const gchar *section;
258   const gchar *path;
259   gboolean details;
260   gboolean found;
261 } CallbackData;
262
263 static gboolean
264 list_resources_cb (GElf_Shdr   *shdr,
265                    const gchar *section,
266                    gpointer     data)
267 {
268   CallbackData *d = data;
269   GResource *resource;
270
271   if (d->section && strcmp (section, d->section) != 0)
272     return TRUE;
273
274   d->found = TRUE;
275
276   resource = resource_from_section (shdr, d->fd);
277   list_resource (resource, "/",
278                  d->section ? "" : section,
279                  d->path,
280                  d->details);
281   g_resource_unref (resource);
282
283   if (d->section)
284     return FALSE;
285
286   return TRUE;
287 }
288
289 static void
290 elf_list_resources (Elf         *elf,
291                     int          fd,
292                     const gchar *section,
293                     const gchar *path,
294                     gboolean     details)
295 {
296   CallbackData data;
297
298   data.fd = fd;
299   data.section = section;
300   data.path = path;
301   data.details = details;
302   data.found = FALSE;
303
304   elf_foreach_resource_section (elf, list_resources_cb, &data);
305
306   if (!data.found)
307     g_printerr ("Can't find resource section %s\n", section);
308 }
309
310 static gboolean
311 extract_resource_cb (GElf_Shdr   *shdr,
312                      const gchar *section,
313                      gpointer     data)
314 {
315   CallbackData *d = data;
316   GResource *resource;
317
318   if (d->section && strcmp (section, d->section) != 0)
319     return TRUE;
320
321   d->found = TRUE;
322
323   resource = resource_from_section (shdr, d->fd);
324   extract_resource (resource, d->path);
325   g_resource_unref (resource);
326
327   if (d->section)
328     return FALSE;
329
330   return TRUE;
331 }
332
333 static void
334 elf_extract_resource (Elf         *elf,
335                       int          fd,
336                       const gchar *section,
337                       const gchar *path)
338 {
339   CallbackData data;
340
341   data.fd = fd;
342   data.section = section;
343   data.path = path;
344   data.found = FALSE;
345
346   elf_foreach_resource_section (elf, extract_resource_cb, &data);
347
348   if (!data.found)
349     g_printerr ("Can't find resource section %s\n", section);
350 }
351
352 static gboolean
353 print_section_name (GElf_Shdr   *shdr,
354                     const gchar *name,
355                     gpointer     data)
356 {
357   g_print ("%s\n", name);
358   return TRUE;
359 }
360
361 #endif /* USE_LIBELF */
362
363   /* Toplevel commands {{{1 */
364
365 static void
366 cmd_sections (const gchar *file,
367               const gchar *section,
368               const gchar *path,
369               gboolean     details)
370 {
371   GResource *resource;
372
373 #ifdef USE_LIBELF
374
375   Elf *elf;
376   gint fd;
377
378   if ((elf = get_elf (file, &fd)))
379     {
380       elf_foreach_resource_section (elf, print_section_name, NULL);
381       elf_end (elf);
382       close (fd);
383     }
384   else
385
386 #endif
387
388   if ((resource = get_resource (file)))
389     {
390       /* No sections */
391       g_resource_unref (resource);
392     }
393   else
394     {
395       g_printerr ("Don't know how to handle %s\n", file);
396 #ifndef USE_LIBELF
397       g_printerr ("gresource is built without elf support\n");
398 #endif
399     }
400 }
401
402 static void
403 cmd_list (const gchar *file,
404           const gchar *section,
405           const gchar *path,
406           gboolean     details)
407 {
408   GResource *resource;
409
410 #ifdef USE_LIBELF
411   Elf *elf;
412   int fd;
413
414   if ((elf = get_elf (file, &fd)))
415     {
416       elf_list_resources (elf, fd, section, path ? path : "", details);
417       elf_end (elf);
418       close (fd);
419     }
420   else
421
422 #endif
423
424   if ((resource = get_resource (file)))
425     {
426       list_resource (resource, "/", "", path ? path : "", details);
427       g_resource_unref (resource);
428     }
429   else
430     {
431       g_printerr ("Don't know how to handle %s\n", file);
432 #ifndef USE_LIBELF
433       g_printerr ("gresource is built without elf support\n");
434 #endif
435     }
436 }
437
438 static void
439 cmd_extract (const gchar *file,
440              const gchar *section,
441              const gchar *path,
442              gboolean     details)
443 {
444   GResource *resource;
445
446 #ifdef USE_LIBELF
447
448   Elf *elf;
449   int fd;
450
451   if ((elf = get_elf (file, &fd)))
452     {
453       elf_extract_resource (elf, fd, section, path);
454       elf_end (elf);
455       close (fd);
456     }
457   else
458
459 #endif
460
461   if ((resource = get_resource (file)))
462     {
463       extract_resource (resource, path);
464       g_resource_unref (resource);
465     }
466   else
467     {
468       g_printerr ("Don't know how to handle %s\n", file);
469 #ifndef USE_LIBELF
470       g_printerr ("gresource is built without elf support\n");
471 #endif
472     }
473 }
474
475 static gint
476 cmd_help (gboolean     requested,
477           const gchar *command)
478 {
479   const gchar *description;
480   const gchar *synopsis;
481   gchar *option;
482   GString *string;
483
484   option = NULL;
485
486   string = g_string_new (NULL);
487
488   if (command == NULL)
489     ;
490
491   else if (strcmp (command, "help") == 0)
492     {
493       description = _("Print help");
494       synopsis = _("[COMMAND]");
495     }
496
497   else if (strcmp (command, "sections") == 0)
498     {
499       description = _("List sections containing resources in an elf FILE");
500       synopsis = _("FILE");
501     }
502
503   else if (strcmp (command, "list") == 0)
504     {
505       description = _("List resources\n"
506                       "If SECTION is given, only list resources in this section\n"
507                       "If PATH is given, only list matching resources");
508       synopsis = _("FILE [PATH]");
509       option = g_strdup_printf ("[--section %s]", _("SECTION"));
510     }
511
512   else if (strcmp (command, "details") == 0)
513     {
514       description = _("List resources with details\n"
515                       "If SECTION is given, only list resources in this section\n"
516                       "If PATH is given, only list matching resources\n"
517                       "Details include the section, size and compression");
518       synopsis = _("FILE [PATH]");
519       option = g_strdup_printf ("[--section %s]", _("SECTION"));
520     }
521
522   else if (strcmp (command, "extract") == 0)
523     {
524       description = _("Extract a resource file to stdout");
525       synopsis = _("FILE PATH");
526       option = g_strdup_printf ("[--section %s]", _("SECTION"));
527     }
528
529   else
530     {
531       g_string_printf (string, _("Unknown command %s\n\n"), command);
532       requested = FALSE;
533       command = NULL;
534     }
535
536   if (command == NULL)
537     {
538       g_string_append (string,
539       _("Usage:\n"
540         "  gresource [--section SECTION] COMMAND [ARGS…]\n"
541         "\n"
542         "Commands:\n"
543         "  help                      Show this information\n"
544         "  sections                  List resource sections\n"
545         "  list                      List resources\n"
546         "  details                   List resources with details\n"
547         "  extract                   Extract a resource\n"
548         "\n"
549         "Use “gresource help COMMAND” to get detailed help.\n\n"));
550     }
551   else
552     {
553       g_string_append_printf (string, _("Usage:\n  gresource %s%s%s %s\n\n%s\n\n"),
554                               option ? option : "", option ? " " : "", command, synopsis[0] ? synopsis : "", description);
555
556       g_string_append (string, _("Arguments:\n"));
557
558       if (option)
559         g_string_append (string,
560                          _("  SECTION   An (optional) elf section name\n"));
561
562       if (strstr (synopsis, _("[COMMAND]")))
563         g_string_append (string,
564                        _("  COMMAND   The (optional) command to explain\n"));
565
566       if (strstr (synopsis, _("FILE")))
567         {
568           if (strcmp (command, "sections") == 0)
569             g_string_append (string,
570                              _("  FILE      An elf file (a binary or a shared library)\n"));
571           else
572             g_string_append (string,
573                              _("  FILE      An elf file (a binary or a shared library)\n"
574                                "            or a compiled resource file\n"));
575         }
576
577       if (strstr (synopsis, _("[PATH]")))
578         g_string_append (string,
579                        _("  PATH      An (optional) resource path (may be partial)\n"));
580       else if (strstr (synopsis, _("PATH")))
581         g_string_append (string,
582                        _("  PATH      A resource path\n"));
583
584       g_string_append (string, "\n");
585     }
586
587   if (requested)
588     g_print ("%s", string->str);
589   else
590     g_printerr ("%s\n", string->str);
591
592   g_free (option);
593   g_string_free (string, TRUE);
594
595   return requested ? 0 : 1;
596 }
597
598 /* main {{{1 */
599
600 int
601 main (int argc, char *argv[])
602 {
603   gchar *section = NULL;
604   gboolean details = FALSE;
605   void (* function) (const gchar *, const gchar *, const gchar *, gboolean);
606
607 #ifdef G_OS_WIN32
608   gchar *tmp;
609 #endif
610
611   setlocale (LC_ALL, GLIB_DEFAULT_LOCALE);
612   textdomain (GETTEXT_PACKAGE);
613
614 #ifdef G_OS_WIN32
615   tmp = _glib_get_locale_dir ();
616   bindtextdomain (GETTEXT_PACKAGE, tmp);
617   g_free (tmp);
618 #else
619   bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
620 #endif
621
622 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
623   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
624 #endif
625
626   if (argc < 2)
627     return cmd_help (FALSE, NULL);
628
629   if (argc > 3 && strcmp (argv[1], "--section") == 0)
630     {
631       section = argv[2];
632       argv = argv + 2;
633       argc -= 2;
634     }
635
636   if (strcmp (argv[1], "help") == 0)
637     return cmd_help (TRUE, argv[2]);
638
639   else if (argc == 4 && strcmp (argv[1], "extract") == 0)
640     function = cmd_extract;
641
642   else if (argc == 3 && strcmp (argv[1], "sections") == 0)
643     function = cmd_sections;
644
645   else if ((argc == 3 || argc == 4) && strcmp (argv[1], "list") == 0)
646     {
647       function = cmd_list;
648       details = FALSE;
649     }
650   else if ((argc == 3 || argc == 4) && strcmp (argv[1], "details") == 0)
651     {
652       function = cmd_list;
653       details = TRUE;
654     }
655   else
656     return cmd_help (FALSE, argv[1]);
657
658   (* function) (argv[2], section, argc > 3 ? argv[3] : NULL, details);
659
660   return 0;
661 }
662
663 /* vim:set foldmethod=marker: */