[glib] Add a resource tool



commit 716cf3558510be0a7ba7075a431ac2a5ad13b2bf
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Jan 22 02:39:11 2012 -0500

    Add a resource tool
    
    This lets you poke at resources in elf files and
    standalone resource bundles. So far, only listing
    and extracting resources is supported. The support
    for elf files requires libelf.

 configure.ac                     |   11 +
 gio/Makefile.am                  |   13 +-
 gio/gresource-bash-completion.sh |   58 ++++
 gio/gresource-tool.c             |  622 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 703 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 3c7f4ed..03c6931 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1819,6 +1819,17 @@ dnl   failure. try libattr
 fi
 AC_SUBST(XATTR_LIBS)
 
+dnl ************************
+dnl *** check for libelf ***
+dnl ************************
+
+AC_CHECK_LIB([elf], [elf_begin], have_libelf=yes, have_libelf=no)
+if test $have_libelf = yes; then
+  AC_DEFINE(HAVE_LIBELF, 1, [Define if libelf is available])
+  ELF_LIBS=-lelf
+fi
+AC_SUBST(ELF_LIBS)
+
 dnl ****************************************
 dnl *** platform dependent source checks ***
 dnl ****************************************
diff --git a/gio/Makefile.am b/gio/Makefile.am
index 0afd64e..815465e 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -693,10 +693,21 @@ gdbus_LDADD = libgio-2.0.la \
 completiondir = $(sysconfdir)/bash_completion.d
 completion_DATA = \
 	gdbus-bash-completion.sh		\
-	gsettings-bash-completion.sh
+	gsettings-bash-completion.sh		\
+	gresource-bash-completion.sh
 EXTRA_DIST += $(completion_DATA)
 
 # ------------------------------------------------------------------------
+# gresource tool
+
+bin_PROGRAMS += gresource
+gresource_SOURCES = gresource-tool.c
+gresource_LDADD = libgio-2.0.la \
+	$(top_builddir)/glib/libglib-2.0.la 		\
+	$(top_builddir)/gobject/libgobject-2.0.la \
+	$(ELF_LIBS)
+
+# ------------------------------------------------------------------------
 
 dist-hook: $(BUILT_EXTRA_DIST) ../build/win32/vs9/gio.vcproj ../build/win32/vs10/gio.vcxproj ../build/win32/vs10/gio.vcxproj.filters
 	files='$(BUILT_EXTRA_DIST)'; \
diff --git a/gio/gresource-bash-completion.sh b/gio/gresource-bash-completion.sh
new file mode 100644
index 0000000..ef1145d
--- /dev/null
+++ b/gio/gresource-bash-completion.sh
@@ -0,0 +1,58 @@
+
+# Check for bash
+[ -z "$BASH_VERSION" ] && return
+
+####################################################################################################
+
+__gresource() {
+  local choices coffset section
+
+  if [ ${COMP_CWORD} -gt 2 ]; then
+      if [ ${COMP_WORDS[1]} = --section ]; then
+          section=${COMP_WORDS[2]}
+          coffset=2
+      else
+          coffset=0
+      fi
+  else
+      coffset=0
+  fi
+
+  case "$((${COMP_CWORD}-$coffset))" in
+    1)
+      choices=$'--section \nhelp \nsections \nlist \ndetails \nextract '
+      ;;
+
+    2)
+      case "${COMP_WORDS[$(($coffset+1))]}" in
+        --section)
+          return 0
+          ;;
+
+        help)
+          choices=$'sections\nlist\ndetails\nextract'
+          ;;
+
+        sections|list|details|extract)
+          COMPREPLY=($(compgen -f -- ${COMP_WORDS[${COMP_CWORD}]}))
+          return 0
+          ;;
+      esac
+      ;;
+
+    3)
+      case "${COMP_WORDS[$(($coffset+1))]}" in
+        list|details|extract)
+          choices="$(gresource list ${COMP_WORDS[$(($coffset+2))]} 2> /dev/null | sed -e 's.$. .')"
+          ;;
+      esac
+      ;;
+  esac
+
+  local IFS=$'\n'
+  COMPREPLY=($(compgen -W "${choices}" -- "${COMP_WORDS[${COMP_CWORD}]}"))
+}
+
+####################################################################################################
+
+complete -o nospace -F __gresource gresource
diff --git a/gio/gresource-tool.c b/gio/gresource-tool.c
new file mode 100644
index 0000000..3e0d95c
--- /dev/null
+++ b/gio/gresource-tool.c
@@ -0,0 +1,622 @@
+/*
+ * Copyright  2012 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#ifdef HAVE_LIBELF
+#include <libelf.h>
+#include <gelf.h>
+#endif
+
+#include <gio/gio.h>
+#include <gi18n.h>
+
+/* GResource functions {{{1 */
+static GResource *
+get_resource (const gchar *file)
+{
+  gchar *content;
+  gsize size;
+  GResource *resource;
+  GBytes *data;
+
+  resource = NULL;
+
+  if (g_file_get_contents (file, &content, &size, NULL))
+    {
+      data = g_bytes_new_take (content, size);
+      resource = g_resource_new_from_data (data, NULL);
+      g_bytes_unref (data);
+    }
+
+  return resource;
+}
+
+static void
+list_resource (GResource   *resource,
+               const gchar *path,
+               const gchar *section,
+               const gchar *prefix,
+               gboolean     details)
+{
+  gchar **children;
+  gsize size;
+  guint32 flags;
+  gint i;
+  gchar *child;
+  GError *error = NULL;
+  gint len;
+
+  children = g_resource_enumerate_children (resource, path, 0, &error);
+  if (error)
+    {
+      g_printerr ("%s\n", error->message);
+      g_error_free (error);
+      return;
+    }
+  for (i = 0; children[i]; i++)
+    {
+      child = g_strconcat (path, children[i], NULL);
+
+      len = MIN (strlen (child), strlen (prefix));
+      if (strncmp (child, prefix, len) != 0)
+        continue;
+
+      if (g_resource_get_info (resource, child, 0, &size, &flags, NULL))
+        {
+          if (details)
+            g_print ("%s%s%6ld %s %s\n", section, section[0] ? " " : "", size, flags & G_RESOURCE_FLAGS_COMPRESSED ? "c" : "u", child);
+          else
+            g_print ("%s\n", child);
+        }
+      else
+        list_resource (resource, child, section, prefix, details);
+
+      g_free (child);
+    }
+  g_strfreev (children);
+}
+
+static void
+extract_resource (GResource   *resource,
+                  const gchar *path)
+{
+  GBytes *bytes;
+
+  bytes = g_resource_lookup_data (resource, path, 0, NULL);
+  if (bytes != NULL)
+    {
+      gconstpointer data;
+      gsize size, written;
+
+      data = g_bytes_get_data (bytes, &size);
+      written = fwrite (data, 1, size, stdout);
+      if (written < size)
+        g_printerr ("Data truncated\n");
+      g_bytes_unref (bytes);
+    }
+  else
+    {
+      g_printerr ("Can't find resource path %s\n", path);
+    }
+}
+
+/* Elf functions {{{1 */
+
+#ifdef HAVE_LIBELF
+
+static Elf *
+get_elf (const gchar *file,
+         gint        *fd)
+{
+  Elf *elf;
+
+  if (elf_version (EV_CURRENT) == EV_NONE )
+    return NULL;
+
+  *fd = open (file, O_RDONLY);
+  if (*fd < 0)
+    return NULL;
+
+  elf = elf_begin (*fd, ELF_C_READ, NULL);
+  if (elf == NULL)
+    return NULL;
+
+  if (elf_kind (elf) != ELF_K_ELF)
+    return NULL;
+
+  return elf;
+}
+
+typedef gboolean (*SectionCallback) (GElf_Shdr   *shdr,
+                                     const gchar *name,
+                                     gpointer     data);
+
+static void
+elf_foreach_resource_section (Elf             *elf,
+                              SectionCallback  callback,
+                              gpointer         data)
+{
+  size_t shstrndx, shnum;
+  size_t scnidx;
+  Elf_Scn *scn;
+  GElf_Shdr *shdr, shdr_mem;
+  const gchar *section_name;
+
+  elf_getshdrstrndx (elf, &shstrndx);
+  g_assert (shstrndx >= 0);
+
+  elf_getshdrnum (elf, &shnum);
+  g_assert (shnum >= 0);
+
+  for (scnidx = 1; scnidx < shnum; scnidx++)
+    {
+      scn = elf_getscn (elf, scnidx);
+      if (scn == NULL)
+        continue;
+
+      shdr = gelf_getshdr (scn, &shdr_mem);
+      if (shdr == NULL)
+        continue;
+
+      if (shdr->sh_type != SHT_PROGBITS)
+        continue;
+
+      section_name = elf_strptr (elf, shstrndx, shdr->sh_name);
+      if (section_name == NULL ||
+          !g_str_has_prefix (section_name, ".gresource."))
+        continue;
+
+      if (!callback (shdr, section_name + strlen (".gresource."), data))
+        break;
+    }
+}
+
+static GResource *
+resource_from_section (GElf_Shdr *shdr,
+                       int        fd)
+{
+  gsize page_size, page_offset;
+  char *contents;
+  GResource *resource;
+
+  resource = NULL;
+
+  page_size = sysconf(_SC_PAGE_SIZE);
+  page_offset = shdr->sh_offset % page_size;
+  contents = mmap (NULL,  shdr->sh_size + page_offset,
+                   PROT_READ, MAP_PRIVATE, fd, shdr->sh_offset - page_offset);
+  if (contents != MAP_FAILED)
+    {
+      GBytes *bytes;
+
+      bytes = g_bytes_new_static (contents + page_offset, shdr->sh_size);
+      resource = g_resource_new_from_data (bytes, NULL);
+      g_bytes_unref (bytes);
+    }
+  else
+    {
+      g_printerr ("Can't mmap resource section");
+    }
+
+  return resource;
+}
+
+typedef struct
+{
+  int fd;
+  const gchar *section;
+  const gchar *path;
+  gboolean details;
+  gboolean found;
+} CallbackData;
+
+static gboolean
+list_resources_cb (GElf_Shdr   *shdr,
+                   const gchar *section,
+                   gpointer     data)
+{
+  CallbackData *d = data;
+  GResource *resource;
+
+  if (d->section && strcmp (section, d->section) != 0)
+    return TRUE;
+
+  d->found = TRUE;
+
+  resource = resource_from_section (shdr, d->fd);
+  list_resource (resource, "/",
+                 d->section ? "" : section,
+                 d->path,
+                 d->details);
+  g_resource_unref (resource);
+
+  if (d->section)
+    return FALSE;
+
+  return TRUE;
+}
+
+static void
+elf_list_resources (Elf         *elf,
+                    int          fd,
+                    const gchar *section,
+                    const gchar *path,
+                    gboolean     details)
+{
+  CallbackData data;
+
+  data.fd = fd;
+  data.section = section;
+  data.path = path;
+  data.details = details;
+  data.found = FALSE;
+
+  elf_foreach_resource_section (elf, list_resources_cb, &data);
+
+  if (!data.found)
+    g_printerr ("Can't find resource section %s\n", section);
+}
+
+static gboolean
+extract_resource_cb (GElf_Shdr   *shdr,
+                     const gchar *section,
+                     gpointer     data)
+{
+  CallbackData *d = data;
+  GResource *resource;
+
+  if (d->section && strcmp (section, d->section) != 0)
+    return TRUE;
+
+  d->found = TRUE;
+
+  resource = resource_from_section (shdr, d->fd);
+  extract_resource (resource, d->path);
+  g_resource_unref (resource);
+
+  return FALSE;
+}
+
+static void
+elf_extract_resource (Elf         *elf,
+                      int          fd,
+                      const gchar *section,
+                      const gchar *path)
+{
+  CallbackData data;
+
+  data.fd = fd;
+  data.section = section;
+  data.path = path;
+  data.found = FALSE;
+
+  elf_foreach_resource_section (elf, extract_resource_cb, &data);
+
+  if (!data.found)
+    g_printerr ("Can't find resource section %s\n", section);
+}
+
+static gboolean
+print_section_name (GElf_Shdr   *shdr,
+                    const gchar *name,
+                    gpointer     data)
+{
+  g_print ("%s\n", name);
+  return TRUE;
+}
+
+#endif /* HAVE_LIBELF */
+
+  /* Toplevel commands {{{1 */
+
+static void
+cmd_sections (const gchar *file,
+              const gchar *section,
+              const gchar *path,
+              gboolean     details)
+{
+  GResource *resource;
+
+#ifdef HAVE_LIBELF
+
+  Elf *elf;
+  gint fd;
+
+  if ((elf = get_elf (file, &fd)))
+    {
+      elf_foreach_resource_section (elf, print_section_name, NULL);
+      elf_end (elf);
+      close (fd);
+    }
+  else
+
+#endif
+
+  if ((resource = get_resource (file)))
+    {
+      /* No sections */
+      g_resource_unref (resource);
+    }
+  else
+    {
+      g_printerr ("Don't know how to handle %s\n", file);
+#ifndef HAVE_LIBELF
+      g_printerr ("gresource is built without elf support\n");
+#endif
+    }
+}
+
+static void
+cmd_list (const gchar *file,
+          const gchar *section,
+          const gchar *path,
+          gboolean     details)
+{
+  GResource *resource;
+
+#ifdef HAVE_LIBELF
+
+  Elf *elf;
+  int fd;
+
+  if ((elf = get_elf (file, &fd)))
+    {
+      elf_list_resources (elf, fd, section, path ? path : "", details);
+      elf_end (elf);
+      close (fd);
+    }
+  else
+
+#endif
+
+  if ((resource = get_resource (file)))
+    {
+      list_resource (resource, "/", "", path ? path : "", details);
+      g_resource_unref (resource);
+    }
+  else
+    {
+      g_printerr ("Don't know how to handle %s\n", file);
+#ifndef HAVE_LIBELF
+      g_printerr ("gresource is built without elf support\n");
+#endif
+    }
+}
+
+static void
+cmd_extract (const gchar *file,
+             const gchar *section,
+             const gchar *path,
+             gboolean     details)
+{
+  GResource *resource;
+
+#ifdef HAVE_LIBELF
+
+  Elf *elf;
+  int fd;
+
+  if ((elf = get_elf (file, &fd)))
+    {
+      elf_extract_resource (elf, fd, section, path);
+      elf_end (elf);
+      close (fd);
+    }
+  else
+
+#endif
+
+  if ((resource = get_resource (file)))
+    {
+      extract_resource (resource, path);
+      g_resource_unref (resource);
+    }
+  else
+    {
+      g_printerr ("Don't know how to handle %s\n", file);
+#ifndef HAVE_LIBELF
+      g_printerr ("gresource is built without elf support\n");
+#endif
+    }
+}
+
+static gint
+cmd_help (gboolean     requested,
+          const gchar *command)
+{
+  const gchar *description;
+  const gchar *synopsis;
+  gchar *option;
+  GString *string;
+
+  option = NULL;
+
+  string = g_string_new (NULL);
+
+  if (command == NULL)
+    ;
+
+  else if (strcmp (command, "help") == 0)
+    {
+      description = _("Print help");
+      synopsis = "[COMMAND]";
+    }
+
+  else if (strcmp (command, "sections") == 0)
+    {
+      description = _("List sections containing resources in an elf FILE");
+      synopsis = "FILE";
+    }
+
+  else if (strcmp (command, "list") == 0)
+    {
+      description = _("List resources\n"
+                      "If SECTION is given, only list resources in this section\n"
+                      "If PATH is given, only list matching resources");
+      synopsis = "FILE [PATH]";
+      option = g_strdup_printf ("[--section %s]", _("SECTION"));
+    }
+
+  else if (strcmp (command, "details") == 0)
+    {
+      description = _("List resources with details\n"
+                      "If SECTION is given, only list resources in this section\n"
+                      "If PATH is given, only list matching resources\n"
+                      "Details include the section, size and compression");
+      synopsis = "FILE [PATH]";
+      option = g_strdup_printf ("[--section %s]", _("SECTION"));
+    }
+
+  else if (strcmp (command, "extract") == 0)
+    {
+      description = _("Extract a resource file to stdout");
+      synopsis = "FILE PATH";
+      option = g_strdup_printf ("[--section %s]", _("SECTION"));
+    }
+
+  else
+    {
+      g_string_printf (string, _("Unknown command %s\n\n"), command);
+      requested = FALSE;
+      command = NULL;
+    }
+
+  if (command == NULL)
+    {
+      g_string_append (string,
+      _("Usage:\n"
+        "  gresource [--section SECTION] COMMAND [ARGS...]\n"
+        "\n"
+        "Commands:\n"
+        "  help                      Show this information\n"
+        "  sections                  List resource sections\n"
+        "  list                      List resources\n"
+        "  details                   List resources with details\n"
+        "  extract                   Extract a resource\n"
+        "\n"
+        "Use 'gresource help COMMAND' to get detailed help.\n\n"));
+    }
+  else
+    {
+      g_string_append_printf (string, _("Usage:\n  gresource %s%s%s %s\n\n%s\n\n"),
+                              option ? option : "", option ? " " : "", command, synopsis[0] ? _(synopsis) : "", description);
+
+      g_string_append (string, _("Arguments:\n"));
+
+      if (option)
+        g_string_append (string,
+                         _("  SECTION   An (optional) elf section name\n"));
+
+      if (strstr (synopsis, "[COMMAND]"))
+        g_string_append (string,
+                       _("  COMMAND   The (optional) command to explain\n"));
+
+      if (strstr (synopsis, "FILE"))
+        {
+          if (strcmp (command, "sections") == 0)
+            g_string_append (string,
+                             _("  FILE      An elf file (a binary or a shared library)\n"));
+          else
+            g_string_append (string,
+                             _("  FILE      An elf file (a binary or a shared library)\n"
+                               "            or a compiled resource file\n"));
+        }
+
+      if (strstr (synopsis, "[PATH"))
+        g_string_append (string,
+                       _("  PATH      An (optional) resource path (may be partial)\n"));
+      else if (strstr (synopsis, "PATH"))
+        g_string_append (string,
+                       _("  PATH      A resource path\n"));
+
+      g_string_append (string, "\n");
+    }
+
+  if (requested)
+    g_print ("%s", string->str);
+  else
+    g_printerr ("%s\n", string->str);
+
+  g_free (option);
+  g_string_free (string, TRUE);
+
+  return requested ? 0 : 1;
+}
+
+/* main {{{1 */
+
+int
+main (int argc, char *argv[])
+{
+  gchar *section = NULL;
+  gboolean details = FALSE;
+  void (* function) (const gchar *, const gchar *, const gchar *, gboolean);
+
+  g_type_init ();
+
+  if (argc < 2)
+    return cmd_help (FALSE, NULL);
+
+  if (argc > 3 && strcmp (argv[1], "--section") == 0)
+    {
+      section = argv[2];
+      argv = argv + 2;
+      argc -= 2;
+    }
+
+  if (strcmp (argv[1], "help") == 0)
+    return cmd_help (TRUE, argv[2]);
+
+  else if (argc == 4 && strcmp (argv[1], "extract") == 0)
+    function = cmd_extract;
+
+  else if (argc == 3 && strcmp (argv[1], "sections") == 0)
+    function = cmd_sections;
+
+  else if ((argc == 3 || argc == 4) && strcmp (argv[1], "list") == 0)
+    {
+      function = cmd_list;
+      details = FALSE;
+    }
+  else if ((argc == 3 || argc == 4) && strcmp (argv[1], "details") == 0)
+    {
+      function = cmd_list;
+      details = TRUE;
+    }
+  else
+    return cmd_help (FALSE, argv[1]);
+
+  (* function) (argv[2], section, argc > 3 ? argv[3] : NULL, details);
+
+  return 0;
+}
+
+/* vim:set foldmethod=marker: */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]