diff options
author | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2014-12-27 08:13:20 -0600 |
---|---|---|
committer | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2014-12-27 08:13:20 -0600 |
commit | 9b92536e6c51b66406d593745a938975e226f95e (patch) | |
tree | a40b0066dc774827f602f5e372c2f53656007553 /src | |
download | libr-9b92536e6c51b66406d593745a938975e226f95e.tar.gz libr-9b92536e6c51b66406d593745a938975e226f95e.zip |
Initial import
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 39 | ||||
-rw-r--r-- | src/config.h | 94 | ||||
-rw-r--r-- | src/cvtendian.h | 48 | ||||
-rw-r--r-- | src/gettext.h | 271 | ||||
-rw-r--r-- | src/libr-backends.h | 22 | ||||
-rw-r--r-- | src/libr-bfd.c | 533 | ||||
-rw-r--r-- | src/libr-bfd.h | 40 | ||||
-rw-r--r-- | src/libr-elf.c | 412 | ||||
-rw-r--r-- | src/libr-elf.h | 24 | ||||
-rw-r--r-- | src/libr-gtk.c | 443 | ||||
-rw-r--r-- | src/libr-gtk.h | 55 | ||||
-rw-r--r-- | src/libr-i18n.c | 84 | ||||
-rw-r--r-- | src/libr-i18n.h | 14 | ||||
-rw-r--r-- | src/libr-icons.c | 643 | ||||
-rw-r--r-- | src/libr-icons.h | 201 | ||||
-rw-r--r-- | src/libr-internal.h | 34 | ||||
-rw-r--r-- | src/libr-link.h | 26 | ||||
-rw-r--r-- | src/libr-ro.c | 351 | ||||
-rw-r--r-- | src/libr-ro.h | 62 | ||||
-rw-r--r-- | src/libr.c | 489 | ||||
-rw-r--r-- | src/libr.h | 416 | ||||
-rw-r--r-- | src/onecanvas.c | 446 | ||||
-rw-r--r-- | src/onecanvas.h | 6 | ||||
-rw-r--r-- | src/tempfiles.c | 317 | ||||
-rw-r--r-- | src/tempfiles.h | 13 |
25 files changed, 5083 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..5fbf00b --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,39 @@ +libr_la_includedir = $(includedir)/libr +LIBTOOL_DEPS = @LIBTOOL_DEPS@ + +INCLUDES = \ + -D__LIBR_BACKEND_@BACKEND_NAME@__ \ + -D__LIBR_BUILD__ \ + @LIBGLADE_CFLAGS@ \ + @BACKEND_CFLAGS@ \ + @EXTRA_CFLAGS@ + +lib_LTLIBRARIES = \ + libr.la + +libr_la_SOURCES = \ + libr-@LIBR_BACKEND@.c \ + tempfiles.c \ + onecanvas.c \ + libr-icons.c \ + libr-i18n.c \ + libr-gtk.c \ + libr.c + +libr_la_include_HEADERS = \ + gettext.h \ + libr-icons.h \ + libr-i18n.h \ + libr-gtk.h \ + libr.h + +libr_la_LIBADD = \ + @BACKEND_LIBS@ \ + @EXTRA_LIBS@ + +# If not in a fakeroot environment then run ldconfig +install: install-am + @if [ ! -n "${FAKEROOTKEY}" ]; then \ + echo "Regenerating system dependencies..."; \ + ldconfig; \ + fi diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..a51743e --- /dev/null +++ b/src/config.h @@ -0,0 +1,94 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if translation of program messages to the user's native + language is requested. */ +#define ENABLE_NLS 1 + +/* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the + CoreFoundation framework. */ +/* #undef HAVE_CFLOCALECOPYCURRENT */ + +/* Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in + the CoreFoundation framework. */ +/* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */ + +/* Define if the GNU dcgettext() function is already present or preinstalled. + */ +#define HAVE_DCGETTEXT 1 + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define if the GNU gettext() function is already present or preinstalled. */ +#define HAVE_GETTEXT 1 + +/* Define if you have the iconv() function and it works. */ +/* #undef HAVE_ICONV */ + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the <math.h> header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the <pthread.h> header file. */ +#define HAVE_PTHREAD_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the <zlib.h> header file. */ +#define HAVE_ZLIB_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libr" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "0.4.0" diff --git a/src/cvtendian.h b/src/cvtendian.h new file mode 100644 index 0000000..d69158b --- /dev/null +++ b/src/cvtendian.h @@ -0,0 +1,48 @@ +#ifndef __CVTENDIAN_H +#define __CVTENDIAN_H + +/* Support for swapping bytes (endian conversion) */ +#include <byteswap.h> + +/* For obtaining the host endian type */ +#include <endian.h> +#if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define HOST_ENDIAN ELFDATA2LSB +#elif (__BYTE_ORDER == __BIG_ENDIAN) +# define HOST_ENDIAN ELFDATA2MSB +#else +# error "Failed to detect host endian type" +#endif + +/* + * Convert the endian of a parameter + */ +static int ConvertEndian(void *ptr, int bytes) +{ + switch(bytes) + { + case 2: + { + uint16_t *value = (uint16_t *) ptr; + + *value = bswap_16(*value); + } return 1; + case 4: + { + uint32_t *value = (uint32_t *) ptr; + + *value = bswap_32(*value); + } return 1; + case 8: + { + uint64_t *value = (uint64_t *) ptr; + + *value = bswap_64(*value); + } return 1; + default: + break; + } + return 0; +} + +#endif /* __CVTENDIAN_H */ diff --git a/src/gettext.h b/src/gettext.h new file mode 100644 index 0000000..209921e --- /dev/null +++ b/src/gettext.h @@ -0,0 +1,271 @@ +/* Convenience header for conditional use of GNU <libintl.h>. + Copyright (C) 1995-1998, 2000-2002, 2004-2006 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 + Library General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. */ + +#ifndef _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + +/* NLS can be disabled through the configure --disable-nls option. */ +#if ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +# include <libintl.h> + +/* You can set the DEFAULT_TEXT_DOMAIN macro to specify the domain used by + the gettext() and ngettext() macros. This is an alternative to calling + textdomain(), and is useful for libraries. */ +# ifdef DEFAULT_TEXT_DOMAIN +# undef gettext +# define gettext(Msgid) \ + dgettext (DEFAULT_TEXT_DOMAIN, Msgid) +# undef ngettext +# define ngettext(Msgid1, Msgid2, N) \ + dngettext (DEFAULT_TEXT_DOMAIN, Msgid1, Msgid2, N) +# endif + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of <locale.h> a NOP. We don't include <libintl.h> + as well because people using "gettext.h" will not include <libintl.h>, + and also including <libintl.h> would fail on SunOS 4, whereas <locale.h> + is OK. */ +#if defined(__sun) +# include <locale.h> +#endif + +/* Many header files from the libstdc++ coming with g++ 3.3 or newer include + <libintl.h>, which chokes if dcgettext is defined as a macro. So include + it now, to make later inclusions of <libintl.h> a NOP. */ +#if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3) +# include <cstdlib> +# if (__GLIBC__ >= 2) || _GLIBCXX_HAVE_LIBINTL_H +# include <libintl.h> +# endif +#endif + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. + On pre-ANSI systems without 'const', the config.h file is supposed to + contain "#define const". */ +# define gettext(Msgid) ((const char *) (Msgid)) +# define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid)) +# define dcgettext(Domainname, Msgid, Category) \ + ((void) (Category), dgettext (Domainname, Msgid)) +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 \ + ? ((void) (Msgid2), (const char *) (Msgid1)) \ + : ((void) (Msgid1), (const char *) (Msgid2))) +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((void) (Domainname), ngettext (Msgid1, Msgid2, N)) +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((void) (Category), dngettext(Domainname, Msgid1, Msgid2, N)) +# define textdomain(Domainname) ((const char *) (Domainname)) +# define bindtextdomain(Domainname, Dirname) \ + ((void) (Domainname), (const char *) (Dirname)) +# define bind_textdomain_codeset(Domainname, Codeset) \ + ((void) (Domainname), (const char *) (Codeset)) + +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +/* The separator between msgctxt and msgid in a .mo file. */ +#define GETTEXT_CONTEXT_GLUE "\004" + +/* Pseudo function calls, taking a MSGCTXT and a MSGID instead of just a + MSGID. MSGCTXT and MSGID must be string literals. MSGCTXT should be + short and rarely need to change. + The letter 'p' stands for 'particular' or 'special'. */ +#ifdef DEFAULT_TEXT_DOMAIN +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#else +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#endif +#define dpgettext(Domainname, Msgctxt, Msgid) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#define dcpgettext(Domainname, Msgctxt, Msgid, Category) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, Category) +#ifdef DEFAULT_TEXT_DOMAIN +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#else +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#endif +#define dnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dcnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N, Category) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, Category) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +pgettext_aux (const char *domain, + const char *msg_ctxt_id, const char *msgid, + int category) +{ + const char *translation = dcgettext (domain, msg_ctxt_id, category); + if (translation == msg_ctxt_id) + return msgid; + else + return translation; +} + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +npgettext_aux (const char *domain, + const char *msg_ctxt_id, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) +{ + const char *translation = + dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); + if (translation == msg_ctxt_id || translation == msgid_plural) + return (n == 1 ? msgid : msgid_plural); + else + return translation; +} + +/* The same thing extended for non-constant arguments. Here MSGCTXT and MSGID + can be arbitrary expressions. But for string literals these macros are + less efficient than those above. */ + +#include <string.h> + +#define _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS \ + (((__GNUC__ >= 3 || __GNUG__ >= 2) && !__STRICT_ANSI__) \ + /* || __STDC_VERSION__ >= 199901L */ ) + +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS +#include <stdlib.h> +#endif + +#define pgettext_expr(Msgctxt, Msgid) \ + dcpgettext_expr (NULL, Msgctxt, Msgid, LC_MESSAGES) +#define dpgettext_expr(Domainname, Msgctxt, Msgid) \ + dcpgettext_expr (Domainname, Msgctxt, Msgid, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +dcpgettext_expr (const char *domain, + const char *msgctxt, const char *msgid, + int category) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + translation = dcgettext (domain, msg_ctxt_id, category); +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + if (msg_ctxt_id != buf) + free (msg_ctxt_id); +#endif + if (translation != msg_ctxt_id) + return translation; + } + return msgid; +} + +#define npgettext_expr(Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (NULL, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dnpgettext_expr(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (Domainname, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +dcnpgettext_expr (const char *domain, + const char *msgctxt, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + translation = dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + if (msg_ctxt_id != buf) + free (msg_ctxt_id); +#endif + if (!(translation == msg_ctxt_id || translation == msgid_plural)) + return translation; + } + return (n == 1 ? msgid : msgid_plural); +} + +#endif /* _LIBGETTEXT_H */ diff --git a/src/libr-backends.h b/src/libr-backends.h new file mode 100644 index 0000000..a0cd59c --- /dev/null +++ b/src/libr-backends.h @@ -0,0 +1,22 @@ +#ifndef __LIBR_BACKENDS_H +#define __LIBR_BACKENDS_H + +/* + * All of the backend functions are explicitly declared internal to prevent any custom backend + * from leaving out one of these critical functions. + */ +INTERNAL_FN libr_intstatus add_section(libr_file *file_handle, char *resource_name, libr_section **retscn); +INTERNAL_FN void *data_pointer(libr_section *scn, libr_data *data); +INTERNAL_FN size_t data_size(libr_section *scn, libr_data *data); +INTERNAL_FN libr_intstatus find_section(libr_file *file_handle, char *section, libr_section **retscn); +INTERNAL_FN libr_data *get_data(libr_file *file_handle, libr_section *scn); +INTERNAL_FN void initialize_backend(void); +INTERNAL_FN libr_data *new_data(libr_file *file_handle, libr_section *scn); +INTERNAL_FN libr_section *next_section(libr_file *file_handle, libr_section *scn); +INTERNAL_FN libr_intstatus remove_section(libr_file *file_handle, libr_section *scn); +INTERNAL_FN char *section_name(libr_file *file_handle, libr_section *scn); +INTERNAL_FN libr_intstatus set_data(libr_file *file_handle, libr_section *scn, libr_data *data, off_t offset, char *buffer, size_t size); +INTERNAL_FN libr_intstatus open_handles(libr_file *file_handle, char *filename, libr_access_t access); +INTERNAL_FN void write_output(libr_file *file_handle); + +#endif /* __LIBR_BACKENDS_H */ diff --git a/src/libr-bfd.c b/src/libr-bfd.c new file mode 100644 index 0000000..c4bc8b1 --- /dev/null +++ b/src/libr-bfd.c @@ -0,0 +1,533 @@ +/* + * + * Copyright (c) 2008-2011 Erich Hoover + * + * libr libbfd Backend - Add resources into ELF binaries using libbfd + * + * *** PLEASE READ THE README FILE FOR LICENSE DETAILS SPECIFIC TO THIS FILE *** + * + * This program 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 License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * To provide feedback, report bugs, or otherwise contact me: + * ehoover at mines dot edu + * + */ + +/* Include compile-time parameters */ +#include "config.h" + +#include "libr.h" +#include "libr-internal.h" + +/* File access */ +#include <fcntl.h> + +/* Safe rename requires some errno() knowledge */ +#include <errno.h> + +#include <sys/stat.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +/* + * Build the libr_file handle for processing with libbfd + */ +libr_intstatus open_handles(libr_file *file_handle, char *filename, libr_access_t access) +{ + bfd *handle = NULL; + + handle = bfd_openr(filename, "default"); + if(!handle) + RETURN(LIBR_ERROR_OPENFAILED, "Failed to open input file"); + if(!bfd_check_format(handle, bfd_object)) + RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: not a libbfd object"); + if(bfd_get_flavour(handle) != bfd_target_elf_flavour) + RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: not an ELF file"); + bfd_set_error(bfd_error_no_error); + file_handle->filename = filename; + file_handle->bfd_read = handle; + file_handle->access = access; + if(access == LIBR_READ_WRITE) + { + struct stat file_stat; + int fd; + + /* Check for write permission on the file */ + fd = open(filename, O_WRONLY); + if(fd == ERROR) + RETURN(LIBR_ERROR_WRITEPERM, "No write permission for file"); + close(fd); + /* Obtain the access mode of the input file */ + if(stat(filename, &file_stat) == ERROR) + RETURN(LIBR_ERROR_NOSIZE, "Failed to obtain file size"); + file_handle->filemode = file_stat.st_mode; + file_handle->fileowner = file_stat.st_uid; + file_handle->filegroup = file_stat.st_gid; + /* Open a temporary file with the same settings as the input file */ + strcpy(file_handle->tempfile, LIBR_TEMPFILE); + file_handle->fd_handle = mkstemp(file_handle->tempfile); + handle = bfd_openw(file_handle->tempfile, bfd_get_target(file_handle->bfd_read)); + if(!bfd_set_format(handle, bfd_get_format(file_handle->bfd_read))) + RETURN(LIBR_ERROR_SETFORMAT, "Failed to set output file format to input file format"); + if(!bfd_set_arch_mach(handle, bfd_get_arch(file_handle->bfd_read), bfd_get_mach(file_handle->bfd_read))) + RETURN(LIBR_ERROR_SETARCH, "Failed to set output file architecture to input file architecture"); + /* twice needed ? */ + if(!bfd_set_format(handle, bfd_get_format(file_handle->bfd_read))) + RETURN(LIBR_ERROR_SETFORMAT, "Failed to set output file format to input file format"); + file_handle->bfd_write = handle; + } + else + { + file_handle->fd_handle = 0; + file_handle->bfd_write = NULL; + } + RETURN_OK; +} + +/* + * Check to see if a symbol should be kept + */ +int keep_symbol(libr_section *sections, libr_section *chkscn) +{ + libr_section *scn; + + /* Check that the section is publicly exposed */ + for(scn = sections; scn != NULL; scn = scn->next) + { + if(scn == chkscn) + { + /* if it is, and has size zero, then it was marked for deletion */ + if(bfd_get_section_size(chkscn) == 0) + return false; + return true; + } + } + return true; +} + +/* + * Remove the symbol corresponding to a deleted section + */ +void remove_sections(libr_section *sections, void *symtab_buffer, long *symtab_count) +{ + asymbol **symtab = (asymbol **) symtab_buffer; + long i, cnt = *symtab_count; + + for(i=0;i<cnt;i++) + { + libr_section *chkscn = NULL; + asymbol *symbol = symtab[i]; + + if(symbol != NULL) + chkscn = bfd_get_section(symbol); + if(chkscn != NULL && !keep_symbol(sections, chkscn)) + { + /* remove the symbol from the table */ + asymbol **tmp = (asymbol **) malloc(sizeof(asymbol *) * (cnt-(i+1))); + memcpy(&tmp[0], &symtab[i+1], sizeof(asymbol *) * (cnt-(i+1))); + memcpy(&symtab[i], &tmp[0], sizeof(asymbol *) * (cnt-(i+1))); + free(tmp); + cnt--; + } + } + *symtab_count = cnt; +} + +int setup_sections(bfd *ihandle, bfd *ohandle) +{ + libr_section *iscn, *oscn; + bfd_vma vma; + + for(iscn = ihandle->sections; iscn != NULL; iscn = iscn->next) + { + if(bfd_get_section_size(iscn) == 0) + { + continue; /* Section has been marked for deletion */ + } + /* Use SEC_LINKER_CREATED to ask the libbfd backend to take care of configuring the section */ + + // Keep the ARM_ATTRIBUTES section type intact on armhf systems + // If this is not done, readelf -A will not print any architecture information for the modified library, + // and ldd will report that the library cannot be found + if (strcmp(iscn->name, ".ARM.attributes") == 0) + { + oscn = bfd_make_section_anyway_with_flags(ohandle, iscn->name, iscn->flags); + } + else + { + oscn = bfd_make_section_anyway_with_flags(ohandle, iscn->name, iscn->flags | SEC_LINKER_CREATED); + } + if(oscn == NULL) + { + printf("failed to create out section: %s\n", bfd_errmsg(bfd_get_error())); + return false; + } + if(!bfd_set_section_size(ohandle, oscn, iscn->size)) + { + printf("failed to set data size: %s\n", bfd_errmsg(bfd_get_error())); + return false; + } + vma = bfd_section_vma(ihandle, iscn); + if(!bfd_set_section_vma(ohandle, oscn, vma)) + { + printf("failed to set virtual memory address: %s\n", bfd_errmsg(bfd_get_error())); + return false; + } + oscn->lma = iscn->lma; + if(!bfd_set_section_alignment(ohandle, oscn, bfd_section_alignment(ihandle, iscn))) + { + printf("failed to compute section alignment: %s\n", bfd_errmsg(bfd_get_error())); + return false; + } + oscn->entsize = iscn->entsize; + iscn->output_section = oscn; + iscn->output_offset = vma; + if(!bfd_copy_private_section_data(ihandle, iscn, ohandle, oscn)) + { + printf("failed to compute section alignment: %s\n", bfd_errmsg(bfd_get_error())); + return false; + } + } + return true; +} + +/* + * Go through the rather complicated process of using libbfd to build the output file + */ +int build_output(libr_file *file_handle) +{ + void *symtab_buffer = NULL, *reloc_buffer = NULL, *buffer = NULL; + bfd_size_type symtab_size, reloc_size, size; + bfd *ohandle = file_handle->bfd_write; + bfd *ihandle = file_handle->bfd_read; + long symtab_count, reloc_count; + libr_section *iscn, *oscn; + + if(!bfd_set_start_address(ohandle, bfd_get_start_address(ihandle))) + { + printf("failed to set start address: %s\n", bfd_errmsg(bfd_get_error())); + return false; + } + if(!bfd_set_file_flags(ohandle, bfd_get_file_flags(ihandle))) + { + printf("failed to set file flags: %s\n", bfd_errmsg(bfd_get_error())); + return false; + } + /* Setup the sections in the output file */ + if(!setup_sections(ihandle, ohandle)) + return false; /* error already printed */ + if(!bfd_copy_private_header_data(ihandle, ohandle)) + { + printf("failed to copy header: %s\n", bfd_errmsg(bfd_get_error())); + return false; /* failed to create section */ + } + /* Get the old symbol table */ + if((bfd_get_file_flags(ihandle) & HAS_SYMS) == 0) + { + printf("file has no symbol table: %s\n", bfd_errmsg(bfd_get_error())); + return false; + } + symtab_size = bfd_get_symtab_upper_bound(ihandle); + if((signed)symtab_size < 0) + { + printf("failed to get symbol table size: %s\n", bfd_errmsg(bfd_get_error())); + return false; + } + symtab_buffer = malloc(symtab_size); + symtab_count = bfd_canonicalize_symtab(ihandle, symtab_buffer); + if(symtab_count < 0) + { + printf("failed to get symbol table number of entries: %s\n", bfd_errmsg(bfd_get_error())); + return false; + } + /* Tweak the symbol table to remove sections that no-longer exist */ + remove_sections(ihandle->sections, symtab_buffer, &symtab_count); + bfd_set_symtab(ohandle, symtab_buffer, symtab_count); + /* Actually copy section data */ + for(iscn = ihandle->sections; iscn != NULL; iscn = iscn->next) + { + size = bfd_get_section_size(iscn); + if(size == 0) + continue; /* Section has been marked for deletion */ + oscn = iscn->output_section; + reloc_size = bfd_get_reloc_upper_bound(ihandle, iscn); + if(reloc_size == 0) + bfd_set_reloc(ohandle, oscn, NULL, 0); + else + { + reloc_buffer = malloc(reloc_size); + reloc_count = bfd_canonicalize_reloc(ihandle, iscn, reloc_buffer, symtab_buffer); + bfd_set_reloc(ohandle, oscn, reloc_buffer, reloc_count); + } + + if(bfd_get_section_flags(ihandle, iscn) & SEC_HAS_CONTENTS) + { + /* NOTE: if the section is just being copied then do that, otherwise grab + * the user data for the section (stored previously by set_data) + */ + if(iscn->userdata == NULL) + { + buffer = malloc(size); + if(!bfd_get_section_contents(ihandle, iscn, buffer, 0, size)) + { + printf("failed to get section contents: %s\n", bfd_errmsg(bfd_get_error())); + free(buffer); + return false; + } + } + else + buffer = iscn->userdata; + if(!bfd_set_section_contents(ohandle, oscn, buffer, 0, size)) + { + printf("failed to set section contents: %s\n", bfd_errmsg(bfd_get_error())); + free(buffer); + return false; + } + free(buffer); + if(!bfd_copy_private_section_data(ihandle, iscn, ohandle, oscn)) + { + printf("failed to copy private section data: %s\n", bfd_errmsg(bfd_get_error())); + return false; + } + } + } + if(!bfd_copy_private_bfd_data(ihandle, ohandle)) + { + printf("failed to copy private data: %s\n", bfd_errmsg(bfd_get_error())); + return false; + } + return true; +} + +/* + * Perform a cross-device safe rename + */ +int safe_rename(const char *old, const char *new) +{ + char buffer[1024]; + FILE *in, *out; + int read; + + in = fopen(old, "r"); + if(!in) + return -1; + out = fopen(new, "w"); + if(!out) + return -1; + while(!feof(in) && !ferror(in)) + { + read = fread(buffer, 1, sizeof(buffer), in); + fwrite(buffer, read, 1, out); + } + fclose(in); + fclose(out); + if(ferror(in)) + { + remove(new); + return -1; + } + return remove(old); +} + +/* + * Write the output file using the libbfd method + */ +void write_output(libr_file *file_handle) +{ + int write_ok = false; + + if(file_handle->bfd_write != NULL) + { + write_ok = true; + if(!build_output(file_handle)) + { + printf("failed to build output file.\n"); + write_ok = false; + } + if(!bfd_close(file_handle->bfd_write)) + { + printf("failed to close write handle.\n"); + write_ok = false; + } + if(file_handle->fd_handle != 0 && close(file_handle->fd_handle)) + { + write_ok = false; + printf("failed to close write file descriptor.\n"); + } + } + /* The read handle must be closed last since it is used in the write process */ + if(!bfd_close(file_handle->bfd_read)) + printf("failed to close read handle.\n"); + /* Copy the temporary output over the input */ + if(write_ok) + { + if(rename(file_handle->tempfile, file_handle->filename) < 0) + { + if(errno != EXDEV || safe_rename(file_handle->tempfile, file_handle->filename) < 0) + printf("failed to rename output file: %m\n"); + } + if(chmod(file_handle->filename, file_handle->filemode) < 0) + printf("failed to set file mode.\n"); + if(chown(file_handle->filename, file_handle->fileowner, file_handle->filegroup) < 0) + printf("failed to set file ownership.\n"); + } +} + +/* + * Find a named section from the ELF file using libbfd + */ +libr_intstatus find_section(libr_file *file_handle, char *section_name, libr_section **retscn) +{ + libr_section *scn; + + for(scn = file_handle->bfd_read->sections; scn != NULL; scn = scn->next) + { + if(strcmp(scn->name, section_name) == 0) + { + *retscn = scn; + RETURN_OK; + } + } + RETURN(LIBR_ERROR_NOSECTION, "ELF resource section not found"); +} + +/* + * Obtain the data from a section using libbfd + */ +libr_data *get_data(libr_file *file_handle, libr_section *scn) +{ + libr_data *data = malloc(scn->size); + + if(!bfd_get_section_contents(file_handle->bfd_read, scn, data, 0, scn->size)) + { + free(data); + data = NULL; + } + scn->userdata = data; + return data; +} + +/* + * Create new data for a section using libbfd + */ +libr_data *new_data(libr_file *file_handle, libr_section *scn) +{ + /* NOTE: expanding data is handled by set_data for libbfd */ + if(scn->userdata != NULL) + return scn->userdata; + scn->size = 0; + scn->userdata = malloc(0); + return scn->userdata; +} + +/* + * Create new data for a section using libbfd (at least, do so memory-wise) + */ +libr_intstatus set_data(libr_file *file_handle, libr_section *scn, libr_data *data, off_t offset, char *buffer, size_t size) +{ + char *intbuffer = NULL; + + /* special case: clear buffer */ + if(buffer == NULL) + { + scn->size = 0; + if(scn->userdata != NULL) + free(scn->userdata); + RETURN_OK; + } + /* normal case: add new data to the buffer */ + scn->size = offset + size; + scn->userdata = realloc(data, scn->size); + if(scn->userdata == NULL) + RETURN(LIBR_ERROR_MEMALLOC, "Failed to allocate memory for data"); + intbuffer = scn->userdata; + memcpy(&intbuffer[offset], buffer, size); + RETURN_OK; +} + +/* + * Create a new section using libbfd + */ +libr_intstatus add_section(libr_file *file_handle, char *resource_name, libr_section **retscn) +{ + libr_section *scn = NULL; + + scn = bfd_make_section(file_handle->bfd_read, resource_name); + if(scn == NULL) + RETURN(LIBR_ERROR_NEWSECTION, "Failed to create new section"); + if(!bfd_set_section_flags(file_handle->bfd_read, scn, SEC_HAS_CONTENTS | SEC_DATA | SEC_IN_MEMORY)) + RETURN(LIBR_ERROR_SETFLAGS, "Failed to set flags for section"); + *retscn = scn; + RETURN_OK; +} + +/* + * Remove a section and eliminate it from the ELF string table using libbfd + */ +libr_intstatus remove_section(libr_file *file_handle, libr_section *scn) +{ + scn->size = 0; + RETURN_OK; +} + +/* + * Return the pointer to the actual data in the section + */ +void *data_pointer(libr_section *scn, libr_data *data) +{ + return data; +} + +/* + * Return the size of the data in the section + */ +size_t data_size(libr_section *scn, libr_data *data) +{ + return scn->size; +} + +/* + * Return the next section in the ELF file + */ +libr_section *next_section(libr_file *file_handle, libr_section *scn) +{ + /* get the first section */ + if(scn == NULL) + { + if(file_handle->bfd_read == NULL) + return NULL; + return file_handle->bfd_read->sections; + } + return scn->next; +} + +/* + * Return the name of a section + */ +char *section_name(libr_file *file_handle, libr_section *scn) +{ + return (char *) scn->name; +} + +/* + * Initialize libbfd + */ +void initialize_backend(void) +{ + bfd_init(); +} + diff --git a/src/libr-bfd.h b/src/libr-bfd.h new file mode 100644 index 0000000..7b6ea3e --- /dev/null +++ b/src/libr-bfd.h @@ -0,0 +1,40 @@ +#ifndef __LIBR_BFD_H +#define __LIBR_BFD_H + +#include "config.h" + +#include <sys/types.h> +#include <stdint.h> +#include <bfd.h> + +#if BFD_HOST_64BIT_LONG + #if defined(__i386) + #error "Using incorrect binutils header file for architecture." + #endif +#else + #if defined(__amd64) + #error "Using incorrect binutils header file for architecture." + #endif +#endif + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +typedef struct _libr_file { + int fd_handle; + bfd *bfd_read; + bfd *bfd_write; + char *filename; + mode_t filemode; + uid_t fileowner; + gid_t filegroup; + char tempfile[LIBR_TEMPFILE_LEN]; + libr_access_t access; +} libr_file; + +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + +/* for a clean internal API */ +typedef asection libr_section; +typedef void libr_data; + +#endif /* __LIBR_BFD_H */ diff --git a/src/libr-elf.c b/src/libr-elf.c new file mode 100644 index 0000000..be2daae --- /dev/null +++ b/src/libr-elf.c @@ -0,0 +1,412 @@ +/* + * + * Copyright (c) 2008 Erich Hoover + * + * libr libelf Backend - Add resources into ELF binaries using libelf + * + * This program 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.1 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * To provide feedback, report bugs, or otherwise contact me: + * ehoover at mines dot edu + * + */ + +/* Include compile-time parameters */ +#include "config.h" + +#include "libr.h" + +#include <sys/stat.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> + +//#define MANUAL_LAYOUT true + +extern void libr_set_error(libr_intstatus error); + +/* + * Write the output file using libelf + */ +void write_output(libr_file *file_handle) +{ + /* Update the ELF file on the disk */ + if(elf_update(file_handle->elf_handle, ELF_C_NULL) < 0) + { + printf("elf_update() failed: %s.", elf_errmsg(-1)); + return; + } + if(elf_update(file_handle->elf_handle, ELF_C_WRITE) < 0) + { + printf("elf_update() failed: %s.", elf_errmsg(-1)); + return; + } + /* Close the handles */ + elf_end(file_handle->elf_handle); + close(file_handle->fd_handle); +} + +/* + * Return the size of the file represented by the file descriptor + */ +off_t file_size(int fd) +{ + struct stat file_stat; + + if(fstat(fd, &file_stat) == ERROR) + return ERROR; + return file_stat.st_size; +} + +/* + * Open the handles for working with the file using libelf + */ +libr_intstatus open_handles(libr_file *file_handle, char *filename, libr_access_t access) +{ + const int elf_access[2] = {ELF_C_READ, ELF_C_RDWR}; + const int fd_access[2] = {O_RDONLY, O_RDWR}; + Elf *e = NULL; + int fd = 0; + + if((fd = open(filename, fd_access[access], 0)) < 0) + RETURN(LIBR_ERROR_OPENFAILED, "Failed to open input file"); + if((e = elf_begin(fd, elf_access[access], NULL)) == NULL) + RETURN(LIBR_ERROR_BEGINFAILED, "Failed to open ELF file: %s.", elf_errmsg(-1)); + if(elf_kind(e) != ELF_K_ELF) + RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format"); + + file_handle->access = access; + file_handle->fd_handle = fd; + file_handle->elf_handle = e; + file_handle->file_size = file_size(fd); + file_handle->version = EV_CURRENT; /* This should probably match the rest of the file */ + RETURN_OK; +} + +/* + * Expand a section + * (Only used when manually controlling ELF layout) + */ +#ifdef MANUAL_LAYOUT +libr_intstatus expand_section(Elf *e, Elf_Scn *scn, size_t size, int reset) +{ + size_t offset = 0, delta = 0; + Elf_Scn *tmpscn = NULL; + GElf_Shdr shdr; + + if(gelf_getshdr(scn, &shdr) != &shdr) + RETURN(LIBR_INTERROR_GETSHDR, "Failed to obtain ELF header: %s", elf_errmsg(-1)); + if(reset) + { + delta = (size-shdr.sh_size); + shdr.sh_size = size; + } + else + { + delta = size; + shdr.sh_size += size; + } + offset = shdr.sh_offset; + if(gelf_update_shdr(scn, &shdr) < 0) + RETURN(LIBR_ERROR_UPDATE, "Failed to perform dynamic update: %s.", elf_errmsg(-1)); + if(elf_update(e, ELF_C_NULL) < 0) + RETURN(LIBR_ERROR_UPDATE, "Failed to perform dynamic update: %s.", elf_errmsg(-1)); + /* Update any section that follows this one data-wise */ +/* +****** This does not work yet + while((tmpscn = elf_nextscn(e, tmpscn)) != NULL) + { + if(tmpscn == scn) + continue; + if(gelf_getshdr(tmpscn, &shdr) != &shdr) + return LIBR_INTERROR_GETSHDR; + if(offset < shdr.sh_offset) + { + if((name = elf_strptr(e, ehdr.e_shstrndx, shdr.sh_name)) == NULL) + RETURN(LIBR_ERROR_STRPTR, "Failed to obtain section string pointer: %s.", elf_errmsg(-1)); + shdr.sh_offset += delta; + if(gelf_update_shdr(tmpscn, &shdr) < 0) + RETURN(LIBR_ERROR_UPDATE, "Failed to perform dynamic update: %s.", elf_errmsg(-1)); + if(elf_update(e, ELF_C_NULL) < 0) + RETURN(LIBR_ERROR_UPDATE, "Failed to perform dynamic update: %s.", elf_errmsg(-1)); + } + } +*/ + return LIBR_OK; +} +#endif /* MANUAL_LAYOUT */ + +/* + * Obtain the data from a section using libelf + */ +libr_data *get_data(libr_file *file_handle, libr_section *scn) +{ + return elf_getdata(scn, NULL); +} + +/* + * Create new data for a section using libelf + */ +libr_data *new_data(libr_file *file_handle, libr_section *scn) +{ + return elf_newdata(scn); +} + +/* + * Set data for a section using libelf (not written yet) + */ +libr_intstatus set_data(libr_file *file_handle, libr_section *scn, libr_data *data, off_t offset, char *buffer, size_t size) +{ + data->d_align = 1; + data->d_off = offset; + data->d_buf = buffer; + data->d_type = ELF_T_BYTE; + data->d_size = size; + data->d_version = file_handle->version; +#ifdef MANUAL_LAYOUT + if(expand_section(file_handle->elf_handle, scn, data->d_size, true) != LIBR_OK) + RETURN(LIBR_ERROR_EXPANDSECTION, "Failed to expand section"); +#else + if(elf_update(file_handle->elf_handle, ELF_C_NULL) < 0) + RETURN(LIBR_ERROR_UPDATE, "Failed to perform dynamic update: %s.", elf_errmsg(-1)); + if(elf_update(file_handle->elf_handle, ELF_C_WRITE) < 0) + RETURN(LIBR_ERROR_UPDATE, "Failed to perform dynamic update: %s.", elf_errmsg(-1)); +#endif /* MANUAL_LAYOUT */ + RETURN_OK; +} + +/* + * Find a named section from the ELF file using libelf + */ +libr_intstatus find_section(libr_file *file_handle, char *section, libr_section **retscn) +{ + Elf *e = file_handle->elf_handle; + Elf_Scn *scn = NULL; + char *name = NULL; + GElf_Ehdr ehdr; + GElf_Shdr shdr; + uintmax_t si; + + if(gelf_getehdr(e, &ehdr) == NULL) + RETURN(LIBR_ERROR_GETEHDR, "Failed to obtain ELF header: %s", elf_errmsg(-1)); + while((scn = elf_nextscn(e, scn)) != NULL) + { + if(gelf_getshdr(scn, &shdr) != &shdr) + RETURN(LIBR_ERROR_GETSHDR, "Failed to obtain ELF section header: %s", elf_errmsg(-1)); + if((name = elf_strptr(e, ehdr.e_shstrndx, shdr.sh_name)) == NULL) + RETURN(LIBR_ERROR_STRPTR, "Failed to obtain section string pointer: %s.", elf_errmsg(-1)); + + si = (uintmax_t) elf_ndxscn(scn); +/* +printf("%d: %s (%d %d)\n", (long) si, name, (long) shdr.sh_offset, (long) shdr.sh_size); +*/ + if(strcmp(name, section) == 0) + { + *retscn = scn; + RETURN_OK; + } + } + RETURN(LIBR_ERROR_NOSECTION, "ELF resource section not found"); +} + +/* + * Add a new section and create a name for it in the ELF string table using libelf + */ +libr_intstatus add_section(libr_file *file_handle, char *section, Elf_Scn **retscn) +{ + Elf_Scn *scn = NULL, *strscn = NULL; + Elf *e = file_handle->elf_handle; +#ifdef MANUAL_LAYOUT + size_t tblsize = 0; +#endif /* MANUAL_LAYOUT */ + Elf_Data *data; + GElf_Ehdr ehdr; + GElf_Shdr shdr; + + if(gelf_getehdr(e, &ehdr) == NULL) + RETURN(LIBR_ERROR_GETEHDR, "Failed to obtain ELF header: %s", elf_errmsg(-1)); + /* TODO: Support creating a string table for objects that don't have one */ + if(!ehdr.e_shstrndx) + RETURN(LIBR_ERROR_NOTABLE, "No ELF string table"); + strscn = elf_getscn(e, ehdr.e_shstrndx); + if(strscn == NULL) + RETURN(LIBR_ERROR_TABLE, "Failed to open string table: %s.", elf_errmsg(-1)); + data = elf_newdata(strscn); + if(data == NULL) + RETURN(LIBR_ERROR_NEWDATA, "Failed to create data for section"); + data->d_align = 1; + +#ifdef MANUAL_LAYOUT +{ + GElf_Shdr strshdr; + + if(gelf_getshdr(strscn, &strshdr) != &strshdr) + RETURN(LIBR_ERROR_GETSHDR, "Failed to obtain ELF section header: %s", elf_errmsg(-1)); + data->d_off = strshdr.sh_size; +#endif /* MANUAL_LAYOUT */ + + data->d_size = (size_t) strlen(section)+1; + data->d_type = ELF_T_BYTE; + data->d_buf = section; + data->d_version = file_handle->version; + +#ifdef MANUAL_LAYOUT + if(expand_section(e, strscn, data->d_size, false) != LIBR_OK) + return false; +} +#else + /* Update the internal offset information */ + if(elf_update(e, ELF_C_NULL) < 0) + RETURN(LIBR_ERROR_UPDATE, "Failed to perform dynamic update: %s.", elf_errmsg(-1)); +#endif /* MANUAL_LAYOUT */ + + /* seek to the end of the section data */ + if((scn = elf_newscn(e)) == NULL) + RETURN(LIBR_ERROR_NEWSECTION, "Failed to create new section"); + if(gelf_getshdr(scn, &shdr) != &shdr) + RETURN(LIBR_ERROR_GETSHDR, "Failed to obtain ELF section header: %s", elf_errmsg(-1)); + shdr.sh_addralign = 1; +#ifdef MANUAL_LAYOUT + shdr.sh_offset = file_handle->file_size; +#endif /* MANUAL_LAYOUT */ + shdr.sh_size = 0; + shdr.sh_name = data->d_off; + shdr.sh_type = SHT_NOTE; /* TODO: Does "NOTE" type fit best? */ + shdr.sh_flags = SHF_WRITE; + shdr.sh_entsize = 0; + if(gelf_update_shdr(scn, &shdr) < 0) + RETURN(LIBR_ERROR_UPDATE, "Failed to perform dynamic update: %s.", elf_errmsg(-1)); + *retscn = scn; + RETURN_OK; +} + +/* + * Remove a section and eliminate it from the ELF string table using libelf + */ +libr_intstatus remove_section(libr_file *file_handle, libr_section *scn) +{ + unsigned int table_size, str_size; + char *buffer = NULL, *tmp = NULL; + Elf *e = file_handle->elf_handle; + int remaining_size; + Elf_Scn *strscn; + Elf_Data *data; + GElf_Ehdr ehdr; + GElf_Shdr shdr; + + if(gelf_getehdr(e, &ehdr) == NULL) + RETURN(LIBR_ERROR_GETEHDR, "Failed to obtain ELF header: %s", elf_errmsg(-1)); + /* Grab the string table */ + if(!ehdr.e_shstrndx) + RETURN(LIBR_ERROR_NOTABLE, "No ELF string table"); + strscn = elf_getscn(e, ehdr.e_shstrndx); + if(strscn == NULL) + RETURN(LIBR_ERROR_TABLE, "Failed to open string table: %s.", elf_errmsg(-1)); + if((data = elf_getdata(strscn, NULL)) == NULL) + RETURN(LIBR_ERROR_GETDATA, "Failed to obtain data of section"); + /* Find where the section name is in the string table */ + if(gelf_getshdr(scn, &shdr) != &shdr) + RETURN(LIBR_ERROR_GETSHDR, "Failed to obtain ELF section header: %s", elf_errmsg(-1)); + table_size = data->d_size; + buffer = (char *) data->d_buf; + /* Excise the string from the table */ + str_size = strlen(&buffer[shdr.sh_name])+1; + remaining_size = table_size-(shdr.sh_name+str_size); + if(remaining_size < 0) + RETURN(LIBR_ERROR_SIZEMISMATCH, "Section's data size does not make sense"); + if(remaining_size > 0) + { + /* If there is data after our icon entry in the table then it must be moved before resizing + * NOTE: Using memcpy with overlapping addresses is not allowed, use temporary buffer. + */ + tmp = (char *) malloc(remaining_size); + memcpy(tmp, &buffer[shdr.sh_name+str_size], remaining_size); + memcpy(&buffer[shdr.sh_name], tmp, remaining_size); + free(tmp); + } + data->d_size -= str_size; + /* Update the internal offset information */ + if(elf_update(e, ELF_C_NULL) < 0) + RETURN(LIBR_ERROR_UPDATE, "Failed to perform dynamic update: %s.", elf_errmsg(-1)); +#ifdef MANUAL_LAYOUT +{ + GElf_Shdr strshdr; + + if(gelf_getshdr(strscn, &strshdr) != &strshdr) + RETURN(LIBR_ERROR_GETSHDR, "Failed to obtain ELF section header: %s", elf_errmsg(-1)); + strshdr.sh_size -= str_size; + if(gelf_update_shdr(strscn, &strshdr) < 0) + RETURN(LIBR_ERROR_UPDATE, "Failed to perform dynamic update: %s.", elf_errmsg(-1)); +} +#endif /* MANUAL_LAYOUT */ + + /* Clear the section itself and update the offsets */ + if(elfx_remscn(e, scn) == 0) + RETURN(LIBR_ERROR_REMOVESECTION, "Failed to remove section: %s.", elf_errmsg(-1)); + RETURN_OK; +} + +/* + * Return the pointer to the actual data in the section + */ +void *data_pointer(libr_section *scn, libr_data *data) +{ + return data->d_buf; +} + +/* + * Return the size of the data in the section + */ +size_t data_size(libr_section *scn, libr_data *data) +{ + return data->d_size; +} + +/* + * Return the next section in the ELF file + */ +libr_section *next_section(libr_file *file_handle, libr_section *scn) +{ + return elf_nextscn(file_handle->elf_handle, scn); +} + +/* + * Retrieve the name of a section + */ +char *section_name(libr_file *file_handle, libr_section *scn) +{ + char *name = NULL; + GElf_Shdr shdr; + GElf_Ehdr ehdr; + + if(gelf_getehdr(file_handle->elf_handle, &ehdr) == NULL) + return NULL; + if(gelf_getshdr(scn, &shdr) != &shdr) + return NULL; + if((name = elf_strptr(file_handle->elf_handle, ehdr.e_shstrndx, shdr.sh_name)) == NULL) + return NULL; + return strdup(name); +} + +/* + * Initialize the libelf backend + */ +void initialize_backend(void) +{ + if(elf_version(EV_CURRENT) == EV_NONE) + return; //errx(EX_SOFTWARE, "ELF library initialization failed: %s", elf_errmsg(-1)); +} diff --git a/src/libr-elf.h b/src/libr-elf.h new file mode 100644 index 0000000..4c632e8 --- /dev/null +++ b/src/libr-elf.h @@ -0,0 +1,24 @@ +#ifndef __LIBR_ELF_H +#define __LIBR_ELF_H + +/* Handle ELF files */ +#include <libelf.h> +#include <gelf.h> + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +typedef struct _libr_file { + int fd_handle; + Elf *elf_handle; + size_t file_size; + libr_access_t access; + unsigned int version; +} libr_file; + +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + +/* for a clean internal API */ +typedef Elf_Scn libr_section; +typedef Elf_Data libr_data; + +#endif /* __LIBR_ELF_H */ diff --git a/src/libr-gtk.c b/src/libr-gtk.c new file mode 100644 index 0000000..f746aa8 --- /dev/null +++ b/src/libr-gtk.c @@ -0,0 +1,443 @@ +/* + * + * Copyright (c) 2008-2009 Erich Hoover + * + * libr GTK support - Convenience functions for using resources in GTK applications + * + * This program 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.1 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * To provide feedback, report bugs, or otherwise contact me: + * ehoover at mines dot edu + * + */ + +/* Include compile-time parameters */ +#include "config.h" + +#include "libr.h" +#include "libr-gtk.h" +#include "libr-icons.h" +#include "tempfiles.h" + +/* For loading GTK/GDK images */ +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <glib.h> + +/* For loading GLADE files */ +#include <glade/glade.h> + +/* For loading GTK+ Builder files */ +#include <gtk/gtk.h> + +/* For malloc/free */ +#include <stdlib.h> + +/* For string handling */ +#include <string.h> + +typedef gchar * (*GladeFileCallback)(GladeXML *, const gchar *, guint *); +GladeFileCallback glade_set_file_callback(GladeFileCallback callback, gpointer user_data); + +/* Use weak binding for all glade and GTK+ requirements */ +#pragma weak glade_set_file_callback + +#pragma weak gtk_window_set_default_icon_list +#pragma weak gdk_pixbuf_loader_get_pixbuf +#pragma weak gtk_builder_add_from_string +#pragma weak gdk_pixbuf_loader_set_size +#pragma weak g_type_check_instance_cast +#pragma weak gtk_builder_add_from_file +#pragma weak glade_xml_new_from_buffer +#pragma weak gdk_pixbuf_loader_write +#pragma weak gdk_pixbuf_loader_close +#pragma weak gdk_pixbuf_loader_new +#pragma weak g_signal_connect_data +#pragma weak g_signal_connect +#pragma weak gtk_builder_new +#pragma weak g_object_unref +#pragma weak glade_xml_new +#pragma weak g_list_append +#pragma weak glade_init +#pragma weak gtk_init +#pragma weak g_free + +#define GLADE_SECTION ".glade" +#define BUILDER_SECTION ".ui" + +/* + * Handle the resource request from libglade + */ +gchar *libr_glade_read_resource(GladeXML *gladefile, const gchar *filename, guint *size, gpointer user_data) +{ + return libr_malloc((libr_file *) user_data, (char *) filename, (size_t *) size); +} + +/* + * Handle the resource request from GtkBuilder + */ +gboolean libr_gtk_read_resource(GtkBuilder *builder, const gchar *filename, gchar **data, gsize *size, GError **error, gpointer user_data) +{ + if(data == NULL) + return FALSE; + *data = libr_malloc((libr_file *) user_data, (char *) filename, (size_t *) size); + if(*data == NULL) + return FALSE; + return TRUE; +} + +/* + * Load the libglade resource appropriately for the currently installed version + * (AKA, hurray hacks!) + */ +GladeXML *libr_new_glade(libr_file *handle, char *gladefile, size_t gladefile_size) +{ + if(glade_set_file_callback) /* The not-yet (ever?) existing way */ + { + /* Register a callback for libglade to load our resources */ + if(glade_set_file_callback((GladeFileCallback) libr_glade_read_resource, handle) != NULL) + printf("warning: over-wrote an application callback!\n"); + /* Initialize libglade almost as usual, just use a buffer instead of a file */ + return glade_xml_new_from_buffer(gladefile, gladefile_size, NULL, NULL); + } + else /* The hacky way */ + { + char *glade_file[PATH_MAX]; + GladeXML *ret = NULL; + char *temp_folder; + + temp_folder = libr_extract_resources(handle); + if(temp_folder == NULL) + return NULL; + strcpy((char*)glade_file, temp_folder); + strcat((char*)glade_file, "/"); + strcat((char*)glade_file, GLADE_SECTION); + ret = glade_xml_new((char*)glade_file, NULL, NULL); + if(ret == NULL) + cleanup_folder(temp_folder); + else + register_folder_cleanup(temp_folder); + return ret; + } +} + +/* + * Load the GtkBuilder resource appropriately for the currently installed version + * (AKA, hurray hacks!) + */ +int libr_new_builder(libr_file *handle, char *builderfile, size_t builderfile_size, GtkBuilder *builder) +{ + /* Register a callback for GtkBuilder to load our resources */ + if(g_signal_connect(builder, "load-resource", (GCallback) libr_gtk_read_resource, handle)) + { + /* Initialize GtkBuilder almost as usual, just use a buffer instead of a file */ + if(gtk_builder_add_from_string(builder, builderfile, builderfile_size, NULL) == 0) + return false; + return true; + } + else /* The hacky way */ + { + char *builder_file[PATH_MAX]; + char *temp_folder; + int ret = false; + + temp_folder = libr_extract_resources(handle); + if(temp_folder == NULL) + return false; + strcpy((char*)builder_file, temp_folder); + strcat((char*)builder_file, "/"); + strcat((char*)builder_file, BUILDER_SECTION); + ret = gtk_builder_add_from_file(builder, (char*)builder_file, NULL); + if(ret == 0) + cleanup_folder(temp_folder); + else + register_folder_cleanup(temp_folder); + g_free(temp_folder); + return (ret != 0); + } +} + +/* + * Return a GTK icon list + */ +EXPORT_FN IconList *libr_gtk_iconlist(libr_file *handle) +{ + int sizes[] = {16, 32, 48, 96, 128}; + IconList *icons = NULL; + GdkPixbuf *icon = NULL; + int sizes_len = 5, i; + + if(handle == NULL) + { + /* Must pass a file handle to obtain the icons from */ + return NULL; + } + if(gtk_init == NULL) + { + /* GTK+ was not linked with the application */ + return false; + } + /* Go through the list of GTK "required" image sizes and build the icons */ + for(i=0;i<sizes_len;i++) + { + libr_icon *icon_handle = libr_icon_geticon_bysize(handle, sizes[i]); + GdkPixbufLoader *stream = gdk_pixbuf_loader_new(); + char *iconfile = NULL; + size_t iconfile_size; + + if(icon_handle == NULL) + { + /* Failed to find an icon */ + printf("Failed to find an icon\n"); + continue; + } + iconfile = libr_icon_malloc(icon_handle, &iconfile_size); + if(iconfile == NULL) + { + /* Failed to obtain embedded icon */ + continue; + } + libr_icon_close(icon_handle); + /* TODO: Use the "size-prepared" signal to properly scale the width and height +void user_function (GdkPixbufLoader *loader, gint width, gint height, gpointer user_data) + */ + gdk_pixbuf_loader_set_size(stream, sizes[i], sizes[i]); + if(!gdk_pixbuf_loader_write(stream, (unsigned char *)iconfile, iconfile_size, NULL)) + { + /* Failed to process image */ + continue; + } + if(!gdk_pixbuf_loader_close(stream, NULL)) + { + /* Failed to create image */ + continue; + } + icon = gdk_pixbuf_loader_get_pixbuf(stream); + if(icon == NULL) + { + /* Failed to convert image to pixbuf */ + continue; + } + icons = g_list_append(icons, icon); + } + return icons; +} + +/* + * Shared GtkBuilder resource loading + */ +GtkBuilder *libr_gtk_load_internal(libr_file *handle, char *resource_name) +{ + GtkBuilder *builder = NULL; + size_t builder_length; + char *builder_data; + + /* Obtain the GtkBuilder XML definition */ + builder_data = libr_malloc(handle, resource_name, &builder_length); + if(builder_data == NULL) + { + /* Failed to obtain embedded GtkBuilder definition file */ + goto failed; + } + /* Setup the GtkBuilder environment */ + builder = gtk_builder_new(); + if(builder == NULL) + goto failed; + if(!libr_new_builder(handle, builder_data, builder_length, builder)) + { + /* Failed to build interface from resource file */ + g_object_unref(G_OBJECT(builder)); + goto failed; + } +failed: + free(builder_data); + return builder; +} + +/* + * Load the requested GtkBuilder resource and any applicable icons + */ +EXPORT_FN int libr_gtk_load(BuilderHandle **gtk_ret, char *resource_name) +{ + libr_file *handle; + int ret = false; + + if(gtk_ret == NULL) + { + /* Why on earth would you call this without obtaining the handle to the resource? */ + return false; + } + if(gtk_builder_new == NULL) + { + /* GtkBuilder was not linked with the application */ + return false; + } + /* Obtain the handle to the executable */ + if((handle = libr_open(NULL, LIBR_READ)) == NULL) + { + /* "Failed to open this executable (%s) for resources", progname() */ + return false; + } + register_internal_handle(handle); + *gtk_ret = libr_gtk_load_internal(handle, resource_name); + if(*gtk_ret == NULL) + goto failed; + ret = true; +failed: + if(!ret) + libr_close(handle); + return ret; +} + +/* + * Automatically load the ".ui" GtkBuilder resource and any applicable icons + */ +EXPORT_FN int libr_gtk_autoload(BuilderHandle **gtk_ret, IconList **icons_ret, int set_default_icon) +{ + GList *icons = NULL; + libr_file *handle; + int ret = false; + + if(gtk_ret == NULL) + { + /* Why on earth would you call this without obtaining the handle to the resource? */ + return false; + } + if(gtk_builder_new == NULL) + { + /* GtkBuilder was not linked with the application */ + return false; + } + /* Obtain the handle to the executable */ + if((handle = libr_open(NULL, LIBR_READ)) == NULL) + { + /* "Failed to open this executable (%s) for resources", progname() */ + return false; + } + register_internal_handle(handle); + /* Obtain the icons from the ELF binary */ + icons = libr_gtk_iconlist(handle); + /* Set the embedded icons as the default icon list (if requested) */ + if(icons != NULL && set_default_icon) + gtk_window_set_default_icon_list(icons); + *gtk_ret = libr_gtk_load_internal(handle, BUILDER_SECTION); + if(*gtk_ret == NULL) + goto failed; + if(icons_ret) + *icons_ret = icons; + ret = true; +failed: + if(!ret) + libr_close(handle); + return ret; +} + +/* + * Shared libglade resource loading + */ +GladeXML *libr_glade_load_internal(libr_file *handle, char *resource_name) +{ + GladeXML *glade = NULL; + size_t glade_length; + char *glade_data; + + /* Obtain the GLADE XML definition */ + glade_data = libr_malloc(handle, resource_name, &glade_length); + if(glade_data == NULL) + { + /* Failed to obtain embedded glade file */ + goto failed; + } + /* Initialize libglade appropriate for the available version */ + glade = libr_new_glade(handle, glade_data, glade_length); + if(glade == NULL) + { + /* Failed to initialize embedded glade file */ + goto failed; + } +failed: + free(glade_data); + return glade; +} + +/* + * Load the requested libglade resource and any applicable icons + */ +EXPORT_FN int libr_glade_load(GladeHandle **glade_ret, char *resource_name) +{ + libr_file *handle; + int ret = false; + + if(glade_ret == NULL) + { + /* Why on earth would you call this without obtaining the handle to the resource? */ + return false; + } + if(glade_init == NULL) + { + /* libglade was not linked with the application */ + return false; + } + /* Obtain the handle to the executable */ + if((handle = libr_open(NULL, LIBR_READ)) == NULL) + { + /* "Failed to open this executable (%s) for resources", progname() */ + return false; + } + register_internal_handle(handle); + *glade_ret = libr_glade_load_internal(handle, resource_name); + if(*glade_ret == NULL) + goto failed; + ret = true; +failed: + if(!ret) + libr_close(handle); + return ret; +} + +/* + * Automatically load the ".glade" resource and any applicable icons + */ +EXPORT_FN int libr_glade_autoload(GladeHandle **glade_ret, IconList **icons_ret, int set_default_icon) +{ + libr_file *handle = NULL; + GList *icons = NULL; + + if(glade_ret == NULL) + { + /* Why on earth would you call this without obtaining the handle to the resource? */ + return false; + } + if(glade_init == NULL) + { + /* libglade was not linked with the application */ + return false; + } + /* Obtain the handle to the executable */ + if((handle = libr_open(NULL, LIBR_READ)) == NULL) + { + /* "Failed to open this executable (%s) for resources", progname() */ + return false; + } + register_internal_handle(handle); + icons = libr_gtk_iconlist(handle); + /* Set the embedded icons as the default icon list (if requested) */ + if(icons != NULL && set_default_icon) + gtk_window_set_default_icon_list(icons); + /* Return the libglade and icon handles for the application */ + *glade_ret = libr_glade_load_internal(handle, GLADE_SECTION); + if(icons_ret) + *icons_ret = icons; + return true; +} diff --git a/src/libr-gtk.h b/src/libr-gtk.h new file mode 100644 index 0000000..fa6ba1b --- /dev/null +++ b/src/libr-gtk.h @@ -0,0 +1,55 @@ +/* + * + * Copyright (c) 2008 Erich Hoover + * + * libr-gtk - Convenience support for GTK+ + * + * This program 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.1 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * To provide feedback, report bugs, or otherwise contact me: + * ehoover at mines dot edu + * + */ + +#ifndef __LIBR_GTK_H +#define __LIBR_GTK_H + +#include "libr.h" + +#ifndef GLADE_H + typedef void GladeHandle; +#else + typedef GladeXML GladeHandle; +#endif +#ifndef __GTK_BUILDER_H__ + typedef void BuilderHandle; +#else + typedef GtkBuilder BuilderHandle; +#endif +#ifndef __G_LIB_H__ + typedef void IconList; +#else + typedef GList IconList; +#endif + +/* GTK Convenience API */ +IconList *libr_gtk_iconlist(libr_file *handle); +int libr_gtk_autoload(BuilderHandle **gtk_ret, IconList **icons_ret, int set_default_icon); +int libr_gtk_load(BuilderHandle **gtk_ret, char *resource_name); +int libr_glade_autoload(GladeHandle **glade_ret, IconList **icons_ret, int set_default_icon); +int libr_glade_load(GladeHandle **glade_ret, char *resource_name); + +#endif /* __LIBR_GTK_H */ + diff --git a/src/libr-i18n.c b/src/libr-i18n.c new file mode 100644 index 0000000..25e5664 --- /dev/null +++ b/src/libr-i18n.c @@ -0,0 +1,84 @@ +/* + * + * Copyright (c) 2009 Erich Hoover + * + * libr i18n - Add language resources into ELF binaries + * + * This program 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.1 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * To provide feedback, report bugs, or otherwise contact me: + * ehoover at mines dot edu + * + */ + +/* Include compile-time parameters */ +#include "config.h" + +#include "libr-i18n.h" +#include "tempfiles.h" + +/* Internationalization support */ +#include <locale.h> + +/* For string handling */ +#include <string.h> +#include <stdio.h> + +/* + * Extract the internationalization resources from the binary + * and setup gettext with the extracted folder. + */ +EXPORT_FN int libr_i18n_load(libr_file *handle, const char *domain) +{ + char *temp_folder; + int ret = true; + + temp_folder = libr_extract_resources(handle); + if(temp_folder == NULL) + return false; + if(!setlocale(LC_ALL, "")) + ret = false; + if(!bindtextdomain(domain, temp_folder)) + ret = false; + if(!textdomain(domain)) + ret = false; + if(!ret) + cleanup_folder(temp_folder); + else + register_folder_cleanup(temp_folder); + return ret; +} + +EXPORT_FN int libr_i18n_autoload(const char *domain) +{ + libr_file *handle; + + /* Obtain the handle to the executable */ + if((handle = libr_open(NULL, LIBR_READ)) == NULL) + { + /* "Failed to open this executable (%s) for resources", progname() */ + return false; + } + /* Obtain the language files from the ELF binary */ + if(!libr_i18n_load(handle, domain)) + { + /* "Failed to load language resources!" */ + goto failed; + } + +failed: + libr_close(handle); + return true; +} diff --git a/src/libr-i18n.h b/src/libr-i18n.h new file mode 100644 index 0000000..5b61546 --- /dev/null +++ b/src/libr-i18n.h @@ -0,0 +1,14 @@ +#ifndef __LIBR_I18N_H +#define __LIBR_I18N_H + +#include "libr.h" +#include "gettext.h" + +#define _(string) gettext(string) +/* for strings used in structures (must manually call gettext!): */ +#define N_(string) (string) + +int libr_i18n_autoload(const char *domain); +int libr_i18n_load(libr_file *handle, const char *domain); + +#endif /* __LIBR_I18N_H */ diff --git a/src/libr-icons.c b/src/libr-icons.c new file mode 100644 index 0000000..18dc536 --- /dev/null +++ b/src/libr-icons.c @@ -0,0 +1,643 @@ +/* + * + * Copyright (c) 2008-2011 Erich Hoover + * + * libr icons - Add icon resources into ELF binaries + * + * This program 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.1 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * To provide feedback, report bugs, or otherwise contact me: + * ehoover at mines dot edu + * + */ + +/* Include compile-time parameters */ +#include "config.h" + +#include "libr-icons.h" + +/* For "one canvas" SVG documents */ +#include "onecanvas.h" + +/* For string manipulation */ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +/* For handling files */ +#include <sys/stat.h> + +/* For C99 number types */ +#include <stdint.h> + +#define ICON_SECTION ".icon" +#define TERM_LEN 1 + +#define OFFSET_ENTRIES 0 +#define OFFSET_GUID OFFSET_ENTRIES+sizeof(uint32_t) + +#if defined(__i386) + #define ID12FORMAT "%012llx" +#elif defined(__x86_64) + #define ID12FORMAT "%012lx" +#else + #define ID12FORMAT "%012lx" + #warning "string formatting may be incorrect on this architecture." +#endif + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +typedef uint32_t ID8; +typedef uint16_t ID4; +typedef struct {uint64_t p:48;} __attribute__((__packed__)) ID12; + +typedef struct { + ID8 g1; + ID4 g2; + ID4 g3; + ID4 g4; + ID12 g5; +} __attribute__((__packed__)) UUID; + +typedef struct { + char *name; + size_t offset; + size_t entry_size; + libr_icontype_t type; + unsigned int icon_size; +} iconentry; + +typedef struct{ + size_t size; + char *buffer; + iconentry entry; +} iconlist; + +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + +/* + * Decode a UUID to its binary representation + * + * NOTE: The last 12-bit parameter cannot be obtained using (uint64_t *) with + * some versions of GCC using some optimization levels. This problem is very + * frustrating to debug, so I do not recommend playing with it yourself. + */ +UUID guid_decode(char *guid) +{ + UUID id = {0x00000000, 0x0000, 0x0000, 0x0000, {0x000000000000} }; + uint64_t tmp12; + + sscanf(guid, "%08x-%04hx-%04hx-%04hx-" ID12FORMAT, &id.g1, &id.g2, &id.g3, &id.g4, &tmp12); + id.g5.p = tmp12; + return id; +} + +/* + * Return the size of the file represented by the file stream + */ +off_t fsize(FILE *handle) +{ + struct stat file_stat; + + if(fstat(fileno(handle), &file_stat) == ERROR) + return ERROR; + return file_stat.st_size; +} + +/* + * Create a new icon handle + */ +libr_icon *new_icon_handle(libr_icontype_t type, unsigned int icon_size, char *buffer, size_t buffer_size) +{ + libr_icon *icon_handle = (libr_icon *) malloc(sizeof(libr_icon)); + + icon_handle->type = type; + icon_handle->buffer = buffer; + icon_handle->icon_size = icon_size; + icon_handle->buffer_size = buffer_size; + return icon_handle; +} + +/* + * Obtain an existing icon resource list + */ +int get_iconlist(libr_file *file_handle, iconlist *icons) +{ + if(icons == NULL) + { + /* Need to be able to return SOMETHING */ + return false; + } + /* Obtain the icon resource list */ + icons->buffer = libr_malloc(file_handle, ICON_SECTION, &(icons->size)); + if(icons->buffer == NULL) + return false; + return true; +} + +/* + * Get the next entry in an icon resource list + */ +iconentry *get_nexticon(iconlist *icons, iconentry *last_entry) +{ + size_t i; + + /* The icon list is needed both for the data buffer and for a call-specific iconentry instance */ + if(icons == NULL) + return NULL; + /* If this is the first call (last_entry == NULL) then return the first entry */ + if(last_entry == NULL) + icons->entry.offset = sizeof(uint32_t)+sizeof(UUID); + else + icons->entry.offset += icons->entry.entry_size; + /* Check to see if we've run out of entries */ + if(icons->entry.offset >= icons->size) + return NULL; + i = icons->entry.offset; + memcpy(&(icons->entry.entry_size), &(icons->buffer[i]), sizeof(uint32_t)); + i += sizeof(uint32_t); + icons->entry.type = icons->buffer[i]; + i += sizeof(unsigned char); + switch(icons->entry.type) + { + case LIBR_SVG: + icons->entry.icon_size = 0; + icons->entry.name = &(icons->buffer[i]); + break; + case LIBR_PNG: + memcpy(&(icons->entry.icon_size), &(icons->buffer[i]), sizeof(uint32_t)); + i += sizeof(uint32_t); + icons->entry.name = &(icons->buffer[i]); + break; + default: + /* Invalid entry type */ + return NULL; + } + return &(icons->entry); +} + +/* + * Free an icon handle + */ +EXPORT_FN int libr_icon_close(libr_icon *icon) +{ + if(icon == NULL) + return false; + if(icon->buffer == NULL) + return false; + free(icon->buffer); + free(icon); + return true; +} + +/* + * Read an icon resource from an ELF file by name + */ +EXPORT_FN libr_icon *libr_icon_geticon_byname(libr_file *handle, char *icon_name) +{ + iconentry *entry = NULL; + libr_icon *icon = NULL; + size_t buffer_size = 0; + unsigned int icon_size; + libr_icontype_t type; + char *buffer = NULL; + int inlist = false; + iconlist icons; + + if(!get_iconlist(handle, &icons)) + { + /* Failed to obtain a list of ELF icons */ + return NULL; + } + /* Look for the icon name in the entry list */ + while((entry = get_nexticon(&icons, entry)) != NULL) + { + if(!strcmp(entry->name, icon_name)) + { + type = entry->type; + icon_size = entry->icon_size; + inlist = true; + break; + } + } + if(!inlist) + { + /* Could not find icon name in the list of icons */ + return false; + } + /* Get the icon from the ELF binary */ + if(!libr_size(handle, icon_name, &buffer_size)) + { + /* Failed to obtain ELF icon size */ + return NULL; + } + /* Allocate memory for the icon */ + buffer = (char *) malloc(buffer_size); + if(buffer == NULL) + { + /* Failed to allocate memory for icon */ + return NULL; + } + /* Get the compressed icon from the ELF file */ + if(!libr_read(handle, icon_name, buffer)) + { + /* Failed to obtain ELF icon */ + goto geticon_byname_complete; + } + icon = new_icon_handle(type, icon_size, buffer, buffer_size); + +geticon_byname_complete: + if(icon == NULL) + free(buffer); + return icon; +} + +/* + * Read an icon resource from an ELF file by the square icon size + */ +EXPORT_FN libr_icon *libr_icon_geticon_bysize(libr_file *handle, unsigned int iconsize) +{ + unsigned int closest_id = 0, i = 0, j = 0; + int found_png = false, found_svg = false; + unsigned long closest_size = 0; + iconentry *entry = NULL; + iconlist icons; + + if(!get_iconlist(handle, &icons)) + { + /* Failed to obtain a list of ELF icons */ + return NULL; + } + /* Look for the closest size match, ignore SVG in case there are multiple icons */ + while((entry = get_nexticon(&icons, entry)) != NULL) + { + if(entry->type == LIBR_SVG) + found_svg = true; + if(entry->type == LIBR_PNG) + { + if(j == 0) + { + closest_size = entry->icon_size; + found_png = true; + } + if(abs(iconsize-entry->icon_size) < closest_size) + { + closest_size = entry->icon_size; + closest_id = i; + } + j++; + } + i++; + } + /* If any PNG files were found then use the file if: + * 1) There are no SVG files <OR> + * 2) The PNG is an EXACT size match + */ + if(found_png) + { + i=0; + entry = NULL; + while((entry = get_nexticon(&icons, entry)) != NULL) + { + if(i == closest_id) + { + if(entry->icon_size == iconsize || !found_svg) + return libr_icon_geticon_byname(handle, entry->name); + break; + } + i++; + } + } + /* Otherwise use the SVG (provided that there is one) */ + if(found_svg) + { + entry = NULL; + while((entry = get_nexticon(&icons, entry)) != NULL) + { + if(entry->type == LIBR_SVG) + { + libr_icon *icon = libr_icon_geticon_byname(handle, entry->name); + if (icon) { + libr_icon *icon_onecanvas; + char *buffer; + + /* should we report the requested size for SVG? */ + icon->icon_size = iconsize; + + /* if the SVG is a "one canvas" document then extract the correctly sized icon */ + if((buffer = onecanvas_geticon_bysize(icon->buffer, iconsize)) != NULL) + { + libr_icon_close(icon); + icon_onecanvas = new_icon_handle(LIBR_SVG, iconsize, buffer, strlen(buffer)); + return icon_onecanvas; + } + } + return icon; + } + } + } + /* Give up */ + return NULL; +} + +/* + * Obtains the icon UUID for the ELF file + */ +EXPORT_FN int libr_icon_getuuid(libr_file *handle, char *uuid) +{ + UUID id = {0x00000000, 0x0000, 0x0000, 0x0000, {0x000000000000} }; + iconlist icons; + + if(!get_iconlist(handle, &icons)) + { + /* Failed to obtain the list of ELF icons */ + return false; + } + /* Now store the GUID to the return string */ + memcpy(&id, &(icons.buffer[OFFSET_GUID]), sizeof(UUID)); + snprintf(uuid, GUIDSTR_LENGTH, "%08x-%04hx-%04hx-%04hx-" ID12FORMAT "\n", id.g1, id.g2, id.g3, id.g4, (uint64_t) id.g5.p); + free(icons.buffer); + return true; +} +EXPORT_FN int libr_icon_getguid(libr_file *handle, char *uuid) ALIAS_FN(libr_icon_getuuid); + +/* + * Allocate a buffer containing the data of an icon + */ +EXPORT_FN char *libr_icon_malloc(libr_icon *icon, size_t *size) +{ + char *iconfile = NULL; + + if(size == NULL) + { + /* No return size passed */ + return NULL; + } + if(!libr_icon_size(icon, size)) + { + /* Failed to obtain embedded icon file size */ + return NULL; + } + iconfile = (char *) malloc(*size); + if(!libr_icon_read(icon, iconfile)) + { + /* Failed to obtain embedded icon file */ + free(iconfile); + return NULL; + } + return iconfile; +} + +/* + * Create an icon resource to represent a file on the hard disk + */ +EXPORT_FN libr_icon *libr_icon_newicon_byfile(libr_icontype_t type, unsigned int icon_size, char *icon_file) +{ + libr_icon *icon_handle = NULL; + size_t len, buffer_size = 0; + char *buffer = NULL; + FILE *handle = NULL; + + /* Open a handle to the icon file */ + if((handle = fopen(icon_file, "r")) == NULL) + { + /* Failed to open icon file */ + return NULL; + } + /* Get the size of the icon file */ + if((buffer_size = fsize(handle)) == ERROR) + { + /* Failed to obtain the icon's file size */ + return NULL; + } + /* Allocate a buffer for the uncompressed icon */ + buffer = (char *) malloc(buffer_size); + if(buffer == NULL) + { + /* Failed to allocate a buffer for the icon data */ + return NULL; + } + /* Read the uncompressed image from the disk */ + if((len = fread(buffer, 1, buffer_size, handle)) <= 0) + { + /* Failed to read icon from disk */ + goto newicon_complete; + } + fclose(handle); + if(len != buffer_size) + { + /* Failed to read the entire icon */ + goto newicon_complete; + } + /* Allocate the icon handle */ + icon_handle = new_icon_handle(type, icon_size, buffer, buffer_size); + +newicon_complete: + if(icon_handle == NULL) + free(buffer); + return icon_handle; +} + +/* + * Copy the icon resource into a buffer + */ +EXPORT_FN int libr_icon_read(libr_icon *icon, char *buffer) +{ + if(icon == NULL) + return false; + memcpy(buffer, icon->buffer, icon->buffer_size); + return true; +} + +/* + * Get the memory size of an icon resource + */ +EXPORT_FN int libr_icon_size(libr_icon *icon, size_t *size) +{ + if(icon == NULL) + return false; + *size = icon->buffer_size; + return true; +} + +/* + * Save the icon resource to a file + */ +EXPORT_FN int libr_icon_save(libr_icon *icon, char *filename) +{ + FILE *file = NULL; + int ret = false; + size_t len; + + if(icon == NULL) + return false; + /* Open the file to store the image */ + if((file = fopen(filename, "w")) == NULL) + { + /* Failed to open file to write the icon */ + return false; + } + /* Store the uncompressed icon to disk */ + if((len = fwrite(icon->buffer, 1, icon->buffer_size, file)) <= 0) + { + /* Failed to write output file */ + goto saveicon_complete; + } + if(len != icon->buffer_size) + { + /* Did not write the entire file */ + goto saveicon_complete; + } + ret = true; + +saveicon_complete: + /* Close remaining resources */ + fclose(file); + return ret; +} + +/* + * Sets the icon GUID for the ELF file + */ +EXPORT_FN int libr_icon_setuuid(libr_file *handle, char *guid) +{ + int ret = false; + iconlist icons; + UUID id; + int i; + + /* First check the GUID string */ + for(i=0;i<strlen(guid);i++) + { + if(!isxdigit(guid[i])) + { + if(guid[i] == '-' && (i == 8 || i == 13 || i == 18 || i == 23)) + continue; + /* not a valid GUID string */ + return false; + } + } + id = guid_decode(guid); + /* Now check existing resources */ + if(!get_iconlist(handle, &icons)) + { + /* No icons exist in the file, create a new icon section with the GUID */ + uint32_t entries = 0; + + icons.size = sizeof(uint32_t)+sizeof(UUID); + icons.buffer = (char *) malloc(icons.size); + memcpy(&(icons.buffer[OFFSET_ENTRIES]), &entries, sizeof(uint32_t)); + } + /* Set the GUID and write the resource */ + if(!libr_write(handle, ICON_SECTION, icons.buffer, icons.size, LIBR_UNCOMPRESSED, LIBR_OVERWRITE)) + { + /* failed to write icon resource */ + goto setguid_complete; + } + ret = true; + +setguid_complete: + free(icons.buffer); + return ret; +} +EXPORT_FN int libr_icon_setguid(libr_file *handle, char *uuid) ALIAS_FN(libr_icon_setuuid); + +/* + * Add an icon resource to an ELF file + */ +EXPORT_FN int libr_icon_write(libr_file *handle, libr_icon *icon, char *icon_name, libr_overwrite_t overwrite) +{ + size_t entry_size, i; + iconentry *entry = NULL; + iconlist icons; + int ret = false; + + /* Check to make sure the user did not make a poor name choice */ + if(!strcmp(icon_name, ICON_SECTION)) + { + /* ".icon" is a reserved section name */ + return false; + + } + /* Check to make sure the file supports icon resources */ + if(!get_iconlist(handle, &icons)) + { + /* A GUID must be set first */ + return false; + } + /* First add the icon as a new named section */ + if(!libr_write(handle, icon_name, icon->buffer, icon->buffer_size, LIBR_COMPRESSED, overwrite)) + { + /* Failed to add the icon as a resource */ + goto writeicon_complete; + } + /* Look to see if the icon already has an entry */ + while((entry = get_nexticon(&icons, entry)) != NULL) + { + if(!strcmp(entry->name, icon_name)) + { + ret = true; + goto writeicon_complete; + } + } + /* Now add the icon to the list of icon resources in the ".icon" section */ + switch(icon->type) + { + case LIBR_SVG: + entry_size = sizeof(uint32_t)+sizeof(unsigned char)+strlen(icon_name)+TERM_LEN; + break; + case LIBR_PNG: + entry_size = sizeof(uint32_t)+sizeof(unsigned char)+sizeof(uint32_t)+strlen(icon_name)+TERM_LEN; + break; + default: + /* Unhandled icon type */ + goto writeicon_complete; + } + icons.buffer = (char *) realloc(icons.buffer, icons.size+entry_size); + if(icons.buffer == NULL) + { + /* Failed to expand memory size */ + goto writeicon_complete; + } + i = icons.size; + memcpy(&(icons.buffer[i]), &entry_size, sizeof(uint32_t)); + i+=sizeof(uint32_t); + icons.buffer[i] = icon->type; + i+=sizeof(unsigned char); + if(icon->type == LIBR_PNG) + { + memcpy(&(icons.buffer[i]), &icon->icon_size, sizeof(uint32_t)); + i+=sizeof(uint32_t); + } + memcpy(&(icons.buffer[i]), icon_name, strlen(icon_name)); + i+=strlen(icon_name); + icons.buffer[i] = '\0'; + icons.size += entry_size; + if(i != (icons.size-1)) + printf("Really dangerous, buffer size mismatch!\n"); + /* Write the updated icon table */ + if(!libr_write(handle, ICON_SECTION, icons.buffer, icons.size, LIBR_UNCOMPRESSED, LIBR_OVERWRITE)) + { + /* failed to write icon resource */ + goto writeicon_complete; + } + ret = true; + +writeicon_complete: + if(icons.buffer) + free(icons.buffer); + return ret; +} diff --git a/src/libr-icons.h b/src/libr-icons.h new file mode 100644 index 0000000..779fef3 --- /dev/null +++ b/src/libr-icons.h @@ -0,0 +1,201 @@ +/* + * + * Copyright (c) 2008-2010 Erich Hoover + * + * libr-icons - Handle icon resources in ELF binaries + * + * This program 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.1 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * To provide feedback, report bugs, or otherwise contact me: + * ehoover at mines dot edu + * + */ + +#ifndef __LIBR_ICONS_H +#define __LIBR_ICONS_H + +#include "libr.h" + +typedef enum { + LIBR_SVG = 0, + LIBR_PNG = 1 +} libr_icontype_t; + +#define UUIDSTR_LENGTH 37 +#define GUIDSTR_LENGTH UUIDSTR_LENGTH + +#ifdef __LIBR_BUILD__ + typedef struct { + char *buffer; + size_t buffer_size; + libr_icontype_t type; + unsigned int icon_size; + } libr_icon; +#else + typedef void libr_icon; +#endif + +/************************************************************************* + * libr Icon API + *************************************************************************/ + +/** + * @page libr_icon_close Release an icon resource handle. + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>int libr_icon_close(libr_icon *icon);</b> + * + * @section DESCRIPTION + * Release the icon resource allocated by a call to + * <b>libr_icon_geticon_byid</b>(3), <b>libr_icon_geticon_byname</b>(3), + * <b>libr_icon_geticon_bysize</b>(3), <b>libr_icon_newicon_byfile</b>(3), + * or <b>libr_icon_newicon_frombuffer</b>(3). + * + * @param icon The icon handle to release. + * @return Returns 1 on success, 0 on failure. + * + * @section SA SEE ALSO + * <b>libr_icon_geticon_byid</b>(3), <b>libr_icon_geticon_byname</b>(3), + * <b>libr_icon_geticon_bysize</b>(3), <b>libr_icon_newicon_byfile</b>(3), + * <b>libr_icon_newicon_frombuffer</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +int libr_icon_close(libr_icon *icon); + +/* +libr_icon *libr_icon_geticon_byid(libr_file *handle, unsigned int iconid); +*/ + +/** + * @page libr_icon_geticon_byname Retrieve an icon resource from an ELF + * binary (by the icon resource's name). + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>libr_icon *libr_icon_geticon_byname(libr_file *handle, char *iconname);</b> + * + * @section DESCRIPTION + * Return a resource handle to an icon stored in a libr-compatible ELF + * binary. When this handle is no-longer needed it must be unallocated + * using <b>libr_icon_close</b>(3). + * + * @param handle A handle returned by <b>libr_open</b>(3). + * @param iconname The exact name of the resource to return. + * @return Returns a handle on success, NULL on failure. + * + * @section SA SEE ALSO + * <b>libr_open</b>(3), <b>libr_icon_close</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +libr_icon *libr_icon_geticon_byname(libr_file *handle, char *iconname); + +/** + * @page libr_icon_geticon_bysize Retrieve an icon resource from an ELF + * binary (by the desired icon size). + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>libr_icon *libr_icon_geticon_bysize(libr_file *handle, unsigned int iconsize);</b> + * + * @section DESCRIPTION + * Return a resource handle to the closest requested size icon stored + * in a libr-compatible ELF binary. When this handle is no-longer + * needed it must be unallocated using <b>libr_icon_close</b>(3). + * + * @param handle A handle returned by <b>libr_open</b>(3). + * @param iconsize The size of the resource to return, use 0 + * to request an SVG icon. + * @return Returns a handle on success, NULL on failure. + * + * @section SA SEE ALSO + * <b>libr_open</b>(3), <b>libr_icon_close</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +libr_icon *libr_icon_geticon_bysize(libr_file *handle, unsigned int iconsize); + +/** + * @page libr_icon_getuuid Retrieve the UUID of an application. + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>int libr_icon_getuuid(libr_file *handle, char *uuid);</b> + * + * @section DESCRIPTION + * Returns the icon UUID corresponding to the ELF binary in hex notation + * (XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX), which requires a 37 character + * buffer (36 data characters and a NULL terminator). + * + * @param handle A handle returned by <b>libr_open</b>(3). + * @param uuid A buffer to store the UUID of the application. + * @return Returns 1 on success, 0 on failure. + * + * @section SA SEE ALSO + * <b>libr_open</b>(3), <b>libr_icon_close</b>(3), + * <b>libr_icon_setuuid</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +int libr_icon_getuuid(libr_file *handle, char *uuid); +DEPRECATED_FN int libr_icon_getguid(libr_file *handle, char *uuid); + +char *libr_icon_malloc(libr_icon *icon, size_t *size); +/* +libr_icon *libr_icon_newicon_frombuffer(libr_icontype_t type, int iconsize, char *buffer, size_t size); +*/ +libr_icon *libr_icon_newicon_byfile(libr_icontype_t type, unsigned int iconsize, char *iconfile); +/* +unsigned int libr_icon_num(libr_file *handle); +*/ +int libr_icon_read(libr_icon *icon, char *buffer); +int libr_icon_size(libr_icon *icon, size_t *size); +int libr_icon_save(libr_icon *icon, char *filename); + +/** + * @page libr_icon_setuuid Write a UUID into an application binary. + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>int libr_icon_setuuid(libr_file *handle, char *uuid);</b> + * + * @section DESCRIPTION + * Sets the icon UUID corresponding to the ELF binary in hex notation + * (XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX), which requires a 37 character + * buffer (36 data characters and a NULL terminator). + * + * @param handle A handle returned by <b>libr_open</b>(3). + * @param uuid A UUID to set for the application, can be generated by + * the terminal program "uuid". + * @return Returns 1 on success, 0 on failure. + * + * @section SA SEE ALSO + * <b>libr_open</b>(3), <b>libr_icon_close</b>(3), + * <b>libr_icon_getuuid</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +int libr_icon_setuuid(libr_file *handle, char *uuid); +DEPRECATED_FN int libr_icon_setguid(libr_file *handle, char *guid); +int libr_icon_write(libr_file *handle, libr_icon *icon, char *iconname, libr_overwrite_t overwrite); + +#endif /* __LIBR_ICONS_H */ diff --git a/src/libr-internal.h b/src/libr-internal.h new file mode 100644 index 0000000..f7008a4 --- /dev/null +++ b/src/libr-internal.h @@ -0,0 +1,34 @@ +#ifndef __LIBR_INTERNAL_H +#define __LIBR_INTERNAL_H + +#define false 0 +#define true 1 +#define ERROR -1 +#define EXPORT_FN __attribute__((visibility ("protected"))) +#define INTERNAL_FN __attribute__ ((visibility ("internal"))) +#define LIBR_TEMPFILE "/tmp/libr-temp.XXXXXX" +#define LIBR_TEMPFILE_LEN 22 + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +typedef struct { + char *message; + libr_status status; + const char *function; +} libr_intstatus; + +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + +struct _libr_file; + +void libr_set_error(libr_intstatus error); +libr_intstatus make_status(const char *function, libr_status code, char *message, ...); +/* Only called directly by cleanup routine, all other calls should be through libr_close */ +void libr_close_internal(struct _libr_file *file_handle); + +#define SET_ERROR(code,...) make_status(__FUNCTION__, code, __VA_ARGS__) +#define RETURN(code,...) return SET_ERROR(code, __VA_ARGS__) +#define RETURN_OK return SET_ERROR(LIBR_OK, NULL) +#define PUBLIC_RETURN(code,message) {SET_ERROR(code, message); return (code == LIBR_OK);} + +#endif /* __LIBR_INTERNAL_H */ diff --git a/src/libr-link.h b/src/libr-link.h new file mode 100644 index 0000000..b1bdb54 --- /dev/null +++ b/src/libr-link.h @@ -0,0 +1,26 @@ +#ifndef __LIBR_LINK_H +#define __LIBR_LINK_H + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +typedef struct { + void **symbol; + char *name; +} symbol_table; + +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + +#define SYMBOL_TABLE(tbl, ...) \ +const symbol_table tbl[] = { \ + __VA_ARGS__ \ + {NULL, NULL} \ +} + +#define OVERRIDE_SYMBOL(a) FN_##a +#define SYMBOL(sym) {(void **)&FN_##sym, #sym} +#define DEFINE_SYMBOL(ret, fn, ...) ret (*OVERRIDE_SYMBOL(fn))(__VA_ARGS__) +#define LOAD_SYMBOLS(lib, tbl) load_symbols(lib, tbl, sizeof(tbl)/sizeof(symbol_table)) + +int load_symbols(const char *library, const symbol_table *table, int entries); + +#endif /* __LIBR_LINK_H */ diff --git a/src/libr-ro.c b/src/libr-ro.c new file mode 100644 index 0000000..c3de28d --- /dev/null +++ b/src/libr-ro.c @@ -0,0 +1,351 @@ +/* + * + * Copyright (c) 2009 Erich Hoover + * Copyright (c) 2008-2009 Martin Rosenau + * + * libr read-only Backend - Read resources from ELF binaries + * + * This program 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.1 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * To provide feedback, report bugs, or otherwise contact me: + * ehoover at mines dot edu + * + */ + +/* Include compile-time parameters */ +#include "config.h" + +#include "libr.h" +#include "libr-internal.h" + +/* malloc/free */ +#include <stdlib.h> + +/* For memory byte-wise compare */ +#include <string.h> + +/* For endian conversion */ +#include "cvtendian.h" + +#define RETURN_UNSUPPORTED RETURN(LIBR_ERROR_UNSUPPORTED, "The read-only backend does not support this operation"); + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +typedef struct { + unsigned char magic[4]; + eClass byte_size; + eEncoding endian; + unsigned char version; + unsigned char padding[9]; +} ElfPreHeader; + +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + +#define ELF_HALF(b) sizeof(uint16_t) +#define ELF_WORD(b) sizeof(uint32_t) +#define ELF_XWORD(b) ((b == ELFCLASS32) ? sizeof(uint32_t) : sizeof(uint64_t)) +#define ELF_ADDR(b) ELF_XWORD(b) +#define ELF_OFF(b) ELF_XWORD(b) + +/* ELF Header Offsets */ +#define HDROFF_TYPE(b) sizeof(ElfPreHeader) /* ElfXX_Half e_type; */ +#define HDROFF_MACHINE(b) HDROFF_TYPE(b)+ELF_HALF(b) /* ElfXX_Half e_machine; */ +#define HDROFF_VERSION(b) HDROFF_MACHINE(b)+ELF_HALF(b) /* ElfXX_Word e_version; */ +#define HDROFF_ENTRY(b) HDROFF_VERSION(b)+ELF_WORD(b) /* ElfXX_Addr e_entry; */ +#define HDROFF_PHOFF(b) HDROFF_ENTRY(b)+ELF_ADDR(b) /* ElfXX_Off e_phoff; */ +#define HDROFF_SHOFF(b) HDROFF_PHOFF(b)+ELF_OFF(b) /* ElfXX_Off e_shoff; */ +#define HDROFF_FLAGS(b) HDROFF_SHOFF(b)+ELF_OFF(b) /* ElfXX_Word e_flags; */ +#define HDROFF_EHSIZE(b) HDROFF_FLAGS(b)+ELF_WORD(b) /* ElfXX_Half e_ehsize; */ +#define HDROFF_PHENTSIZE(b) HDROFF_EHSIZE(b)+ELF_HALF(b) /* ElfXX_Half e_phentsize; */ +#define HDROFF_PHNUM(b) HDROFF_PHENTSIZE(b)+ELF_HALF(b) /* ElfXX_Half e_phnum; */ +#define HDROFF_SHENTSIZE(b) HDROFF_PHNUM(b)+ELF_HALF(b) /* ElfXX_Half e_shentsize; */ +#define HDROFF_SHNUM(b) HDROFF_SHENTSIZE(b)+ELF_HALF(b) /* ElfXX_Half e_shnum; */ +#define HDROFF_SHSTRNDX(b) HDROFF_SHNUM(b)+ELF_HALF(b) /* ElfXX_Half e_shstrndx; */ + +/* ELF Section Offsets */ +#define SECOFF_NAME(b) 0 /* ElfXX_Word sh_name; */ +#define SECOFF_TYPE(b) SECOFF_NAME(b)+ELF_WORD(b) /* ElfXX_Word sh_type; */ +#define SECOFF_FLAGS(b) SECOFF_TYPE(b)+ELF_WORD(b) /* ElfXX_XWord sh_flags; */ +#define SECOFF_ADDR(b) SECOFF_FLAGS(b)+ELF_XWORD(b) /* ElfXX_Addr sh_addr; */ +#define SECOFF_OFFSET(b) SECOFF_ADDR(b)+ELF_ADDR(b) /* ElfXX_Off sh_offset; */ +#define SECOFF_SIZE(b) SECOFF_OFFSET(b)+ELF_OFF(b) /* ElfXX_XWord sh_size; */ +#define SECOFF_LINK(b) SECOFF_SIZE(b)+ELF_XWORD(b) /* ElfXX_Word sh_link; */ +#define SECOFF_INFO(b) SECOFF_LINK(b)+ELF_WORD(b) /* ElfXX_Word sh_info; */ +#define SECOFF_ADDRALIGN SECOFF_INFO(b)+ELF_WORD(b) /* ElfXX_XWord sh_addralign; */ +#define SECOFF_ENTSIZE SECOFF_ADDRALIGN(b)+ELF_XWORD(b) /* ElfXX_XWord sh_entsize; */ + +/* + * Safely read a parameter from the ELF binary + */ +static int read_param(FILE *handle, void *result, size_t bytes, eClass endian) +{ + if(fread(result, 1, bytes, handle) != bytes) + return 0; + if(ferror(handle)) + return 0; + if(endian != HOST_ENDIAN && !ConvertEndian(result, bytes)) + return 0; + return 1; +} + +/* + * The read-only backend requires no initialization + */ +void initialize_backend(void) +{ + if(sizeof(ElfPreHeader) != 16) + fprintf(stderr, "WARNING: Your compiler did not properly pack important structures!\n"); +} + +/* + * The read-only backend cannot write an output file + */ +void write_output(libr_file *file_handle) {} + +/* + * The read-only backend cannot add sections + */ +libr_intstatus add_section(libr_file *file_handle, char *resource_name, libr_section **retscn) +{ + RETURN_UNSUPPORTED; +} + +/* + * Return the name of a section + */ +char *section_name(libr_file *file_handle, libr_section *scn) +{ + if(scn == NULL) + return NULL; + return scn->name; +} + +/* + * Return the pointer to the actual data in the section + */ +void *data_pointer(libr_section *scn, libr_data *data) +{ + return (void *) data; +} + +/* + * Return the size of the data in the section + */ +size_t data_size(libr_section *scn, libr_data *data) +{ + return scn->size; +} + +/* + * Find the resource stored in the ELF binary + */ +libr_intstatus find_section(libr_file *file_handle, char *section, libr_section **retscn) +{ + char *test_name; + int i; + + for(i=0; i<file_handle->total_sections; i++) + { + test_name = section_name(file_handle, &(file_handle->secdata[i])); + if(test_name != NULL && strcmp(test_name, section) == 0) + break; + } + if(i >= file_handle->total_sections) + RETURN(LIBR_ERROR_NOSECTION, "ELF resource section not found"); + + /* Found the resource, hurray! */ + *retscn = &(file_handle->secdata[i]); + RETURN_OK; +} + +/* + * Read the section from the ELF binary + */ +libr_data *get_data(libr_file *file_handle, libr_section *scn) +{ + FILE *handle = file_handle->handle; + libr_data *data = NULL; + size_t n; + + fseek(handle, scn->data_offset, SEEK_SET); + data = (libr_data *) malloc(scn->size); + n = fread(data, 1, scn->size, handle); + if(n == 0) + goto failed; /* Empty section? */ + if(ferror(handle)) + goto failed; + + /* Succeeded in reading the data */ + return data; +failed: + free(data); + return NULL; +} + +/* + * UNSUPORTED BY BACKEND: Create a new data section + */ +libr_data *new_data(libr_file *file_handle, libr_section *scn) +{ + return NULL; +} + +/* + * Find the next section given a section pointer + */ +libr_section *next_section(libr_file *file_handle, libr_section *scn) +{ + int total_sections = file_handle->total_sections; + libr_section *test_scn = NULL; + int i; + + if(total_sections == 0) + return NULL; + /* Requesting the first section */ + if(scn == NULL) + { + i = 0; + /* Do not return an empty section */ + while(test_scn == NULL || test_scn->size == 0) + { + if(i > total_sections) + return NULL; + test_scn = &(file_handle->secdata[i++]); + } + return test_scn; + } + /* Return the next section given a section pointer */ + for(i=0; i<total_sections; i++) + { + test_scn = &(file_handle->secdata[i]); + + if(test_scn == scn && (i+1) < total_sections) + { + libr_section *next_scn = &(file_handle->secdata[i+1]); + + /* Returning empty sections is pointless */ + if(next_scn->size != 0) + return next_scn; + } + } + return NULL; +} + +/* + * UNSUPORTED BY BACKEND: Remove a section + */ +libr_intstatus remove_section(libr_file *file_handle, libr_section *scn) +{ + RETURN_UNSUPPORTED; +} + +/* + * UNSUPORTED BY BACKEND: Set the data for a section + */ +libr_intstatus set_data(libr_file *file_handle, libr_section *scn, libr_data *data, off_t offset, char *buffer, size_t size) +{ + RETURN_UNSUPPORTED; +} + +/* + * Open a handle to the ELF binary (provided that read-only access is requested) + */ +libr_intstatus open_handles(libr_file *file_handle, char *filename, libr_access_t access) +{ + const char elf_magic[] = {'\x7F','E','L','F'}; + uint16_t total_sections, sh_size, strings_sec; + ElfPreHeader file_info; + libr_section *secdata; + FILE *handle = NULL; + uint64_t sh_offset; + unsigned long i; + + if(access == LIBR_READ_WRITE) + RETURN_UNSUPPORTED; + handle = fopen(filename, "rb"); + if(!handle) + RETURN(LIBR_ERROR_OPENFAILED, "Failed to open input file"); + if(fread(&file_info, 1, sizeof(ElfPreHeader), handle) != sizeof(ElfPreHeader)) + RETURN(LIBR_ERROR_WRONGFORMAT, "Failed to read pre-header bytes from input file"); + if(memcmp(file_info.magic, elf_magic, sizeof(elf_magic)) != 0) + RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: not an ELF binary"); + + /* Confirm processor (byte size) and packing (endian) */ + if(!enum_valid(file_info.byte_size, ELFCLASS)) + RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: invalid byte size"); + if(!enum_valid(file_info.endian, ELFDATA)) + RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: invalid endian type"); + + /* Get the file offset to the Section Header tables */ + fseek(handle, HDROFF_SHOFF(file_info.byte_size), SEEK_SET); + if(!read_param(handle, &sh_offset, ELF_OFF(file_info.byte_size), file_info.endian)) + RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read section header offset"); + /* Get the size of the Section Header tables */ + fseek(handle, HDROFF_SHENTSIZE(file_info.byte_size), SEEK_SET); + if(!read_param(handle, &sh_size, ELF_HALF(file_info.byte_size), file_info.endian)) + RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read section header size"); + /* Get the total number of sections */ + fseek(handle, HDROFF_SHNUM(file_info.byte_size), SEEK_SET); + if(!read_param(handle, &total_sections, ELF_HALF(file_info.byte_size), file_info.endian)) + RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read total number of sections"); + /* Get the ID of the "strings" section */ + fseek(handle, HDROFF_SHSTRNDX(file_info.byte_size), SEEK_SET); + if(!read_param(handle, &strings_sec, ELF_HALF(file_info.byte_size), file_info.endian)) + RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read string section ID"); + if(strings_sec >= total_sections) + RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: invalid string section ID"); + secdata = (libr_section *) malloc(sizeof(libr_section)*total_sections); + + /* Load section information */ + for(i=0; i<total_sections; i++) + { + long sec_start = sh_offset+sh_size*i; + + /* Grab the offset in the string table to the name of the section */ + fseek(handle, sec_start+SECOFF_NAME(file_info.byte_size), SEEK_SET); + if(!read_param(handle, &(secdata[i].name_offset), ELF_WORD(file_info.byte_size), file_info.endian)) + RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read section name offset"); + /* Grab the offset to the data for the section */ + fseek(handle, sec_start+SECOFF_OFFSET(file_info.byte_size), SEEK_SET); + if(!read_param(handle, &(secdata[i].data_offset), ELF_OFF(file_info.byte_size), file_info.endian)) + RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read section data offset"); + /* Grab the size of the data for the section */ + fseek(handle, sec_start+SECOFF_SIZE(file_info.byte_size), SEEK_SET); + if(!read_param(handle, &(secdata[i].size), ELF_XWORD(file_info.byte_size), file_info.endian)) + RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read section size"); + } + /* Locate the name offset within the "strings" section and load the string */ + for(i=0; i<total_sections; i++) + { + long stringsec_start = secdata[strings_sec].data_offset; + size_t n; + + fseek(handle, stringsec_start+secdata[i].name_offset, SEEK_SET); + n = fread(secdata[i].name, 1, ELFSTRING_MAX-1, handle); + if(ferror(handle)) + RETURN(LIBR_ERROR_WRONGFORMAT, "Invalid input file format: failed to read string"); + secdata[i].name[n] = '\0'; + } + + /* Hold onto the important parameters */ + file_handle->secdata = secdata; + file_handle->total_sections = total_sections; + file_handle->endian = file_info.endian; + file_handle->byte_size = file_info.byte_size; + file_handle->handle = handle; + file_handle->filename = filename; + file_handle->access = access; + RETURN_OK; +} diff --git a/src/libr-ro.h b/src/libr-ro.h new file mode 100644 index 0000000..8b8e41a --- /dev/null +++ b/src/libr-ro.h @@ -0,0 +1,62 @@ +#ifndef __LIBRRO_H +#define __LIBRRO_H + +/* For file handle support */ +#include <stdio.h> + +/* For integer types with set bit-sizes */ +#include <stdint.h> + +/* + * NOTE: Packing the enum uses the smallest number of bytes + * possible to represent the value. This packing does not + * guarantee that a "short enum" will be 8 bits, however, + * for the small enumerations in the ELF specification this + * IS the case (no enum requires more than 8 bits). + */ +#define SHORT_ENUM __attribute__ ((__packed__)) + +/* Type of byte-packing (endian) */ +typedef enum SHORT_ENUM { + ELFDATANONE, /* Invalid */ + ELFDATA2LSB, /* Least Significant Byte First */ + ELFDATA2MSB, /* Most Significant Byte First */ + ELFDATAMAX, /* Invalid */ +} eEncoding; + +/* Type of target processor */ +typedef enum SHORT_ENUM { + ELFCLASSNONE, /* Invalid */ + ELFCLASS32, /* 32-bit Field Alignment */ + ELFCLASS64, /* 64-bit Field Alignment */ + ELFCLASSMAX, /* Invalid */ +} eClass; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +#define ELFSTRING_MAX 200 +typedef struct _libr_section { + uint64_t size; + uint64_t data_offset; + uint32_t name_offset; + char name[ELFSTRING_MAX]; +} libr_section; + +typedef struct _libr_file { + FILE *handle; + char *filename; + eEncoding endian; + eClass byte_size; + libr_access_t access; + libr_section *secdata; + unsigned long total_sections; +} libr_file; + +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + +/* for a clean internal API */ +typedef void libr_data; + +#define enum_valid(val, name) (val > name##NONE && val < name##MAX) + +#endif /* __LIBRRO_H */ diff --git a/src/libr.c b/src/libr.c new file mode 100644 index 0000000..d038594 --- /dev/null +++ b/src/libr.c @@ -0,0 +1,489 @@ +/* + * + * Copyright (c) 2008-2009 Erich Hoover + * + * libr - Add resources into ELF binaries + * + * This program 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.1 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * To provide feedback, report bugs, or otherwise contact me: + * ehoover at mines dot edu + * + */ + +/* Include compile-time parameters */ +#include "config.h" + +#include "libr.h" +#include "tempfiles.h" + +/* Obtain file information */ +#include <sys/stat.h> +#include <stdlib.h> +#include <stdio.h> + +/* Compress files */ +#include <zlib.h> +#include <math.h> /* for ceil */ + +/* Handle strings and variable arguments*/ +#include <string.h> +#include <stdarg.h> + +/* For C99 number types */ +#include <stdint.h> + +/* Handle status codes for multiple threads */ +#include <pthread.h> + +#define SPEC_VERSION '1' +#define OFFSET_TYPE ((unsigned long) 4) +#define OFFSET_UNCOMPRESSED ((unsigned long) OFFSET_TYPE+sizeof(unsigned char)) +#define OFFSET_UNCOMPRESSED_SIZE ((unsigned long) OFFSET_TYPE+sizeof(unsigned char)) +#define OFFSET_COMPRESSED ((unsigned long) OFFSET_UNCOMPRESSED_SIZE+sizeof(uint32_t)) + +#if 0 + extern const char * __progname_full; + #define progpath() (char *) __progname_full +#endif +#define getself() ((char *) "/proc/self/exe") + +pthread_key_t error_key; + +/* + * Free the error status code/message structure + * (called on thread destruction or when a new code is set) + */ +void free_error_key(void *_m) +{ + libr_intstatus *error = (libr_intstatus *) _m; + + if(error != NULL) + { + /* Free the error structure */ + if(error->message != NULL) + free(error->message); + free(error); + } +} + +/* + * Set the error code and message for retrieval + */ +void libr_set_error(libr_intstatus error) +{ + static int thread_key_initialized = false; + libr_intstatus *status = NULL; + + if(!thread_key_initialized) + { + if(pthread_key_create(&error_key, free_error_key) != 0) + return; /* a serious pthread-related error occurred */ + if(pthread_setspecific(error_key, NULL) != 0) + return; /* a serious pthread-related error occurred */ + thread_key_initialized = true; + } + free_error_key(pthread_getspecific(error_key)); + status = (libr_intstatus *) malloc(sizeof(libr_intstatus)); + memcpy(status, &error, sizeof(libr_intstatus)); + if(pthread_setspecific(error_key, (void *) status) != 0) + return; /* a serious pthread-related error occurred */ +} + +/* + * Make an internal status passing structure, set the error code with this status + * if the status is not LIBR_OK. + */ +libr_intstatus make_status(const char *function, libr_status code, char *message, ...) +{ + libr_intstatus status = {NULL, code, function}; + va_list args; + + if(message != NULL) + { + status.message = (char *) malloc(1024); + va_start(args, message); + vsnprintf(status.message, 1024, message, args); + va_end(args); + } + + libr_set_error(status); + return status; +} + +/* + * Make sure that the section is libr-compatible + */ +libr_intstatus section_ok(libr_section *scn, libr_data *data) +{ + char required_header[5], test_header[4] = {'R', 'E', 'S', SPEC_VERSION}; + void *ptr = data_pointer(scn, data); + size_t size = data_size(scn, data); + + if(ptr == NULL || size < sizeof(required_header)) + RETURN(LIBR_ERROR_NOTRESOURCE, "Not a valid libr-resource"); + memcpy(required_header, ptr, sizeof(required_header)); + if(strncmp(required_header, test_header, sizeof(test_header)) != 0) + RETURN(LIBR_ERROR_NOTRESOURCE, "Not a valid libr-resource"); + RETURN_OK; +} + +/* + * Remove a resourcefrom the ELF binary handle + */ +EXPORT_FN int libr_clear(libr_file *file_handle, char *resource_name) +{ + libr_data *data = NULL; + libr_section *scn = NULL; + + /* Ensure valid inputs */ + if(file_handle == NULL || resource_name == NULL) + PUBLIC_RETURN(LIBR_ERROR_INVALIDPARAMS, "Invalid parameters passed to function"); + if(file_handle->access != LIBR_READ_WRITE) + PUBLIC_RETURN(LIBR_ERROR_NOPERM, "Open handle with LIBR_READ_WRITE access"); + /* Find the section containing the icon */ + if(find_section(file_handle, resource_name, &scn).status != LIBR_OK) + return false; /* error already set */ + /* Get the section data (interested in header) */ + if((data = get_data(file_handle, scn)) == NULL) + PUBLIC_RETURN(LIBR_ERROR_GETDATA, "Failed to obtain data of section"); + /* Confirm that this resource is libr-compatible */ + if(section_ok(scn, data).status != LIBR_OK) + return false; /* error already set */ + /* Clear the data resource */ + if(set_data(file_handle, scn, data, 0, NULL, 0).status != LIBR_OK) + return false; /* error already set */ + /* Remove the section */ + if(remove_section(file_handle, scn).status != LIBR_OK) + return false; /* error already set */ + return true; +} + +/* + * Close the specified ELF binary handle + */ +EXPORT_FN void libr_close(libr_file *file_handle) +{ + unregister_handle_cleanup(file_handle); + libr_close_internal(file_handle); +} +/* Only called directly by cleanup routine, all other calls should be through libr_close */ +void libr_close_internal(libr_file *file_handle) +{ + write_output(file_handle); + free(file_handle); +} + +/* + * Return the last error message for the active thread + */ +EXPORT_FN char *libr_errmsg(void) +{ + libr_intstatus *error = (libr_intstatus *) pthread_getspecific(error_key); + + if(error == NULL) + return NULL; + return error->message; +} + +/* + * Return the last error code for the active thread (or LIBR_OK for no error) + */ +EXPORT_FN libr_status libr_errno(void) +{ + libr_intstatus *error = (libr_intstatus *) pthread_getspecific(error_key); + + if(error == NULL) /* Nothing has happened yet */ + return LIBR_OK; + return error->status; +} + +/* + * Return the name of a libr-compatible resource + */ +EXPORT_FN char *libr_list(libr_file *file_handle, unsigned int resourceid) +{ + libr_section *scn = NULL; + libr_data *data = NULL; + int i = 0; + + while((scn = next_section(file_handle, scn)) != NULL) + { + /* Get the section data (interested in header) */ + if((data = get_data(file_handle, scn)) == NULL) + return NULL; + if(section_ok(scn, data).status == LIBR_OK) + { + if(i == resourceid) + return strdup(section_name(file_handle, scn)); + i++; + } + } + return NULL; +} + +/* + * Allocate a buffer containing the data of a resource + */ +EXPORT_FN char *libr_malloc(libr_file *file_handle, char *resource_name, size_t *size) +{ + char *buffer = NULL; + size_t size_local; + + if(size == NULL) + size = &size_local; + if(!libr_size(file_handle, resource_name, size)) + return NULL; /* error already set */ + buffer = (char *) malloc(*size); + if(!libr_read(file_handle, resource_name, buffer)) + { + free(buffer); + return NULL; /* error already set */ + } + return buffer; +} + +/* + * Open the specified ELF binary (caller if filename is NULL) + */ +EXPORT_FN libr_file *libr_open(char *filename, libr_access_t access) +{ + libr_file *file_handle = NULL; + static int initialized = false; + + if(!initialized) + { + if(strncmp(zlibVersion(), ZLIB_VERSION, 1) != 0) + { + SET_ERROR(LIBR_ERROR_ZLIBINIT, "zlib library initialization failed"); + return NULL; + } + initialize_backend(); + initialized = true; + } + + if(filename == NULL) + filename = getself(); + file_handle = (libr_file *) malloc(sizeof(libr_file)); + memset(file_handle, 0, sizeof(libr_file)); + if(open_handles(file_handle, filename, access).status != LIBR_OK) + { + /* failed to open file for processing, error already set */ + free(file_handle); + file_handle = NULL; + } + /* Cleanup handles automatically when libr exits memory */ + if(file_handle != NULL) + register_handle_cleanup(file_handle); + return file_handle; +} + +/* + * Read a resource from the specified ELF binary handle + */ +EXPORT_FN int libr_read(libr_file *file_handle, char *resource_name, char *buffer) +{ + unsigned long uncompressed_size = 0, compressed_size = 0; + char *data_buffer = NULL; + libr_section *scn = NULL; + libr_data *data = NULL; + libr_type_t type; + + /* Find the section containing the icon */ + if(find_section(file_handle, resource_name, &scn).status != LIBR_OK) + return false; /* error already set */ + /* Get the section data (interested in header) */ + if((data = get_data(file_handle, scn)) == NULL) + PUBLIC_RETURN(LIBR_ERROR_GETDATA, "Failed to obtain data of section"); + /* Confirm that this resource is libr-compatible */ + if(section_ok(scn, data).status != LIBR_OK) + return false; /* error already set */ + data_buffer = (char *) data_pointer(scn, data); + /* Get the size of the data resource */ + type = (libr_type_t) data_buffer[OFFSET_TYPE]; + switch(type) + { + case LIBR_UNCOMPRESSED: + { if(data_size(scn, data)-OFFSET_UNCOMPRESSED < 0) + PUBLIC_RETURN(LIBR_ERROR_SIZEMISMATCH, "Section's data size does not make sense"); + uncompressed_size = data_size(scn, data)-OFFSET_UNCOMPRESSED; + memcpy(buffer, &data_buffer[OFFSET_UNCOMPRESSED], uncompressed_size); + } break; + case LIBR_COMPRESSED: + { + uint32_t size_temp; + + memcpy(&size_temp, &data_buffer[OFFSET_UNCOMPRESSED_SIZE], sizeof(uint32_t)); + uncompressed_size = size_temp; + compressed_size = data_size(scn, data)-OFFSET_COMPRESSED; + if(uncompress((unsigned char *)buffer, &uncompressed_size, (unsigned char *)&data_buffer[OFFSET_COMPRESSED], compressed_size) != Z_OK) + PUBLIC_RETURN(LIBR_ERROR_UNCOMPRESS, "Failed to uncompress resource data"); + } break; + default: + PUBLIC_RETURN(LIBR_ERROR_INVALIDTYPE, "Invalid data storage type specified"); + } + return true; +} + +/* + * Retrieve the number of libr-compatible resources + */ +EXPORT_FN unsigned int libr_resources(libr_file *file_handle) +{ + libr_section *scn = NULL; + libr_data *data = NULL; + int i = 0; + + while((scn = next_section(file_handle, scn)) != NULL) + { + if((data = get_data(file_handle, scn)) == NULL) + continue; + if(section_ok(scn, data).status == LIBR_OK) + i++; + } + return i; +} + +/* + * Get the size of a resource from the specified ELF binary handle + */ +EXPORT_FN int libr_size(libr_file *file_handle, char *resource_name, size_t *retsize) +{ + char *data_buffer = NULL; + libr_section *scn = NULL; + libr_data *data = NULL; + unsigned long size = 0; + libr_type_t type; + + /* Find the section containing the icon */ + if(find_section(file_handle, resource_name, &scn).status != LIBR_OK) + return false; /* error already set */ + /* Get the section data (interested in header) */ + if((data = get_data(file_handle, scn)) == NULL) + PUBLIC_RETURN(LIBR_ERROR_GETDATA, "Failed to obtain data of section"); + /* Confirm that this resource is libr-compatible */ + if(section_ok(scn, data).status != LIBR_OK) + return false; /* error already set */ + data_buffer = (char *) data_pointer(scn, data); + /* Get the size of the data resource */ + type = (libr_type_t) data_buffer[OFFSET_TYPE]; + switch(type) + { + case LIBR_UNCOMPRESSED: + { + size_t full_size = data_size(scn, data); + + if(full_size-OFFSET_UNCOMPRESSED < 0) + PUBLIC_RETURN(LIBR_ERROR_SIZEMISMATCH, "Section's data size does not make sense"); + size = full_size - OFFSET_UNCOMPRESSED; + } break; + case LIBR_COMPRESSED: + { + memcpy(&size, &data_buffer[OFFSET_UNCOMPRESSED_SIZE], sizeof(uint32_t)); + } break; + default: + PUBLIC_RETURN(LIBR_ERROR_INVALIDTYPE, "Invalid data storage type specified"); + } + *retsize = size; + return true; +} + +/* + * Write a resource to the specified ELF binary handle + */ +EXPORT_FN int libr_write(libr_file *file_handle, char *resource_name, char *buffer, size_t size, libr_type_t type, libr_overwrite_t overwrite) +{ + char header[9] = {'R', 'E', 'S', SPEC_VERSION}; + unsigned int header_size = 4; + libr_section *scn = NULL; + libr_data *data = NULL; + libr_intstatus ret; + + /* Ensure valid inputs */ + if(file_handle == NULL || resource_name == NULL || buffer == NULL) + PUBLIC_RETURN(LIBR_ERROR_INVALIDPARAMS, "Invalid parameters passed to function"); + if(file_handle->access != LIBR_READ_WRITE) + PUBLIC_RETURN(LIBR_ERROR_NOPERM, "Open handle with LIBR_READ_WRITE access"); + /* Get the section if it already exists */ + ret = find_section(file_handle, resource_name, &scn); + if(ret.status == LIBR_OK) + { + /* If the section exists (and overwrite is not specified) then fail */ + if(!overwrite) + PUBLIC_RETURN(LIBR_ERROR_OVERWRITE, "Section already exists, over-write not specified"); + /* Grab the existing data section for overwriting */ + if((data = get_data(file_handle, scn)) == NULL) + PUBLIC_RETURN(LIBR_ERROR_GETDATA, "Failed to obtain data of section"); + } + else if(ret.status == LIBR_ERROR_NOSECTION) + { + /* Create a new section named "resource_name" */ + if(add_section(file_handle, resource_name, &scn).status != LIBR_OK) + return false; /* error already set */ + /* Create a data segment to store the compressed image */ + if((data = new_data(file_handle, scn)) == NULL) + PUBLIC_RETURN(LIBR_ERROR_NEWDATA, "Failed to create data for section"); + } + else + return false; /* error already set */ + + header[header_size++] = (char) type; + switch(type) + { + case LIBR_UNCOMPRESSED: + /* Do nothing, just stick the data in */ + break; + case LIBR_COMPRESSED: + { + char *compressed_buffer = NULL, *uncompressed_buffer = buffer; + unsigned long compressed_size = 0, uncompressed_size = size; + uint32_t size_temp; + + /* Store the uncompressed size to the header */ + size_temp = uncompressed_size; + memcpy(&header[header_size], &size_temp, sizeof(uint32_t)); + header_size += sizeof(uint32_t); + /* Compress the data for storage */ + compressed_size = ceil((uncompressed_size+12)*1.1); + compressed_buffer = (char *) malloc(compressed_size); + if(compress((unsigned char *)compressed_buffer, &compressed_size, (unsigned char *)uncompressed_buffer, uncompressed_size) != Z_OK) + { + free(compressed_buffer); + PUBLIC_RETURN(LIBR_ERROR_COMPRESS, "Failed to compress resource data"); + } + /* From here on treat the compressed buffer as the data */ + buffer = compressed_buffer; + size = compressed_size; + } break; + default: + PUBLIC_RETURN(LIBR_ERROR_INVALIDTYPE, "Invalid data storage type specified"); + } + /* Store the resource header data */ + if(set_data(file_handle, scn, data, 0, &header[0], header_size).status != LIBR_OK) + return false; /* error already set */ + /* Create a data segment to store the post-header data + * NOTE: For existing files the data of the section is represented as a continuous stream + * (so calling elf_getdata now WILL NOT return the post-header data) + */ + if((data = new_data(file_handle, scn)) == NULL) + PUBLIC_RETURN(LIBR_ERROR_NEWDATA, "Failed to create data for section"); + /* Store the actual user data to the section */ + if(set_data(file_handle, scn, data, header_size, buffer, size).status != LIBR_OK) + return false; /* error already set */ + /* Close compression resources */ + if(type == LIBR_COMPRESSED) + free(buffer); + return true; +} diff --git a/src/libr.h b/src/libr.h new file mode 100644 index 0000000..b1aa1d7 --- /dev/null +++ b/src/libr.h @@ -0,0 +1,416 @@ +/* + * + * Copyright (c) 2008-2011 Erich Hoover + * + * libr - Add resources into ELF binaries + * + * This program 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.1 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * To provide feedback, report bugs, or otherwise contact me: + * ehoover at mines dot edu + * + */ + +#ifndef __LIBR_H +#define __LIBR_H + +#include <sys/types.h> + +#define DEPRECATED_FN __attribute__ ((deprecated)) +#define ALIAS_FN(fn) __attribute__ ((weak, alias (#fn))) + +/** + * @addtogroup libr_status libr_status + * @brief Enumeration of possible libr status values. + * @{ + * \#include <libr.h> + */ +/** Possible libr status values */ +typedef enum { + LIBR_OK = 0, /**< Success */ + LIBR_ERROR_GETEHDR = -1, /**< Failed to obtain ELF header: */ + LIBR_ERROR_NOTABLE = -2, /**< No ELF string table */ + LIBR_ERROR_TABLE = -3, /**< Failed to open string table: */ + LIBR_ERROR_GETDATA = -4, /**< Failed to obtain data of section */ + LIBR_ERROR_GETSHDR = -5, /**< Failed to obtain ELF section header: */ + LIBR_ERROR_SIZEMISMATCH = -6, /**< Section's data size does not make sense */ + LIBR_ERROR_UPDATE = -7, /**< Failed to perform dynamic update: */ + LIBR_ERROR_NEWSECTION = -8, /**< Failed to create new section */ + LIBR_ERROR_NEWDATA = -9, /**< Failed to create data for section */ + LIBR_ERROR_REMOVESECTION = -10, /**< Failed to remove section: */ + LIBR_ERROR_NOSECTION = -11, /**< ELF resource section not found */ + LIBR_ERROR_STRPTR = -12, /**< Failed to obtain section string pointer: */ + LIBR_ERROR_NOTRESOURCE = -13, /**< Not a valid libr-resource */ + LIBR_ERROR_EXPANDSECTION = -14, /**< Failed to expand section */ + LIBR_ERROR_WRONGFORMAT = -15, /**< Invalid input file format */ + LIBR_ERROR_SETFLAGS = -16, /**< Failed to set flags for section */ + LIBR_ERROR_NOPERM = -17, /**< Open handle with LIBR_READ_WRITE access */ + LIBR_ERROR_NOSIZE = -18, /**< Failed to obtain file size */ + LIBR_ERROR_SETFORMAT = -19, /**< Failed to set output file format to input file format */ + LIBR_ERROR_SETARCH = -20, /**< Failed to set output file architecture to input file architecture */ + LIBR_ERROR_OVERWRITE = -21, /**< Section already exists, over-write not specified */ + LIBR_ERROR_COMPRESS = -22, /**< Failed to compress resource data */ + LIBR_ERROR_INVALIDTYPE = -23, /**< Invalid data storage type specified */ + LIBR_ERROR_MEMALLOC = -24, /**< Failed to allocate memory for data */ + LIBR_ERROR_INVALIDPARAMS = -25, /**< Invalid parameters passed to function */ + LIBR_ERROR_UNCOMPRESS = -26, /**< Failed to uncompress resource data */ + LIBR_ERROR_ZLIBINIT = -27, /**< zlib library initialization failed */ + LIBR_ERROR_OPENFAILED = -28, /**< Failed to open input file */ + LIBR_ERROR_BEGINFAILED = -29, /**< Failed to open ELF file: */ + LIBR_ERROR_WRITEPERM = -30, /**< No write permission for file */ + LIBR_ERROR_UNSUPPORTED = -31, /**< The requested operation is not supported by the backend */ +} libr_status; +/** + * @} + */ + +typedef enum { + LIBR_READ = 0, + LIBR_READ_WRITE = 1, +} libr_access_t; + +typedef enum { + LIBR_UNCOMPRESSED = 0, + LIBR_COMPRESSED = 1 +} libr_type_t; + +typedef enum { + LIBR_NOOVERWRITE = 0, + LIBR_OVERWRITE = 1 +} libr_overwrite_t; + +#ifdef __LIBR_BUILD__ + #include "libr-internal.h" + #if __LIBR_BACKEND_libbfd__ + #include "libr-bfd.h" + #elif __LIBR_BACKEND_libelf__ + #include "libr-elf.h" + #elif __LIBR_BACKEND_readonly__ + #include "libr-ro.h" + #else /* LIBR_BACKEND */ + #error "Unhandled backend" + #endif /* LIBR_BACKEND */ + #include "libr-backends.h" +#else + struct _libr_file; + typedef struct _libr_file libr_file; +#endif /* __LIBR_BUILD__ */ + +/************************************************************************* + * libr Resource Management API + *************************************************************************/ + +/** + * @page libr_clear Remove a resource from an ELF executable. + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>int libr_clear(libr_file *handle, char *resourcename);</b> + * + * @section DESCRIPTION + * Removes a libr-compatible resource from an ELF executable. The handle + * must be opened using <b>libr_open</b>(3) with either <b>LIBR_WRITE</b> + * or <b>LIBR_READ_WRITE</b> access in order to remove a resource. + * + * Please note that resource removal does not occur until the handle is + * closed using <b>libr_close</b>(3). + * + * @param handle A handle returned by <b>libr_open</b>(3). + * @param resourcename The name of the libr-compatible resource to remove. + * + * @section SA SEE ALSO + * <b>libr_open</b>(3), <b>libr_close</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +int libr_clear(libr_file *handle, char *resourcename); + +/** + * @page libr_close Close a handle to an ELF executable. + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>void libr_close(libr_file *handle);</b> + * + * @section DESCRIPTION + * Handles opened with <b>libr_open</b>(3) should be closed with + * <b>libr_close</b>() when they are no-longer needed by the calling + * application. + * + * @param handle The handle to close. + * + * @section SA SEE ALSO + * <b>libr_open</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +void libr_close(libr_file *handle); + +/** + * @page libr_errmsg Return a detailed description of the last + * libr-related error. + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>char *libr_errmsg(void);</b> + * + * @section DESCRIPTION + * Returns a detailed string describing the last error encountered by + * the libr resource library. The string is an internal error + * description, so it should not be freed. + * + * If no errors have been encountered then NULL is returned. + * + * @section SA SEE ALSO + * <b>libr_errno</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +char *libr_errmsg(void); + +/** + * @page libr_errno Return a status code describing the last + * libr-related error. + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>libr_status libr_errno(void);</b> + * + * @section DESCRIPTION + * Returns a code corresponding to the last error encountered by + * the libr resource library. For a detailed description of possible + * return values see <b>libr_status</b>(3). + * + * To get a user-readable string corresponding to the last error the + * <b>libr_errmsg</b>(3) function should be used instead. + * + * If no errors have been encountered then <b>LIBR_OK</b> is returned. + * + * @section SA SEE ALSO + * <b>libr_errmsg</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +libr_status libr_errno(void); + +/** + * @page libr_list Obtain the name of a libr ELF resource (by index). + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>char *libr_list(libr_file *file_handle, unsigned int resourceid);</b> + * + * @section DESCRIPTION + * Returns the name of a libr-compatible resource stored in an ELF binary + * corresponding to the given resource index. The index value ranges from + * 0 to the value returned by <b>libr_resources</b>(3), which returns the + * total number of libr-compatible resources stored in the ELF binary. + * + * @param handle A handle returned by <b>libr_open</b>(3). + * @param resourceid The index of the libr-compatible resource for which + * the name will be returned. + * + * @return Returns a string containing the name of the resource section. This + * string is allocated when the function is called, so it <i>must be + * unallocated</i> with a call to <b>free</b>(3) when it is no-longer + * needed. NULL is returned on failure. + * + * @section SA SEE ALSO + * <b>libr_open</b>(3), <b>free</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +char *libr_list(libr_file *file_handle, unsigned int resourceid); + +/** + * @page libr_malloc Obtain the data corresponding to a libr ELF resource. + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>char *libr_malloc(libr_file *handle, char *resourcename, size_t *size);</b> + * + * @section DESCRIPTION + * Returns the contents of a libr-compatible resource stored in an ELF binary + * corresponding to the given resource name. + * + * @param handle A handle returned by <b>libr_open</b>(3). + * @param resourcename The name of the libr-compatible resource for which + * the data will be returned. + * @param size A pointer for storing the length of the data contained in the + * the resource. May be NULL. + * + * @return Returns NULL on failure, the pointer to a buffer containing the data + * for the resource on success. When the buffer is no-longer used it must + * be unallocated using a call to <b>free</b>(3). + * + * @section SA SEE ALSO + * <b>libr_open</b>(3), <b>free</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +char *libr_malloc(libr_file *handle, char *resourcename, size_t *size); + +/** + * @page libr_open Open an ELF executable file for resource management. + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>libr_file *libr_open(char *filename, libr_access_t access);</b> + * + * @section DESCRIPTION + * <b>libr_open</b>() can be used on any ELF executable, however, + * <b>libr_open</b>() called with <b>LIBR_READ</b> access is only useful + * for executables that already contain libr-compatible stored resources. + * + * An application can easily access its own resources by passing NULL for + * the filename and requesting <b>LIBR_READ</b> access. For the obvious + * reason that an actively-open application cannot edit itself, the + * calling binary may only request <b>LIBR_READ</b> access. + * + * @param filename ELF executable to manage. Pass a NULL pointer as the + * filename in order to access the calling binary (<b>LIBR_READ</b> + * access only) @param access Requested access type (<b>LIBR_READ</b>, + * <b>LIBR_WRITE</b>, <b>LIBR_READ_WRITE</b>), the valid operations for + * the returned handle will be restricted based upon the requested access. + * @return Returns a libr file handle on success, NULL on failure. The + * handle should be freed with <b>libr_close</b>(3) when no-longer used. + * + * @section SA SEE ALSO + * <b>libr_close</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +libr_file *libr_open(char *filename, libr_access_t access); + +/** + * @page libr_read Read out the contents of a libr ELF resource. + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>int libr_read(libr_file *handle, char *resourcename, char *buffer);</b> + * + * @section WARNING + * This function does not allocate memory for the buffer, so the buffer must + * be large enough to fit the resource data. For this reason it is suggested + * that <b>libr_malloc</b>(3) be used in preference over this function. + * + * @section DESCRIPTION + * Reads the contents of a resource embedded in an ELF binary, the resource + * must be compatible with the libr specification. + * + * @param handle A handle returned by <b>libr_open</b>(3). + * @return Returns 1 on success, 0 on failure. + * + * @section SA SEE ALSO + * <b>libr_open</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +int libr_read(libr_file *handle, char *resourcename, char *buffer); + +/** + * @page libr_resources Returns the number of resources contained in + * the ELF binary. + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>unsigned int libr_resources(libr_file *handle);</b> + * + * @section DESCRIPTION + * Returns the total number of libr-compatible resources contained + * in the ELF binary. Intended to be used with <b>libr_list</b>(3) + * to return the full list of resources contained in the binary. + * + * @param handle A handle returned by <b>libr_open</b>(3). + * @return The total number of libr resources in the binary. + * + * @section SA SEE ALSO + * <b>libr_open</b>(3), <b>libr_list</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +unsigned int libr_resources(libr_file *handle); + +/** + * @page libr_size Returns the uncompressed size of a libr resource. + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>int libr_size(libr_file *handle, char *resourcename, size_t *size);</b> + * + * @section DESCRIPTION + * Obtain the total number of bytes consumed by the uncompressed + * version of the specific libr-resource. Intended to be used with + * <b>libr_read</b>(3) in order to allocate a large enough buffer + * for the resource. + * + * @param handle A handle returned by <b>libr_open</b>(3). + * @param resourcename The name of the resource for which the + * size of the data section will be returned. + * @param size A pointer for storing the size of the data section. + * This pointer cannot be NULL. + * @return Returns 1 on success, 0 on failure. + * + * @section SA SEE ALSO + * <b>libr_open</b>(3), <b>libr_read</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +int libr_size(libr_file *handle, char *resourcename, size_t *size); + +/** + * @page libr_write Adds a libr resource to an ELF binary. + * @section SYNOPSIS + * \#include <libr.h> + * + * <b>int libr_write(libr_file *handle, char *resourcename, char *buffer, size_t size, libr_type_t type, libr_overwrite_t overwrite);</b> + * + * @section DESCRIPTION + * Adds a libr-compatible resource into the ELF binary. The handle + * must be opened using <b>libr_open</b>(3) with either <b>LIBR_WRITE</b> + * or <b>LIBR_READ_WRITE</b> access in order to add a resource. + * + * @param handle A handle returned by <b>libr_open</b>(3). + * @param resourcename The name of the resource to create. + * @param buffer A string containing the data of the resource. + * @param size The total size of the buffer. + * @param type The method which should be used for storing the + * data (either <b>LIBR_UNCOMPRESSED</b> or + * <b>LIBR_COMPRESSED</b>). + * @param overwrite Whether overwriting an existing resource + * should be permitted (either <b>LIBR_NOOVERWRITE</b> or + * <b>LIBR_OVERWRITE</b>). + * @return Returns 1 on success, 0 on failure. + * + * @section SA SEE ALSO + * <b>libr_open</b>(3) + * + * @section AUTHOR + * Erich Hoover <ehoover@mines.edu> + */ +int libr_write(libr_file *handle, char *resourcename, char *buffer, size_t size, libr_type_t type, libr_overwrite_t overwrite); + +#endif /* __LIBR_H */ + diff --git a/src/onecanvas.c b/src/onecanvas.c new file mode 100644 index 0000000..e53ece7 --- /dev/null +++ b/src/onecanvas.c @@ -0,0 +1,446 @@ +/* + * + * Copyright (c) 2010 Erich Hoover + * + * libr "one canvas" - Handle multiple icons stored in a single "one canvas" + * SVG document. + * + * This program 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.1 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * To provide feedback, report bugs, or otherwise contact me: + * ehoover at mines dot edu + * + */ + +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <stdlib.h> +#include <time.h> + +#define FALSE 0 +#define TRUE 1 + +typedef struct { + double x; + double y; + double width; + double height; + int icon_width; + int icon_height; +} IconSVG; + +typedef enum { + STATUS_FINDSVG, + STATUS_FINDMETADATA, + STATUS_FINDPUBLISHER_START, + STATUS_FINDPUBLISHER_STOP, + STATUS_FINDHIDDEN, + STATUS_FINDBOUNDS, + STATUS_FAILED, + STATUS_DONE, +} eStatus; + +typedef struct { + IconSVG **iconlist; + int iconlist_num; + eStatus status; + + char *hidden_stop; + char *hidden_start; + char *publisher_stop; + char *publisher_start; + char *coordinate_stop; + char *coordinate_start; +} OneCanvasIconInfo; + +/* + * Find the start of the next XML tag (search for '<') + */ +static inline char *xml_nextTag(char *c) +{ + c++; + if(c == NULL) + return NULL; + return strchr(c, '<'); +} + +/* + * Pull out the name/type of a tag. + */ +static inline char *xml_getTagName(char *c) +{ + char *tag_end = NULL, *tag_space, *tag_close, *tag_feed, *tag_line; + static char tagname[20]; + int tag_len; + + if(++c == NULL) + return NULL; + tag_space = strchr(c, ' '); + tag_close = strchr(c, '>'); + tag_feed = strchr(c, '\r'); + tag_line = strchr(c, '\n'); + if(tag_space) + tag_end = tag_space; + if(tag_close && tag_end > tag_close) + tag_end = tag_close; + if(tag_feed && tag_end > tag_feed) + tag_end = tag_feed; + if(tag_line && tag_end > tag_line) + tag_end = tag_line; + if(!tag_end) + return NULL; + tag_len = tag_end - c; + tag_len = tag_len > 19 ? 19 : tag_len; + strncpy(tagname, c, tag_len); + tagname[tag_len] = '\0'; + return tagname; +} + +/* + * Find the position in the string corresponding to a particular named attribute. + */ +static inline char *xml_getTagAttributePtr(char *c, char *attrname) +{ + char *end, *name; + int found; + + if(++c == NULL) + return NULL; + end = strchr(c, '>'); + while(c < end) + { + int name_len; + char *equal; + + equal = c = strchr(c, '='); + if(c == NULL) + break; + c++; + name = equal; + while(name[0] != ' ' && name[0] != '\t' && name[0] != '\n') + name--; + name++; /* don't include the space */ + name_len = equal-name; + if(name_len != strlen(attrname)) + continue; + if(strncasecmp(attrname, name, name_len) == 0) + { + found = TRUE; + break; + } + } + if(!found) + return NULL; + return c-strlen(attrname)-1; +} + +/* + * Return the value of an XML tag's named attribute. + */ +static inline char *xml_getTagAttribute(char *c, char *attrname) +{ + char *data_end; + int data_len; + char *attr; + + c = xml_getTagAttributePtr(c, attrname); + if(c == NULL) + return NULL; + c+=strlen(attrname); /* skip the name */ + c+=2; /* skip the equals sign and the quote */ + data_end = strchr(c, '"'); + data_len = data_end - c; + attr = (char *) malloc(data_len+1); + strncpy(attr, c, data_len); + attr[data_len] = '\0'; + return attr; +} + +/* + * Find the value of an XML tag attribute and convert it to a number. + */ +static inline double xml_getTagAttributeFloat(char *c, char *attrname) +{ + char *value = xml_getTagAttribute(c, attrname); + double ret; + + if(!value) + return nan("nan"); + sscanf(value, "%lf", &ret); + free(value); + return ret; +} + +/* + * Match the beginning an XML tag by "id" (preferred) or Inkscape's + * label (undesireable but acceptable). + */ +static inline char *xml_idMatchStart(char *stream_pos, char *layer_name) +{ + char *id_acceptable = xml_getTagAttribute(stream_pos, "inkscape:label"); + char *id_preferred = xml_getTagAttribute(stream_pos, "id"); + + if(id_preferred && strncasecmp(id_preferred, layer_name, strlen(layer_name)) == 0) + { + free(id_acceptable); + return id_preferred; + } + if(id_acceptable && strncasecmp(id_acceptable, layer_name, strlen(layer_name)) == 0) + { + free(id_preferred); + return id_acceptable; + } + free(id_acceptable); + free(id_preferred); + return NULL; +} + +/* + * Match the entirety of an XML tag by "id" (preferred) or Inkscape's + * label (undesireable but acceptable). + */ +static inline int xml_idMatch(char *stream_pos, char *layer_name) +{ + char *id_acceptable = xml_getTagAttribute(stream_pos, "inkscape:label"); + char *id_preferred = xml_getTagAttribute(stream_pos, "id"); + int ret = FALSE; + + if((id_preferred && strcasecmp(id_preferred, layer_name) == 0) + || (id_acceptable && strcasecmp(id_acceptable, layer_name) == 0)) + ret = TRUE; + free(id_acceptable); + free(id_preferred); + return ret; +} + +/* + * Strip all the XML tags from a string and return only the data not + * contained within any tags. + */ +static inline char *xml_stripTags(char *data, int len) +{ + char *ret = (char *) malloc(len+1); + char *tag_left, *tag_right; + + memcpy(ret, data, len+1); + ret[len] = '\0'; + while((tag_left = strchr(ret, '<')) != NULL) + { + tag_right = strchr(ret, '>'); + memmove(tag_left, tag_right+1, strlen(ret)-(tag_right-ret)); + } + return ret; +} + +/* + * Return the information for all of the icons within a "one-canvas" + * data stream. + */ +OneCanvasIconInfo onecanvas_geticons(char *stream) +{ + eStatus status = STATUS_FINDSVG; + unsigned int stream_size = 0; + OneCanvasIconInfo info; + char *publisher = NULL; + char *stream_pos; + int i; + + memset(&info, 0, sizeof(info)); + stream_pos = stream; + while(stream_pos) + { + char *name = xml_getTagName(stream_pos); + + if(!name) + { + stream_pos = xml_nextTag(stream_pos); + continue; + } + switch(status) + { + case STATUS_FINDSVG: + { + if(strcasecmp(name, "svg") == 0) + { + info.coordinate_start = xml_getTagAttributePtr(stream_pos, "x"); + info.coordinate_stop = xml_getTagAttributePtr(stream_pos, "viewBox"); + if(info.coordinate_start == NULL || info.coordinate_stop == NULL) + { + status = STATUS_FAILED; + break; + } + info.coordinate_stop = strchr(info.coordinate_stop, '"')+1; + info.coordinate_stop = strchr(info.coordinate_stop, '"')+1; + status = STATUS_FINDMETADATA; + } + } break; + case STATUS_FINDMETADATA: + { + if(strcasecmp(name, "metadata") == 0) + { + status = STATUS_FINDPUBLISHER_START; + } + else if(strcasecmp(name, "/svg") == 0) + { + status = STATUS_FAILED; + } + } break; + case STATUS_FINDPUBLISHER_START: + { + if(strcasecmp(name, "dc:publisher") == 0) + { + status = STATUS_FINDPUBLISHER_STOP; + info.publisher_start = stream_pos + strlen("<dc:publisher>"); + } + else if(strcasecmp(name, "/metadata") == 0) + { + status = STATUS_FAILED; + } + } break; + case STATUS_FINDPUBLISHER_STOP: + { + if(strcasecmp(name, "/dc:publisher") == 0) + { + info.publisher_stop = stream_pos; + publisher = xml_stripTags(info.publisher_start, info.publisher_stop-info.publisher_start); + if(strcasecmp(publisher, "one-canvas") == 0) + status = STATUS_FINDHIDDEN; + else + status = STATUS_FAILED; + } + else if(strcasecmp(name, "/metadata") == 0) + { + status = STATUS_FAILED; + } + } break; + case STATUS_FINDHIDDEN: + { + if(strcasecmp(name, "g") == 0) + { + if(xml_idMatch(stream_pos, "hidden")) + { + char *style_start; + + info.hidden_start = stream_pos; + info.hidden_stop = info.hidden_start; + style_start = xml_getTagAttributePtr(stream_pos, "style"); + if(style_start) + { + info.hidden_start = style_start; + info.hidden_stop = strchr(style_start, '"')+1; + info.hidden_stop = strchr(info.hidden_stop, '"')+1; + } + else + { + info.hidden_start += strlen("<g "); + info.hidden_stop += strlen("<g "); + } + status = STATUS_FINDBOUNDS; + } + } + } break; + case STATUS_FINDBOUNDS: + { + if(strcasecmp(name, "rect") == 0) + { + char *layer_name = xml_idMatchStart(stream_pos, "iconlayer-"); + + if(layer_name != NULL) + { + IconSVG *icon = (IconSVG *) malloc(sizeof(IconSVG)); + + icon->x = xml_getTagAttributeFloat(stream_pos, "x"); + icon->y = xml_getTagAttributeFloat(stream_pos, "y"); + icon->width = xml_getTagAttributeFloat(stream_pos, "width"); + icon->height = xml_getTagAttributeFloat(stream_pos, "height"); + sscanf(layer_name, "iconlayer-%dx%d", &(icon->icon_width), &(icon->icon_height)); + free(layer_name); + status = STATUS_FINDBOUNDS; + info.iconlist = (IconSVG **) realloc(info.iconlist, (info.iconlist_num+1)*sizeof(IconSVG *)); + info.iconlist[info.iconlist_num] = icon; + info.iconlist_num++; + } + } + else if(strcasecmp(name, "/g") == 0) + { + status = STATUS_DONE; + } + } break; + default: + break; + } + if(status == STATUS_DONE || status == STATUS_FAILED) + break; + stream_pos = xml_nextTag(stream_pos); + } + free(publisher); + info.status = status; + return info; +} + +/* + * Obtain a single icon from the "one-canvas" stream corresponding + * to a particular icon size. + */ +char *onecanvas_geticon_bysize(char *icon_data, int requested_size) +{ + OneCanvasIconInfo info = onecanvas_geticons(icon_data); + char *ret = NULL; + int i; + + if(info.status == STATUS_DONE && info.iconlist_num > 0) + { + int closest_diff = abs(info.iconlist[0]->icon_width - requested_size); + int tocoord_length, topubl_length, tohidden_length; + int icon_id = 0; + IconSVG *icon; + int ret_max; + + for(i=0;i<info.iconlist_num;i++) + { + int size_diff = abs(info.iconlist[i]->icon_width - requested_size); + + if(size_diff < closest_diff) + { + closest_diff = size_diff; + icon_id = i; + } + } + icon = info.iconlist[icon_id]; + /* Note: 200 characters is a very generous over estimate for the data we add in */ + ret_max = strlen(icon_data)+1+200; + ret = (char *) malloc(ret_max); + tocoord_length = info.coordinate_start-icon_data; + snprintf(ret, ret_max, "%.*s", tocoord_length, icon_data); + /* Output the coordinates of the icon */ + snprintf(&ret[strlen(ret)], ret_max-strlen(ret), "\nx=\"0px\"\ny=\"0px\"\n"); + snprintf(&ret[strlen(ret)], ret_max-strlen(ret), "width=\"%d\"\n", icon->icon_width); + snprintf(&ret[strlen(ret)], ret_max-strlen(ret), "height=\"%d\"\n", icon->icon_height); + snprintf(&ret[strlen(ret)], ret_max-strlen(ret), "viewBox=\"%lf %lf %lf %lf\"\n", icon->x, icon->y, icon->width, icon->height); + topubl_length = info.publisher_start-info.coordinate_stop; + snprintf(&ret[strlen(ret)], ret_max-strlen(ret), "%.*s", topubl_length, info.coordinate_stop); + /* Hide the "hidden" layer */ + tohidden_length = info.hidden_start-info.publisher_stop; + snprintf(&ret[strlen(ret)], ret_max-strlen(ret), "%.*s", tohidden_length, info.publisher_stop); + snprintf(&ret[strlen(ret)], ret_max-strlen(ret), "\ndisplay=\"none\"\n"); + /* Output the rest of the document */ + snprintf(&ret[strlen(ret)], ret_max-strlen(ret), "%s", info.hidden_stop); + } + for(i=0;i<info.iconlist_num;i++) + free(info.iconlist[i]); + free(info.iconlist); + return ret; +}
\ No newline at end of file diff --git a/src/onecanvas.h b/src/onecanvas.h new file mode 100644 index 0000000..e201417 --- /dev/null +++ b/src/onecanvas.h @@ -0,0 +1,6 @@ +#ifndef __ONECANVAS_H +#define __ONECANVAS_H + +char *onecanvas_geticon_bysize(char *icon_data, int requested_size); + +#endif /* __ONECANVAS_H */ diff --git a/src/tempfiles.c b/src/tempfiles.c new file mode 100644 index 0000000..edf72a1 --- /dev/null +++ b/src/tempfiles.c @@ -0,0 +1,317 @@ +/* + * + * Copyright (c) 2009 Erich Hoover + * + * libr temp files - Handle temporary files and handles that require cleanup + * when libr closes. + * + * This program 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.1 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * To provide feedback, report bugs, or otherwise contact me: + * ehoover at mines dot edu + * + */ + +#include "tempfiles.h" + +/* For malloc/free and mkdtemp */ +#include <stdlib.h> + +/* For string handling */ +#include <string.h> +#include <stdio.h> + +/* For directory cleanup */ +#include <unistd.h> +#include <dirent.h> + +/* For directory creation */ +#include <sys/stat.h> +#include <sys/types.h> +#include <errno.h> + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +/* Hold on to folder names for cleanup when libr is removed from memory */ +typedef struct CLEANUPFOLDER { + char *folder; + struct CLEANUPFOLDER *next; +} CleanupFolder; +CleanupFolder *folders_to_remove = NULL; + +/* Hold on to libr handles for cleanup when libr is removed from memory */ +typedef struct CLEANUPHANDLE { + int internal; /* do not warn the user about cleaning this handle up */ + libr_file *handle; + struct CLEANUPHANDLE *next; +} CleanupHandle; +CleanupHandle *handles_to_remove = NULL; + +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + +/* + * Register a folder for cleanup when libr is removed from memory + */ +void register_folder_cleanup(char *temp_folder) +{ + CleanupFolder *folder = malloc(sizeof(CleanupFolder)); + + folder->folder = strdup(temp_folder); + folder->next = NULL; + if(folders_to_remove != NULL) + { + CleanupFolder *f; + + for(f = folders_to_remove; f->next != NULL; f = f->next) {} + f->next = folder; + } + else + folders_to_remove = folder; +} + +/* + * Register a libr handle for cleanup when libr is removed from memory + */ +void register_handle_cleanup(libr_file *handle) +{ + CleanupHandle *h = malloc(sizeof(CleanupHandle)); + + h->handle = handle; + h->internal = FALSE; + h->next = NULL; + if(handles_to_remove != NULL) + { + CleanupHandle *i; + + for(i = handles_to_remove; i->next != NULL; i = i->next) {} + i->next = h; + } + else + handles_to_remove = h; +} + +/* + * Remove a libr handle from the cleanup list + */ +void unregister_handle_cleanup(libr_file *handle) +{ + CleanupHandle *i, *last = NULL; + int found = FALSE; + + if(handles_to_remove == NULL) + { + printf("Unregistering handle with no list of cleanup handles!\n"); + return; + } + for(i = handles_to_remove; i != NULL; last = i, i = i->next) + { + if(i->handle == handle) + { + if(last == NULL) + handles_to_remove = i->next; + else + last->next = i->next; + free(i); + found = TRUE; + break; + } + } + if(!found) + printf("Could not find handle to remove from cleanup list!\n"); +} + +/* + * Flag a handle as internal (do not warn about unsafe cleanup) + */ +void register_internal_handle(libr_file *handle) +{ + int found = FALSE; + CleanupHandle *i; + + if(handles_to_remove == NULL) + { + printf("No cleanup list!\n"); + return; + } + for(i = handles_to_remove; i != NULL; i = i->next) + { + if(i->handle == handle) + { + i->internal = TRUE; + found = TRUE; + break; + } + } + if(!found) + printf("Could not find handle in cleanup list!\n"); +} + +/* + * Cleanup a temporary folder used to hack the inability to load resources from a buffer + */ +void cleanup_folder(char *temp_folder) +{ + char *filepath = (char *) malloc(PATH_MAX); + DIR *dir = opendir(temp_folder); + struct dirent *file; + + while((file = readdir(dir)) != NULL) + { + char *filename = file->d_name; + + /* Do not delete "self" or "parent" directory entries */ + if(!strcmp(filename, ".") || !strcmp(filename, "..")) + continue; + /* But delete anything else */ + strcpy(filepath, temp_folder); + strcat(filepath, "/"); + strcat(filepath, filename); + if(file->d_type == DT_DIR) + cleanup_folder(filepath); + else + { + if(unlink(filepath)) + printf("libr failed to cleanup '%s' in temporary folder: %m\n", filename); + } + } + free(filepath); + closedir(dir); + if(rmdir(temp_folder) != 0) + printf("libr failed to remove temporary folder: %m\n"); +} + +/* + * Perform cleanup when libr is removed from memory + */ +void do_cleanup(void) __attribute__((destructor)); +void do_cleanup(void) +{ + CleanupFolder *f, *fnext; + CleanupHandle *h, *hnext; + + /* Cleanup folders */ + for(f = folders_to_remove; f != NULL; f = fnext) + { + folders_to_remove = NULL; + fnext = f->next; + cleanup_folder(f->folder); + free(f->folder); + free(f); + } + /* Cleanup handles */ + for(h = handles_to_remove; h != NULL; h = hnext) + { + handles_to_remove = NULL; + hnext = h->next; + /* Unless the handle was created internally then warn the developer to cleanup their act */ + if(!h->internal) + printf("Warning: Application did not cleanup resource handle: %p\n", h->handle); + libr_close_internal(h->handle); + free(h); + } +} + +/* + * Build all the directories required by a resource + * (and construct the output string) + */ +int make_valid_path(char *out_path, size_t maxpath, char *start_folder, char *resource_name) +{ + char *a, *c = resource_name; + + strcpy(out_path, start_folder); + while((a=strchr(c, '/')) != NULL) + { + strcat(out_path, "/"); + strncat(out_path, c, (size_t) (a-c)); + if(mkdir(out_path, S_IRUSR|S_IWUSR|S_IXUSR) != 0) + { + if(errno != EEXIST) + { + printf("failed to make directory: %s %m\n", out_path); + return false; + } + } + c = a+1; + } + strcat(out_path, "/"); + strcat(out_path, c); + return true; +} + +/* + * Extract all the resources from the ELF file for use by the resource loader + */ +char *libr_extract_resources(libr_file *handle) +{ + char *temp_mask = strdup(LIBR_TEMPFILE); + char *temp_folder; + int i = 0; + + temp_folder = mkdtemp(temp_mask); + if(temp_folder == NULL) + { + /* failed to extract ELF resources, could not create a temporary path */ + goto failed; + } + /* If this library cannot dynamically load resources then pull out all the resources to a temporary directory */ + for(i=0;i<libr_resources(handle);i++) + { + char *resource_name = libr_list(handle, i); + char *file_path[PATH_MAX]; + size_t resource_size; + FILE *file_handle; + char *resource; + + resource = libr_malloc(handle, resource_name, &resource_size); + if(!make_valid_path((char *)file_path, sizeof(file_path), temp_folder, resource_name)) + { + /* failed to build the path required by a resource */ + cleanup_folder(temp_folder); + temp_folder = NULL; + goto failed; + } + file_handle = fopen((const char *) file_path, "w"); + if(file_handle == NULL) + { + /* failed to extract ELF resources, could not write to temporary path */ + cleanup_folder(temp_folder); + temp_folder = NULL; + goto failed; + } + /* if the resource is empty then fwrite will fail */ + if( (resource_size != 0) && (fwrite(resource, resource_size, 1, file_handle) != 1) ) + { + /* failed to extract ELF resources, temporary path out of space? */ + cleanup_folder(temp_folder); + temp_folder = NULL; + goto failed; + } + fclose(file_handle); + free(resource); + } +failed: + if(temp_folder != NULL) + temp_folder = strdup(temp_folder); + free(temp_mask); + return temp_folder; +} diff --git a/src/tempfiles.h b/src/tempfiles.h new file mode 100644 index 0000000..5b9b0bc --- /dev/null +++ b/src/tempfiles.h @@ -0,0 +1,13 @@ +#ifndef __TEMPFILES_H +#define __TEMPFILES_H + +#include "libr.h" + +void cleanup_folder(char *temp_folder); +void register_handle_cleanup(libr_file *handle); +void unregister_handle_cleanup(libr_file *handle); +void register_internal_handle(libr_file *handle); +void register_folder_cleanup(char *temp_folder); +char *libr_extract_resources(libr_file *handle); + +#endif /* __TEMPFILES_H */ |