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