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