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