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