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