summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/debugedit.c1661
-rw-r--r--tools/elfdeps.c299
-rw-r--r--tools/hashtab.c523
-rw-r--r--tools/hashtab.h143
-rw-r--r--tools/javadeps.c1288
-rw-r--r--tools/rpmdeps.c105
-rw-r--r--tools/rpmgraph.c258
7 files changed, 4277 insertions, 0 deletions
diff --git a/tools/debugedit.c b/tools/debugedit.c
new file mode 100644
index 0000000..d1b9335
--- /dev/null
+++ b/tools/debugedit.c
@@ -0,0 +1,1661 @@
+/* Copyright (C) 2001, 2002, 2003, 2005, 2007, 2009, 2010, 2011 Red Hat, Inc.
+ Written by Alexander Larsson <alexl@redhat.com>, 2002
+ Based on code by Jakub Jelinek <jakub@redhat.com>, 2001.
+
+ 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 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. */
+
+#include "system.h"
+
+/* Needed for libelf */
+#define _FILE_OFFSET_BITS 64
+
+#include <assert.h>
+#include <byteswap.h>
+#include <endian.h>
+#include <errno.h>
+#include <error.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <popt.h>
+
+#include <gelf.h>
+#include <dwarf.h>
+
+#include <rpm/rpmio.h>
+#include <rpm/rpmpgp.h>
+#include "tools/hashtab.h"
+
+#define DW_TAG_partial_unit 0x3c
+#define DW_FORM_sec_offset 0x17
+#define DW_FORM_exprloc 0x18
+#define DW_FORM_flag_present 0x19
+#define DW_FORM_ref_sig8 0x20
+
+char *base_dir = NULL;
+char *dest_dir = NULL;
+char *list_file = NULL;
+int list_file_fd = -1;
+int do_build_id = 0;
+
+typedef struct
+{
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ Elf_Scn **scn;
+ const char *filename;
+ int lastscn;
+ GElf_Shdr shdr[0];
+} DSO;
+
+typedef struct
+{
+ unsigned char *ptr;
+ uint32_t addend;
+} REL;
+
+#define read_uleb128(ptr) ({ \
+ unsigned int ret = 0; \
+ unsigned int c; \
+ int shift = 0; \
+ do \
+ { \
+ c = *ptr++; \
+ ret |= (c & 0x7f) << shift; \
+ shift += 7; \
+ } while (c & 0x80); \
+ \
+ if (shift >= 35) \
+ ret = UINT_MAX; \
+ ret; \
+})
+
+static uint16_t (*do_read_16) (unsigned char *ptr);
+static uint32_t (*do_read_32) (unsigned char *ptr);
+static void (*write_32) (unsigned char *ptr, GElf_Addr val);
+
+static int ptr_size;
+static int cu_version;
+
+static inline uint16_t
+buf_read_ule16 (unsigned char *data)
+{
+ return data[0] | (data[1] << 8);
+}
+
+static inline uint16_t
+buf_read_ube16 (unsigned char *data)
+{
+ return data[1] | (data[0] << 8);
+}
+
+static inline uint32_t
+buf_read_ule32 (unsigned char *data)
+{
+ return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+}
+
+static inline uint32_t
+buf_read_ube32 (unsigned char *data)
+{
+ return data[3] | (data[2] << 8) | (data[1] << 16) | (data[0] << 24);
+}
+
+static const char *
+strptr (DSO *dso, int sec, off_t offset)
+{
+ Elf_Scn *scn;
+ Elf_Data *data;
+
+ scn = dso->scn[sec];
+ if (offset >= 0 && (GElf_Addr) offset < dso->shdr[sec].sh_size)
+ {
+ data = NULL;
+ while ((data = elf_rawdata (scn, data)) != NULL)
+ {
+ if (data->d_buf
+ && offset >= data->d_off
+ && offset < data->d_off + data->d_size)
+ return (const char *) data->d_buf + (offset - data->d_off);
+ }
+ }
+
+ return NULL;
+}
+
+
+#define read_1(ptr) *ptr++
+
+#define read_16(ptr) ({ \
+ uint16_t ret = do_read_16 (ptr); \
+ ptr += 2; \
+ ret; \
+})
+
+#define read_32(ptr) ({ \
+ uint32_t ret = do_read_32 (ptr); \
+ ptr += 4; \
+ ret; \
+})
+
+REL *relptr, *relend;
+int reltype;
+
+#define do_read_32_relocated(ptr) ({ \
+ uint32_t dret = do_read_32 (ptr); \
+ if (relptr) \
+ { \
+ while (relptr < relend && relptr->ptr < ptr) \
+ ++relptr; \
+ if (relptr < relend && relptr->ptr == ptr) \
+ { \
+ if (reltype == SHT_REL) \
+ dret += relptr->addend; \
+ else \
+ dret = relptr->addend; \
+ } \
+ } \
+ dret; \
+})
+
+#define read_32_relocated(ptr) ({ \
+ uint32_t ret = do_read_32_relocated (ptr); \
+ ptr += 4; \
+ ret; \
+})
+
+static void
+dwarf2_write_le32 (unsigned char *p, GElf_Addr val)
+{
+ uint32_t v = (uint32_t) val;
+
+ p[0] = v;
+ p[1] = v >> 8;
+ p[2] = v >> 16;
+ p[3] = v >> 24;
+}
+
+
+static void
+dwarf2_write_be32 (unsigned char *p, GElf_Addr val)
+{
+ uint32_t v = (uint32_t) val;
+
+ p[3] = v;
+ p[2] = v >> 8;
+ p[1] = v >> 16;
+ p[0] = v >> 24;
+}
+
+static struct
+ {
+ const char *name;
+ unsigned char *data;
+ Elf_Data *elf_data;
+ size_t size;
+ int sec, relsec;
+ } debug_sections[] =
+ {
+#define DEBUG_INFO 0
+#define DEBUG_ABBREV 1
+#define DEBUG_LINE 2
+#define DEBUG_ARANGES 3
+#define DEBUG_PUBNAMES 4
+#define DEBUG_PUBTYPES 5
+#define DEBUG_MACINFO 6
+#define DEBUG_LOC 7
+#define DEBUG_STR 8
+#define DEBUG_FRAME 9
+#define DEBUG_RANGES 10
+#define DEBUG_TYPES 11
+ { ".debug_info", NULL, NULL, 0, 0, 0 },
+ { ".debug_abbrev", NULL, NULL, 0, 0, 0 },
+ { ".debug_line", NULL, NULL, 0, 0, 0 },
+ { ".debug_aranges", NULL, NULL, 0, 0, 0 },
+ { ".debug_pubnames", NULL, NULL, 0, 0, 0 },
+ { ".debug_pubtypes", NULL, NULL, 0, 0, 0 },
+ { ".debug_macinfo", NULL, NULL, 0, 0, 0 },
+ { ".debug_loc", NULL, NULL, 0, 0, 0 },
+ { ".debug_str", NULL, NULL, 0, 0, 0 },
+ { ".debug_frame", NULL, NULL, 0, 0, 0 },
+ { ".debug_ranges", NULL, NULL, 0, 0, 0 },
+ { ".debug_types", NULL, NULL, 0, 0, 0 },
+ { NULL, NULL, NULL, 0, 0, 0 }
+ };
+
+struct abbrev_attr
+ {
+ unsigned int attr;
+ unsigned int form;
+ };
+
+struct abbrev_tag
+ {
+ unsigned int entry;
+ unsigned int tag;
+ int nattr;
+ struct abbrev_attr attr[0];
+ };
+
+static hashval_t
+abbrev_hash (const void *p)
+{
+ struct abbrev_tag *t = (struct abbrev_tag *)p;
+
+ return t->entry;
+}
+
+static int
+abbrev_eq (const void *p, const void *q)
+{
+ struct abbrev_tag *t1 = (struct abbrev_tag *)p;
+ struct abbrev_tag *t2 = (struct abbrev_tag *)q;
+
+ return t1->entry == t2->entry;
+}
+
+static void
+abbrev_del (void *p)
+{
+ free (p);
+}
+
+static htab_t
+read_abbrev (DSO *dso, unsigned char *ptr)
+{
+ htab_t h = htab_try_create (50, abbrev_hash, abbrev_eq, abbrev_del);
+ unsigned int attr, form;
+ struct abbrev_tag *t;
+ int size;
+ void **slot;
+
+ if (h == NULL)
+ {
+no_memory:
+ error (0, ENOMEM, "%s: Could not read .debug_abbrev", dso->filename);
+ if (h)
+ htab_delete (h);
+ return NULL;
+ }
+
+ while ((attr = read_uleb128 (ptr)) != 0)
+ {
+ size = 10;
+ t = malloc (sizeof (*t) + size * sizeof (struct abbrev_attr));
+ if (t == NULL)
+ goto no_memory;
+ t->entry = attr;
+ t->nattr = 0;
+ slot = htab_find_slot (h, t, INSERT);
+ if (slot == NULL)
+ {
+ free (t);
+ goto no_memory;
+ }
+ if (*slot != NULL)
+ {
+ error (0, 0, "%s: Duplicate DWARF abbreviation %d", dso->filename,
+ t->entry);
+ free (t);
+ htab_delete (h);
+ return NULL;
+ }
+ t->tag = read_uleb128 (ptr);
+ ++ptr; /* skip children flag. */
+ while ((attr = read_uleb128 (ptr)) != 0)
+ {
+ if (t->nattr == size)
+ {
+ size += 10;
+ t = realloc (t, sizeof (*t) + size * sizeof (struct abbrev_attr));
+ if (t == NULL)
+ goto no_memory;
+ }
+ form = read_uleb128 (ptr);
+ if (form == 2
+ || (form > DW_FORM_flag_present && form != DW_FORM_ref_sig8))
+ {
+ error (0, 0, "%s: Unknown DWARF DW_FORM_%d", dso->filename, form);
+ htab_delete (h);
+ return NULL;
+ }
+
+ t->attr[t->nattr].attr = attr;
+ t->attr[t->nattr++].form = form;
+ }
+ if (read_uleb128 (ptr) != 0)
+ {
+ error (0, 0, "%s: DWARF abbreviation does not end with 2 zeros",
+ dso->filename);
+ htab_delete (h);
+ return NULL;
+ }
+ *slot = t;
+ }
+
+ return h;
+}
+
+#define IS_DIR_SEPARATOR(c) ((c)=='/')
+
+static char *
+canonicalize_path (const char *s, char *d)
+{
+ char *rv = d;
+ const char *sroot;
+ char *droot;
+
+ if (IS_DIR_SEPARATOR (*s))
+ {
+ *d++ = *s++;
+ if (IS_DIR_SEPARATOR (*s) && !IS_DIR_SEPARATOR (s[1]))
+ {
+ /* Special case for "//foo" meaning a Posix namespace
+ escape. */
+ *d++ = *s++;
+ }
+ while (IS_DIR_SEPARATOR (*s))
+ s++;
+ }
+ droot = d;
+ sroot = s;
+
+ while (*s)
+ {
+ /* At this point, we're always at the beginning of a path
+ segment. */
+
+ if (s[0] == '.' && (s[1] == 0 || IS_DIR_SEPARATOR (s[1])))
+ {
+ s++;
+ if (*s)
+ while (IS_DIR_SEPARATOR (*s))
+ ++s;
+ }
+
+ else if (s[0] == '.' && s[1] == '.'
+ && (s[2] == 0 || IS_DIR_SEPARATOR (s[2])))
+ {
+ char *pre = d - 1; /* includes slash */
+ while (droot < pre && IS_DIR_SEPARATOR (*pre))
+ pre--;
+ if (droot <= pre && ! IS_DIR_SEPARATOR (*pre))
+ {
+ while (droot < pre && ! IS_DIR_SEPARATOR (*pre))
+ pre--;
+ /* pre now points to the slash */
+ if (droot < pre)
+ pre++;
+ if (pre + 3 == d && pre[0] == '.' && pre[1] == '.')
+ {
+ *d++ = *s++;
+ *d++ = *s++;
+ }
+ else
+ {
+ d = pre;
+ s += 2;
+ if (*s)
+ while (IS_DIR_SEPARATOR (*s))
+ s++;
+ }
+ }
+ else
+ {
+ *d++ = *s++;
+ *d++ = *s++;
+ }
+ }
+ else
+ {
+ while (*s && ! IS_DIR_SEPARATOR (*s))
+ *d++ = *s++;
+ }
+
+ if (IS_DIR_SEPARATOR (*s))
+ {
+ *d++ = *s++;
+ while (IS_DIR_SEPARATOR (*s))
+ s++;
+ }
+ }
+ while (droot < d && IS_DIR_SEPARATOR (d[-1]))
+ --d;
+ if (d == rv)
+ *d++ = '.';
+ *d = 0;
+
+ return rv;
+}
+
+static int
+has_prefix (const char *str,
+ const char *prefix)
+{
+ size_t str_len;
+ size_t prefix_len;
+
+ str_len = strlen (str);
+ prefix_len = strlen (prefix);
+
+ if (str_len < prefix_len)
+ return 0;
+
+ return strncmp (str, prefix, prefix_len) == 0;
+}
+
+static int dirty_elf;
+static void
+dirty_section (unsigned int sec)
+{
+ elf_flagdata (debug_sections[sec].elf_data, ELF_C_SET, ELF_F_DIRTY);
+ dirty_elf = 1;
+}
+
+static int
+edit_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir, int phase)
+{
+ unsigned char *ptr = debug_sections[DEBUG_LINE].data, *dir;
+ unsigned char **dirt;
+ unsigned char *endsec = ptr + debug_sections[DEBUG_LINE].size;
+ unsigned char *endcu, *endprol;
+ unsigned char opcode_base;
+ uint32_t value, dirt_cnt;
+ size_t comp_dir_len = strlen (comp_dir);
+ size_t abs_file_cnt = 0, abs_dir_cnt = 0;
+
+ if (phase != 0)
+ return 0;
+
+ ptr += off;
+
+ endcu = ptr + 4;
+ endcu += read_32 (ptr);
+ if (endcu == ptr + 0xffffffff)
+ {
+ error (0, 0, "%s: 64-bit DWARF not supported", dso->filename);
+ return 1;
+ }
+
+ if (endcu > endsec)
+ {
+ error (0, 0, "%s: .debug_line CU does not fit into section",
+ dso->filename);
+ return 1;
+ }
+
+ value = read_16 (ptr);
+ if (value != 2 && value != 3 && value != 4)
+ {
+ error (0, 0, "%s: DWARF version %d unhandled", dso->filename,
+ value);
+ return 1;
+ }
+
+ endprol = ptr + 4;
+ endprol += read_32 (ptr);
+ if (endprol > endcu)
+ {
+ error (0, 0, "%s: .debug_line CU prologue does not fit into CU",
+ dso->filename);
+ return 1;
+ }
+
+ opcode_base = ptr[4 + (value >= 4)];
+ ptr = dir = ptr + 4 + (value >= 4) + opcode_base;
+
+ /* dir table: */
+ value = 1;
+ while (*ptr != 0)
+ {
+ ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1;
+ ++value;
+ }
+
+ dirt = (unsigned char **) alloca (value * sizeof (unsigned char *));
+ dirt[0] = (unsigned char *) ".";
+ dirt_cnt = 1;
+ ptr = dir;
+ while (*ptr != 0)
+ {
+ dirt[dirt_cnt++] = ptr;
+ ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1;
+ }
+ ptr++;
+
+ /* file table: */
+ while (*ptr != 0)
+ {
+ char *s, *file;
+ size_t file_len, dir_len;
+
+ file = (char *) ptr;
+ ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1;
+ value = read_uleb128 (ptr);
+
+ if (value >= dirt_cnt)
+ {
+ error (0, 0, "%s: Wrong directory table index %u",
+ dso->filename, value);
+ return 1;
+ }
+ file_len = strlen (file);
+ dir_len = strlen ((char *)dirt[value]);
+ s = malloc (comp_dir_len + 1 + file_len + 1 + dir_len + 1);
+ if (s == NULL)
+ {
+ error (0, ENOMEM, "%s: Reading file table", dso->filename);
+ return 1;
+ }
+ if (*file == '/')
+ {
+ memcpy (s, file, file_len + 1);
+ if (dest_dir && has_prefix (file, base_dir))
+ ++abs_file_cnt;
+ }
+ else if (*dirt[value] == '/')
+ {
+ memcpy (s, dirt[value], dir_len);
+ s[dir_len] = '/';
+ memcpy (s + dir_len + 1, file, file_len + 1);
+ }
+ else
+ {
+ char *p = s;
+ if (comp_dir_len != 0)
+ {
+ memcpy (s, comp_dir, comp_dir_len);
+ s[comp_dir_len] = '/';
+ p += comp_dir_len + 1;
+ }
+ memcpy (p, dirt[value], dir_len);
+ p[dir_len] = '/';
+ memcpy (p + dir_len + 1, file, file_len + 1);
+ }
+ canonicalize_path (s, s);
+ if (list_file_fd != -1)
+ {
+ char *p = NULL;
+ if (base_dir == NULL)
+ p = s;
+ else if (has_prefix (s, base_dir))
+ p = s + strlen (base_dir);
+ else if (has_prefix (s, dest_dir))
+ p = s + strlen (dest_dir);
+
+ if (p)
+ {
+ size_t size = strlen (p) + 1;
+ while (size > 0)
+ {
+ ssize_t ret = write (list_file_fd, p, size);
+ if (ret == -1)
+ break;
+ size -= ret;
+ p += ret;
+ }
+ }
+ }
+
+ free (s);
+
+ read_uleb128 (ptr);
+ read_uleb128 (ptr);
+ }
+ ++ptr;
+
+ if (dest_dir)
+ {
+ unsigned char *srcptr, *buf = NULL;
+ size_t base_len = strlen (base_dir);
+ size_t dest_len = strlen (dest_dir);
+ size_t shrank = 0;
+
+ if (dest_len == base_len)
+ abs_file_cnt = 0;
+ if (abs_file_cnt)
+ {
+ srcptr = buf = malloc (ptr - dir);
+ memcpy (srcptr, dir, ptr - dir);
+ ptr = dir;
+ }
+ else
+ ptr = srcptr = dir;
+ while (*srcptr != 0)
+ {
+ size_t len = strlen ((char *)srcptr) + 1;
+ const unsigned char *readptr = srcptr;
+
+ char *orig = strdup ((const char *) srcptr);
+
+ if (*srcptr == '/' && has_prefix ((char *)srcptr, base_dir))
+ {
+ if (dest_len < base_len)
+ ++abs_dir_cnt;
+ memcpy (ptr, dest_dir, dest_len);
+ ptr += dest_len;
+ readptr += base_len;
+ }
+ srcptr += len;
+
+ shrank += srcptr - readptr;
+ canonicalize_path ((char *)readptr, (char *)ptr);
+ len = strlen ((char *)ptr) + 1;
+ shrank -= len;
+ ptr += len;
+
+ if (memcmp (orig, ptr - len, len))
+ dirty_section (DEBUG_STR);
+ free (orig);
+ }
+
+ if (shrank > 0)
+ {
+ if (--shrank == 0)
+ error (EXIT_FAILURE, 0,
+ "canonicalization unexpectedly shrank by one character");
+ else
+ {
+ memset (ptr, 'X', shrank);
+ ptr += shrank;
+ *ptr++ = '\0';
+ }
+ }
+
+ if (abs_dir_cnt + abs_file_cnt != 0)
+ {
+ size_t len = (abs_dir_cnt + abs_file_cnt) * (base_len - dest_len);
+
+ if (len == 1)
+ error (EXIT_FAILURE, 0, "-b arg has to be either the same length as -d arg, or more than 1 char longer");
+ memset (ptr, 'X', len - 1);
+ ptr += len - 1;
+ *ptr++ = '\0';
+ }
+ *ptr++ = '\0';
+ ++srcptr;
+
+ while (*srcptr != 0)
+ {
+ size_t len = strlen ((char *)srcptr) + 1;
+
+ if (*srcptr == '/' && has_prefix ((char *)srcptr, base_dir))
+ {
+ memcpy (ptr, dest_dir, dest_len);
+ if (dest_len < base_len)
+ {
+ memmove (ptr + dest_len, srcptr + base_len,
+ len - base_len);
+ ptr += dest_len - base_len;
+ }
+ dirty_section (DEBUG_STR);
+ }
+ else if (ptr != srcptr)
+ memmove (ptr, srcptr, len);
+ srcptr += len;
+ ptr += len;
+ dir = srcptr;
+ read_uleb128 (srcptr);
+ read_uleb128 (srcptr);
+ read_uleb128 (srcptr);
+ if (ptr != dir)
+ memmove (ptr, dir, srcptr - dir);
+ ptr += srcptr - dir;
+ }
+ *ptr = '\0';
+ free (buf);
+ }
+ return 0;
+}
+
+static unsigned char *
+edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase)
+{
+ int i;
+ uint32_t list_offs;
+ int found_list_offs;
+ char *comp_dir;
+
+ comp_dir = NULL;
+ list_offs = 0;
+ found_list_offs = 0;
+ for (i = 0; i < t->nattr; ++i)
+ {
+ uint32_t form = t->attr[i].form;
+ size_t len = 0;
+ size_t base_len, dest_len;
+
+ while (1)
+ {
+ if (t->attr[i].attr == DW_AT_stmt_list)
+ {
+ if (form == DW_FORM_data4
+ || form == DW_FORM_sec_offset)
+ {
+ list_offs = do_read_32_relocated (ptr);
+ found_list_offs = 1;
+ }
+ }
+
+ if (t->attr[i].attr == DW_AT_comp_dir)
+ {
+ if (form == DW_FORM_string)
+ {
+ free (comp_dir);
+ comp_dir = strdup ((char *)ptr);
+
+ if (phase == 1 && dest_dir && has_prefix ((char *)ptr, base_dir))
+ {
+ base_len = strlen (base_dir);
+ dest_len = strlen (dest_dir);
+
+ memcpy (ptr, dest_dir, dest_len);
+ if (dest_len < base_len)
+ {
+ memset(ptr + dest_len, '/',
+ base_len - dest_len);
+
+ }
+ dirty_section (DEBUG_INFO);
+ }
+ }
+ else if (form == DW_FORM_strp &&
+ debug_sections[DEBUG_STR].data)
+ {
+ char *dir;
+
+ dir = (char *) debug_sections[DEBUG_STR].data
+ + do_read_32_relocated (ptr);
+
+ free (comp_dir);
+ comp_dir = strdup (dir);
+
+ if (phase == 1 && dest_dir && has_prefix (dir, base_dir))
+ {
+ base_len = strlen (base_dir);
+ dest_len = strlen (dest_dir);
+
+ memcpy (dir, dest_dir, dest_len);
+ if (dest_len < base_len)
+ {
+ memmove (dir + dest_len, dir + base_len,
+ strlen (dir + base_len) + 1);
+ }
+ dirty_section (DEBUG_STR);
+ }
+ }
+ }
+ else if ((t->tag == DW_TAG_compile_unit
+ || t->tag == DW_TAG_partial_unit)
+ && t->attr[i].attr == DW_AT_name
+ && form == DW_FORM_strp
+ && debug_sections[DEBUG_STR].data)
+ {
+ char *name;
+
+ name = (char *) debug_sections[DEBUG_STR].data
+ + do_read_32_relocated (ptr);
+ if (*name == '/' && comp_dir == NULL)
+ {
+ char *enddir = strrchr (name, '/');
+
+ if (enddir != name)
+ {
+ comp_dir = malloc (enddir - name + 1);
+ memcpy (comp_dir, name, enddir - name);
+ comp_dir [enddir - name] = '\0';
+ }
+ else
+ comp_dir = strdup ("/");
+ }
+
+ if (phase == 1 && dest_dir && has_prefix (name, base_dir))
+ {
+ base_len = strlen (base_dir);
+ dest_len = strlen (dest_dir);
+
+ memcpy (name, dest_dir, dest_len);
+ if (dest_len < base_len)
+ {
+ memmove (name + dest_len, name + base_len,
+ strlen (name + base_len) + 1);
+ }
+ dirty_section (DEBUG_STR);
+ }
+ }
+
+ switch (form)
+ {
+ case DW_FORM_ref_addr:
+ if (cu_version == 2)
+ ptr += ptr_size;
+ else
+ ptr += 4;
+ break;
+ case DW_FORM_flag_present:
+ break;
+ case DW_FORM_addr:
+ ptr += ptr_size;
+ break;
+ case DW_FORM_ref1:
+ case DW_FORM_flag:
+ case DW_FORM_data1:
+ ++ptr;
+ break;
+ case DW_FORM_ref2:
+ case DW_FORM_data2:
+ ptr += 2;
+ break;
+ case DW_FORM_ref4:
+ case DW_FORM_data4:
+ case DW_FORM_sec_offset:
+ ptr += 4;
+ break;
+ case DW_FORM_ref8:
+ case DW_FORM_data8:
+ case DW_FORM_ref_sig8:
+ ptr += 8;
+ break;
+ case DW_FORM_sdata:
+ case DW_FORM_ref_udata:
+ case DW_FORM_udata:
+ read_uleb128 (ptr);
+ break;
+ case DW_FORM_strp:
+ ptr += 4;
+ break;
+ case DW_FORM_string:
+ ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1;
+ break;
+ case DW_FORM_indirect:
+ form = read_uleb128 (ptr);
+ continue;
+ case DW_FORM_block1:
+ len = *ptr++;
+ break;
+ case DW_FORM_block2:
+ len = read_16 (ptr);
+ form = DW_FORM_block1;
+ break;
+ case DW_FORM_block4:
+ len = read_32 (ptr);
+ form = DW_FORM_block1;
+ break;
+ case DW_FORM_block:
+ case DW_FORM_exprloc:
+ len = read_uleb128 (ptr);
+ form = DW_FORM_block1;
+ assert (len < UINT_MAX);
+ break;
+ default:
+ error (0, 0, "%s: Unknown DWARF DW_FORM_%d", dso->filename,
+ form);
+ return NULL;
+ }
+
+ if (form == DW_FORM_block1)
+ ptr += len;
+
+ break;
+ }
+ }
+
+ /* Ensure the CU current directory will exist even if only empty. Source
+ filenames possibly located in its parent directories refer relatively to
+ it and the debugger (GDB) cannot safely optimize out the missing
+ CU current dir subdirectories. */
+ if (comp_dir && list_file_fd != -1)
+ {
+ char *p;
+ size_t size;
+
+ if (base_dir && has_prefix (comp_dir, base_dir))
+ p = comp_dir + strlen (base_dir);
+ else if (dest_dir && has_prefix (comp_dir, dest_dir))
+ p = comp_dir + strlen (dest_dir);
+ else
+ p = comp_dir;
+
+ size = strlen (p) + 1;
+ while (size > 0)
+ {
+ ssize_t ret = write (list_file_fd, p, size);
+ if (ret == -1)
+ break;
+ size -= ret;
+ p += ret;
+ }
+ }
+
+ if (found_list_offs && comp_dir)
+ edit_dwarf2_line (dso, list_offs, comp_dir, phase);
+
+ free (comp_dir);
+
+ return ptr;
+}
+
+static int
+rel_cmp (const void *a, const void *b)
+{
+ REL *rela = (REL *) a, *relb = (REL *) b;
+
+ if (rela->ptr < relb->ptr)
+ return -1;
+
+ if (rela->ptr > relb->ptr)
+ return 1;
+
+ return 0;
+}
+
+static int
+edit_dwarf2 (DSO *dso)
+{
+ Elf_Data *data;
+ Elf_Scn *scn;
+ int i, j;
+
+ for (i = 0; debug_sections[i].name; ++i)
+ {
+ debug_sections[i].data = NULL;
+ debug_sections[i].size = 0;
+ debug_sections[i].sec = 0;
+ debug_sections[i].relsec = 0;
+ }
+ ptr_size = 0;
+
+ for (i = 1; i < dso->ehdr.e_shnum; ++i)
+ if (! (dso->shdr[i].sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR))
+ && dso->shdr[i].sh_size)
+ {
+ const char *name = strptr (dso, dso->ehdr.e_shstrndx,
+ dso->shdr[i].sh_name);
+
+ if (strncmp (name, ".debug_", sizeof (".debug_") - 1) == 0)
+ {
+ for (j = 0; debug_sections[j].name; ++j)
+ if (strcmp (name, debug_sections[j].name) == 0)
+ {
+ if (debug_sections[j].data)
+ {
+ error (0, 0, "%s: Found two copies of %s section",
+ dso->filename, name);
+ return 1;
+ }
+
+ scn = dso->scn[i];
+ data = elf_rawdata (scn, NULL);
+ assert (data != NULL && data->d_buf != NULL);
+ assert (elf_rawdata (scn, data) == NULL);
+ assert (data->d_off == 0);
+ assert (data->d_size == dso->shdr[i].sh_size);
+ debug_sections[j].data = data->d_buf;
+ debug_sections[j].elf_data = data;
+ debug_sections[j].size = data->d_size;
+ debug_sections[j].sec = i;
+ break;
+ }
+
+ if (debug_sections[j].name == NULL)
+ {
+ error (0, 0, "%s: Unknown debugging section %s",
+ dso->filename, name);
+ }
+ }
+ else if (dso->ehdr.e_type == ET_REL
+ && ((dso->shdr[i].sh_type == SHT_REL
+ && strncmp (name, ".rel.debug_",
+ sizeof (".rel.debug_") - 1) == 0)
+ || (dso->shdr[i].sh_type == SHT_RELA
+ && strncmp (name, ".rela.debug_",
+ sizeof (".rela.debug_") - 1) == 0)))
+ {
+ for (j = 0; debug_sections[j].name; ++j)
+ if (strcmp (name + sizeof (".rel") - 1
+ + (dso->shdr[i].sh_type == SHT_RELA),
+ debug_sections[j].name) == 0)
+ {
+ debug_sections[j].relsec = i;
+ break;
+ }
+ }
+ }
+
+ if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ {
+ do_read_16 = buf_read_ule16;
+ do_read_32 = buf_read_ule32;
+ write_32 = dwarf2_write_le32;
+ }
+ else if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
+ {
+ do_read_16 = buf_read_ube16;
+ do_read_32 = buf_read_ube32;
+ write_32 = dwarf2_write_be32;
+ }
+ else
+ {
+ error (0, 0, "%s: Wrong ELF data enconding", dso->filename);
+ return 1;
+ }
+
+ if (debug_sections[DEBUG_INFO].data != NULL)
+ {
+ unsigned char *ptr, *endcu, *endsec;
+ uint32_t value;
+ htab_t abbrev;
+ struct abbrev_tag tag, *t;
+ int phase;
+ REL *relbuf = NULL;
+
+ if (debug_sections[DEBUG_INFO].relsec)
+ {
+ int ndx, maxndx;
+ GElf_Rel rel;
+ GElf_Rela rela;
+ GElf_Sym sym;
+ GElf_Addr base = dso->shdr[debug_sections[DEBUG_INFO].sec].sh_addr;
+ Elf_Data *symdata = NULL;
+ int rtype;
+
+ i = debug_sections[DEBUG_INFO].relsec;
+ scn = dso->scn[i];
+ data = elf_getdata (scn, NULL);
+ assert (data != NULL && data->d_buf != NULL);
+ assert (elf_getdata (scn, data) == NULL);
+ assert (data->d_off == 0);
+ assert (data->d_size == dso->shdr[i].sh_size);
+ maxndx = dso->shdr[i].sh_size / dso->shdr[i].sh_entsize;
+ relbuf = malloc (maxndx * sizeof (REL));
+ reltype = dso->shdr[i].sh_type;
+ if (relbuf == NULL)
+ error (1, errno, "%s: Could not allocate memory", dso->filename);
+
+ symdata = elf_getdata (dso->scn[dso->shdr[i].sh_link], NULL);
+ assert (symdata != NULL && symdata->d_buf != NULL);
+ assert (elf_getdata (dso->scn[dso->shdr[i].sh_link], symdata)
+ == NULL);
+ assert (symdata->d_off == 0);
+ assert (symdata->d_size
+ == dso->shdr[dso->shdr[i].sh_link].sh_size);
+
+ for (ndx = 0, relend = relbuf; ndx < maxndx; ++ndx)
+ {
+ if (dso->shdr[i].sh_type == SHT_REL)
+ {
+ gelf_getrel (data, ndx, &rel);
+ rela.r_offset = rel.r_offset;
+ rela.r_info = rel.r_info;
+ rela.r_addend = 0;
+ }
+ else
+ gelf_getrela (data, ndx, &rela);
+ gelf_getsym (symdata, ELF64_R_SYM (rela.r_info), &sym);
+ /* Relocations against section symbols are uninteresting
+ in REL. */
+ if (dso->shdr[i].sh_type == SHT_REL && sym.st_value == 0)
+ continue;
+ /* Only consider relocations against .debug_str, .debug_line
+ and .debug_abbrev. */
+ if (sym.st_shndx != debug_sections[DEBUG_STR].sec
+ && sym.st_shndx != debug_sections[DEBUG_LINE].sec
+ && sym.st_shndx != debug_sections[DEBUG_ABBREV].sec)
+ continue;
+ rela.r_addend += sym.st_value;
+ rtype = ELF64_R_TYPE (rela.r_info);
+ switch (dso->ehdr.e_machine)
+ {
+ case EM_SPARC:
+ case EM_SPARC32PLUS:
+ case EM_SPARCV9:
+ if (rtype != R_SPARC_32 && rtype != R_SPARC_UA32)
+ goto fail;
+ break;
+ case EM_386:
+ if (rtype != R_386_32)
+ goto fail;
+ break;
+ case EM_PPC:
+ case EM_PPC64:
+ if (rtype != R_PPC_ADDR32 && rtype != R_PPC_UADDR32)
+ goto fail;
+ break;
+ case EM_S390:
+ if (rtype != R_390_32)
+ goto fail;
+ break;
+ case EM_IA_64:
+ if (rtype != R_IA64_SECREL32LSB)
+ goto fail;
+ break;
+ case EM_X86_64:
+ if (rtype != R_X86_64_32)
+ goto fail;
+ break;
+ case EM_ALPHA:
+ if (rtype != R_ALPHA_REFLONG)
+ goto fail;
+ break;
+ default:
+ fail:
+ error (1, 0, "%s: Unhandled relocation %d in .debug_info section",
+ dso->filename, rtype);
+ }
+ relend->ptr = debug_sections[DEBUG_INFO].data
+ + (rela.r_offset - base);
+ relend->addend = rela.r_addend;
+ ++relend;
+ }
+ if (relbuf == relend)
+ {
+ free (relbuf);
+ relbuf = NULL;
+ relend = NULL;
+ }
+ else
+ qsort (relbuf, relend - relbuf, sizeof (REL), rel_cmp);
+ }
+
+ for (phase = 0; phase < 2; phase++)
+ {
+ ptr = debug_sections[DEBUG_INFO].data;
+ relptr = relbuf;
+ endsec = ptr + debug_sections[DEBUG_INFO].size;
+ while (ptr < endsec)
+ {
+ if (ptr + 11 > endsec)
+ {
+ error (0, 0, "%s: .debug_info CU header too small",
+ dso->filename);
+ return 1;
+ }
+
+ endcu = ptr + 4;
+ endcu += read_32 (ptr);
+ if (endcu == ptr + 0xffffffff)
+ {
+ error (0, 0, "%s: 64-bit DWARF not supported", dso->filename);
+ return 1;
+ }
+
+ if (endcu > endsec)
+ {
+ error (0, 0, "%s: .debug_info too small", dso->filename);
+ return 1;
+ }
+
+ cu_version = read_16 (ptr);
+ if (cu_version != 2 && cu_version != 3 && cu_version != 4)
+ {
+ error (0, 0, "%s: DWARF version %d unhandled", dso->filename,
+ cu_version);
+ return 1;
+ }
+
+ value = read_32_relocated (ptr);
+ if (value >= debug_sections[DEBUG_ABBREV].size)
+ {
+ if (debug_sections[DEBUG_ABBREV].data == NULL)
+ error (0, 0, "%s: .debug_abbrev not present", dso->filename);
+ else
+ error (0, 0, "%s: DWARF CU abbrev offset too large",
+ dso->filename);
+ return 1;
+ }
+
+ if (ptr_size == 0)
+ {
+ ptr_size = read_1 (ptr);
+ if (ptr_size != 4 && ptr_size != 8)
+ {
+ error (0, 0, "%s: Invalid DWARF pointer size %d",
+ dso->filename, ptr_size);
+ return 1;
+ }
+ }
+ else if (read_1 (ptr) != ptr_size)
+ {
+ error (0, 0, "%s: DWARF pointer size differs between CUs",
+ dso->filename);
+ return 1;
+ }
+
+ abbrev = read_abbrev (dso,
+ debug_sections[DEBUG_ABBREV].data + value);
+ if (abbrev == NULL)
+ return 1;
+
+ while (ptr < endcu)
+ {
+ tag.entry = read_uleb128 (ptr);
+ if (tag.entry == 0)
+ continue;
+ t = htab_find_with_hash (abbrev, &tag, tag.entry);
+ if (t == NULL)
+ {
+ error (0, 0, "%s: Could not find DWARF abbreviation %d",
+ dso->filename, tag.entry);
+ htab_delete (abbrev);
+ return 1;
+ }
+
+ ptr = edit_attributes (dso, ptr, t, phase);
+ if (ptr == NULL)
+ break;
+ }
+
+ htab_delete (abbrev);
+ }
+ }
+ free (relbuf);
+ }
+
+ return 0;
+}
+
+static struct poptOption optionsTable[] = {
+ { "base-dir", 'b', POPT_ARG_STRING, &base_dir, 0,
+ "base build directory of objects", NULL },
+ { "dest-dir", 'd', POPT_ARG_STRING, &dest_dir, 0,
+ "directory to rewrite base-dir into", NULL },
+ { "list-file", 'l', POPT_ARG_STRING, &list_file, 0,
+ "file where to put list of source and header file names", NULL },
+ { "build-id", 'i', POPT_ARG_NONE, &do_build_id, 0,
+ "recompute build ID note and print ID on stdout", NULL },
+ POPT_AUTOHELP
+ { NULL, 0, 0, NULL, 0, NULL, NULL }
+};
+
+static DSO *
+fdopen_dso (int fd, const char *name)
+{
+ Elf *elf = NULL;
+ GElf_Ehdr ehdr;
+ int i;
+ DSO *dso = NULL;
+
+ elf = elf_begin (fd, ELF_C_RDWR_MMAP, NULL);
+ if (elf == NULL)
+ {
+ error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1));
+ goto error_out;
+ }
+
+ if (elf_kind (elf) != ELF_K_ELF)
+ {
+ error (0, 0, "\"%s\" is not an ELF file", name);
+ goto error_out;
+ }
+
+ if (gelf_getehdr (elf, &ehdr) == NULL)
+ {
+ error (0, 0, "cannot get the ELF header: %s",
+ elf_errmsg (-1));
+ goto error_out;
+ }
+
+ if (ehdr.e_type != ET_DYN && ehdr.e_type != ET_EXEC && ehdr.e_type != ET_REL)
+ {
+ error (0, 0, "\"%s\" is not a shared library", name);
+ goto error_out;
+ }
+
+ /* Allocate DSO structure. Leave place for additional 20 new section
+ headers. */
+ dso = (DSO *)
+ malloc (sizeof(DSO) + (ehdr.e_shnum + 20) * sizeof(GElf_Shdr)
+ + (ehdr.e_shnum + 20) * sizeof(Elf_Scn *));
+ if (!dso)
+ {
+ error (0, ENOMEM, "Could not open DSO");
+ goto error_out;
+ }
+
+ elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT);
+
+ memset (dso, 0, sizeof(DSO));
+ dso->elf = elf;
+ dso->ehdr = ehdr;
+ dso->scn = (Elf_Scn **) &dso->shdr[ehdr.e_shnum + 20];
+
+ for (i = 0; i < ehdr.e_shnum; ++i)
+ {
+ dso->scn[i] = elf_getscn (elf, i);
+ gelf_getshdr (dso->scn[i], dso->shdr + i);
+ }
+
+ dso->filename = (const char *) strdup (name);
+ return dso;
+
+error_out:
+ if (dso)
+ {
+ free ((char *) dso->filename);
+ free (dso);
+ }
+ if (elf)
+ elf_end (elf);
+ if (fd != -1)
+ close (fd);
+ return NULL;
+}
+
+static const pgpHashAlgo algorithms[] = { PGPHASHALGO_MD5,
+ PGPHASHALGO_SHA1, PGPHASHALGO_SHA256, PGPHASHALGO_SHA384, PGPHASHALGO_SHA512 };
+
+/* Compute a fresh build ID bit-string from the editted file contents. */
+static void
+handle_build_id (DSO *dso, Elf_Data *build_id,
+ size_t build_id_offset, size_t build_id_size)
+{
+ DIGEST_CTX ctx;
+ pgpHashAlgo algorithm;
+ int i = sizeof(algorithms)/sizeof(algorithms[0]);
+ void *digest = NULL;
+ size_t len;
+
+ while (i-- > 0)
+ {
+ algorithm = algorithms[i];
+ if (rpmDigestLength(algorithm) == build_id_size)
+ break;
+ }
+ if (i < 0)
+ {
+ fprintf (stderr, "Cannot handle %Zu-byte build ID\n", build_id_size);
+ exit (1);
+ }
+
+ if (!dirty_elf)
+ goto print;
+
+ if (elf_update (dso->elf, ELF_C_NULL) < 0)
+ {
+ fprintf (stderr, "Failed to update file: %s\n",
+ elf_errmsg (elf_errno ()));
+ exit (1);
+ }
+
+ /* Clear the old bits so they do not affect the new hash. */
+ memset ((char *) build_id->d_buf + build_id_offset, 0, build_id_size);
+
+ ctx = rpmDigestInit(algorithm, 0);
+
+ /* Slurp the relevant header bits and section contents and feed them
+ into the hash function. The only bits we ignore are the offset
+ fields in ehdr and shdrs, since the semantically identical ELF file
+ could be written differently if it doesn't change the phdr layout.
+ We always use the GElf (i.e. Elf64) formats for the bits to hash
+ since it is convenient. It doesn't matter whether this is an Elf32
+ or Elf64 object, only that we are consistent in what bits feed the
+ hash so it comes out the same for the same file contents. */
+ {
+ union
+ {
+ GElf_Ehdr ehdr;
+ GElf_Phdr phdr;
+ GElf_Shdr shdr;
+ } u;
+ Elf_Data x = { .d_version = EV_CURRENT, .d_buf = &u };
+
+ x.d_type = ELF_T_EHDR;
+ x.d_size = sizeof u.ehdr;
+ u.ehdr = dso->ehdr;
+ u.ehdr.e_phoff = u.ehdr.e_shoff = 0;
+ if (elf64_xlatetom (&x, &x, dso->ehdr.e_ident[EI_DATA]) == NULL)
+ {
+ bad:
+ fprintf (stderr, "Failed to compute header checksum: %s\n",
+ elf_errmsg (elf_errno ()));
+ exit (1);
+ }
+
+ x.d_type = ELF_T_PHDR;
+ x.d_size = sizeof u.phdr;
+ for (i = 0; i < dso->ehdr.e_phnum; ++i)
+ {
+ if (gelf_getphdr (dso->elf, i, &u.phdr) == NULL)
+ goto bad;
+ if (elf64_xlatetom (&x, &x, dso->ehdr.e_ident[EI_DATA]) == NULL)
+ goto bad;
+ rpmDigestUpdate(ctx, x.d_buf, x.d_size);
+ }
+
+ x.d_type = ELF_T_SHDR;
+ x.d_size = sizeof u.shdr;
+ for (i = 0; i < dso->ehdr.e_shnum; ++i)
+ if (dso->scn[i] != NULL)
+ {
+ u.shdr = dso->shdr[i];
+ u.shdr.sh_offset = 0;
+ if (elf64_xlatetom (&x, &x, dso->ehdr.e_ident[EI_DATA]) == NULL)
+ goto bad;
+ rpmDigestUpdate(ctx, x.d_buf, x.d_size);
+
+ if (u.shdr.sh_type != SHT_NOBITS)
+ {
+ Elf_Data *d = elf_rawdata (dso->scn[i], NULL);
+ if (d == NULL)
+ goto bad;
+ rpmDigestUpdate(ctx, d->d_buf, d->d_size);
+ }
+ }
+ }
+
+ rpmDigestFinal(ctx, &digest, &len, 0);
+ memcpy((unsigned char *)build_id->d_buf + build_id_offset, digest, build_id_size);
+ free(digest);
+
+ elf_flagdata (build_id, ELF_C_SET, ELF_F_DIRTY);
+
+ print:
+ /* Now format the build ID bits in hex to print out. */
+ {
+ const uint8_t * id = (uint8_t *)build_id->d_buf + build_id_offset;
+ char *hex = pgpHexStr(id, build_id_size);
+ puts (hex);
+ free(hex);
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ DSO *dso;
+ int fd, i;
+ const char *file;
+ poptContext optCon; /* context for parsing command-line options */
+ int nextopt;
+ const char **args;
+ struct stat stat_buf;
+ char *p;
+ Elf_Data *build_id = NULL;
+ size_t build_id_offset = 0, build_id_size = 0;
+
+ optCon = poptGetContext("debugedit", argc, (const char **)argv, optionsTable, 0);
+
+ while ((nextopt = poptGetNextOpt (optCon)) > 0 || nextopt == POPT_ERROR_BADOPT)
+ /* do nothing */ ;
+
+ if (nextopt != -1)
+ {
+ fprintf (stderr, "Error on option %s: %s.\nRun '%s --help' to see a full list of available command line options.\n",
+ poptBadOption (optCon, 0),
+ poptStrerror (nextopt),
+ argv[0]);
+ exit (1);
+ }
+
+ args = poptGetArgs (optCon);
+ if (args == NULL || args[0] == NULL || args[1] != NULL)
+ {
+ poptPrintHelp(optCon, stdout, 0);
+ exit (1);
+ }
+
+ if (dest_dir != NULL)
+ {
+ if (base_dir == NULL)
+ {
+ fprintf (stderr, "You must specify a base dir if you specify a dest dir\n");
+ exit (1);
+ }
+ if (strlen (dest_dir) > strlen (base_dir))
+ {
+ fprintf (stderr, "Dest dir longer than base dir is not supported\n");
+ exit (1);
+ }
+ }
+
+ /* Make sure there are trailing slashes in dirs */
+ if (base_dir != NULL && base_dir[strlen (base_dir)-1] != '/')
+ {
+ p = malloc (strlen (base_dir) + 2);
+ strcpy (p, base_dir);
+ strcat (p, "/");
+ free (base_dir);
+ base_dir = p;
+ }
+ if (dest_dir != NULL && dest_dir[strlen (dest_dir)-1] != '/')
+ {
+ p = malloc (strlen (dest_dir) + 2);
+ strcpy (p, dest_dir);
+ strcat (p, "/");
+ free (dest_dir);
+ dest_dir = p;
+ }
+
+ if (list_file != NULL)
+ {
+ list_file_fd = open (list_file, O_WRONLY|O_CREAT|O_APPEND, 0644);
+ }
+
+ file = args[0];
+
+ if (elf_version(EV_CURRENT) == EV_NONE)
+ {
+ fprintf (stderr, "library out of date\n");
+ exit (1);
+ }
+
+ if (stat(file, &stat_buf) < 0)
+ {
+ fprintf (stderr, "Failed to open input file '%s': %s\n", file, strerror(errno));
+ exit (1);
+ }
+
+ /* Make sure we can read and write */
+ chmod (file, stat_buf.st_mode | S_IRUSR | S_IWUSR);
+
+ fd = open (file, O_RDWR);
+ if (fd < 0)
+ {
+ fprintf (stderr, "Failed to open input file '%s': %s\n", file, strerror(errno));
+ exit (1);
+ }
+
+ dso = fdopen_dso (fd, file);
+ if (dso == NULL)
+ exit (1);
+
+ for (i = 1; i < dso->ehdr.e_shnum; i++)
+ {
+ const char *name;
+
+ switch (dso->shdr[i].sh_type)
+ {
+ case SHT_PROGBITS:
+ name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[i].sh_name);
+ /* TODO: Handle stabs */
+ if (strcmp (name, ".stab") == 0)
+ {
+ fprintf (stderr, "Stabs debuginfo not supported: %s\n", file);
+ exit (1);
+ }
+ if (strcmp (name, ".debug_info") == 0)
+ edit_dwarf2 (dso);
+
+ break;
+ case SHT_NOTE:
+ if (do_build_id
+ && build_id == NULL && (dso->shdr[i].sh_flags & SHF_ALLOC))
+ {
+ /* Look for a build-ID note here. */
+ Elf_Data *data = elf_rawdata (elf_getscn (dso->elf, i), NULL);
+ Elf32_Nhdr nh;
+ Elf_Data dst =
+ {
+ .d_version = EV_CURRENT, .d_type = ELF_T_NHDR,
+ .d_buf = &nh, .d_size = sizeof nh
+ };
+ Elf_Data src = dst;
+ src.d_buf = data->d_buf;
+ assert (sizeof (Elf32_Nhdr) == sizeof (Elf64_Nhdr));
+ while ((char *) data->d_buf + data->d_size -
+ (char *) src.d_buf > (int) sizeof nh
+ && elf32_xlatetom (&dst, &src, dso->ehdr.e_ident[EI_DATA]))
+ {
+ Elf32_Word len = sizeof nh + nh.n_namesz;
+ len = (len + 3) & ~3;
+
+ if (nh.n_namesz == sizeof "GNU" && nh.n_type == 3
+ && !memcmp ((char *) src.d_buf + sizeof nh, "GNU", sizeof "GNU"))
+ {
+ build_id = data;
+ build_id_offset = (char *) src.d_buf + len -
+ (char *) data->d_buf;
+ build_id_size = nh.n_descsz;
+ break;
+ }
+
+ len += nh.n_descsz;
+ len = (len + 3) & ~3;
+ src.d_buf = (char *) src.d_buf + len;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (do_build_id && build_id != NULL)
+ handle_build_id (dso, build_id, build_id_offset, build_id_size);
+
+ if (elf_update (dso->elf, ELF_C_WRITE) < 0)
+ {
+ fprintf (stderr, "Failed to write file: %s\n", elf_errmsg (elf_errno()));
+ exit (1);
+ }
+ if (elf_end (dso->elf) < 0)
+ {
+ fprintf (stderr, "elf_end failed: %s\n", elf_errmsg (elf_errno()));
+ exit (1);
+ }
+ close (fd);
+
+ /* Restore old access rights */
+ chmod (file, stat_buf.st_mode);
+
+ poptFreeContext (optCon);
+
+ return 0;
+}
diff --git a/tools/elfdeps.c b/tools/elfdeps.c
new file mode 100644
index 0000000..b523698
--- /dev/null
+++ b/tools/elfdeps.c
@@ -0,0 +1,299 @@
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <error.h>
+#include <errno.h>
+#include <popt.h>
+#include <gelf.h>
+
+#include <rpm/rpmstring.h>
+#include <rpm/argv.h>
+
+int filter_private = 0;
+int soname_only = 0;
+
+typedef struct elfInfo_s {
+ Elf *elf;
+
+ int isDSO;
+ int isElf64; /* is 64bit marker needed in dependencies */
+ int isExec; /* requires are only added to executables */
+ int gotDEBUG;
+ int gotHASH;
+ int gotGNUHASH;
+ int gotSONAME;
+
+ ARGV_t requires;
+ ARGV_t provides;
+} elfInfo;
+
+static int skipPrivate(const char *s)
+{
+ return (filter_private && rstreq(s, "GLIBC_PRIVATE"));
+}
+
+static void processVerDef(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
+{
+ Elf_Data *data = NULL;
+ unsigned int offset, auxoffset;
+ char *soname = NULL;
+
+ while ((data = elf_getdata(scn, data)) != NULL) {
+ offset = 0;
+
+ for (int i = shdr->sh_info; --i >= 0; ) {
+ GElf_Verdef def_mem, *def;
+ def = gelf_getverdef (data, offset, &def_mem);
+ if (def == NULL)
+ break;
+ auxoffset = offset + def->vd_aux;
+ offset += def->vd_next;
+
+ for (int j = def->vd_cnt; --j >= 0; ) {
+ GElf_Verdaux aux_mem, * aux;
+ const char *s;
+ aux = gelf_getverdaux (data, auxoffset, &aux_mem);
+ if (aux == NULL)
+ break;
+ s = elf_strptr(ei->elf, shdr->sh_link, aux->vda_name);
+ if (s == NULL)
+ break;
+ if (def->vd_flags & VER_FLG_BASE) {
+ rfree(soname);
+ soname = rstrdup(s);
+ auxoffset += aux->vda_next;
+ continue;
+ } else if (soname && !soname_only && !skipPrivate(s)) {
+ const char *marker = ei->isElf64 ? "(64bit)" : "";
+ char *dep = NULL;
+ rasprintf(&dep, "%s(%s)%s", soname, s, marker);
+ argvAdd(&ei->provides, dep);
+ rfree(dep);
+ }
+ }
+
+ }
+ }
+ rfree(soname);
+}
+
+static void processVerNeed(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
+{
+ Elf_Data *data = NULL;
+ char *soname = NULL;
+ while ((data = elf_getdata(scn, data)) != NULL) {
+ unsigned int offset = 0, auxoffset;
+ for (int i = shdr->sh_info; --i >= 0; ) {
+ const char *s = NULL;
+ GElf_Verneed need_mem, *need;
+ need = gelf_getverneed (data, offset, &need_mem);
+ if (need == NULL)
+ break;
+
+ s = elf_strptr(ei->elf, shdr->sh_link, need->vn_file);
+ if (s == NULL)
+ break;
+ rfree(soname);
+ soname = rstrdup(s);
+ auxoffset = offset + need->vn_aux;
+
+ for (int j = need->vn_cnt; --j >= 0; ) {
+ GElf_Vernaux aux_mem, * aux;
+ aux = gelf_getvernaux (data, auxoffset, &aux_mem);
+ if (aux == NULL)
+ break;
+ s = elf_strptr(ei->elf, shdr->sh_link, aux->vna_name);
+ if (s == NULL)
+ break;
+
+ if (ei->isExec && soname && !soname_only && !skipPrivate(s)) {
+ const char *marker = ei->isElf64 ? "(64bit)" : "";
+ char *dep = NULL;
+ rasprintf(&dep, "%s(%s)%s", soname, s, marker);
+ argvAdd(&ei->requires, dep);
+ rfree(dep);
+ }
+ auxoffset += aux->vna_next;
+ }
+ offset += need->vn_next;
+ }
+ }
+ rfree(soname);
+}
+
+static void processDynamic(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
+{
+ Elf_Data *data = NULL;
+ while ((data = elf_getdata(scn, data)) != NULL) {
+ for (int i = 0; i < (shdr->sh_size / shdr->sh_entsize); i++) {
+ ARGV_t *deptype = NULL;
+ const char *s = NULL;
+ GElf_Dyn dyn_mem, *dyn;
+
+ dyn = gelf_getdyn (data, i, &dyn_mem);
+ if (dyn == NULL)
+ break;
+
+ switch (dyn->d_tag) {
+ case DT_HASH:
+ ei->gotHASH = 1;
+ break;
+ case DT_GNU_HASH:
+ ei->gotGNUHASH = 1;
+ break;
+ case DT_DEBUG:
+ ei->gotDEBUG = 1;
+ break;
+ case DT_SONAME:
+ ei->gotSONAME = 1;
+ s = elf_strptr(ei->elf, shdr->sh_link, dyn->d_un.d_val);
+ deptype = &ei->provides;
+ break;
+ case DT_NEEDED:
+ if (ei->isExec) {
+ s = elf_strptr(ei->elf, shdr->sh_link, dyn->d_un.d_val);
+ deptype = &ei->requires;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (s && deptype) {
+ const char *marker = ei->isElf64 ? "()(64bit)" : "";
+ char *dep = rstrscat(NULL, s, marker, NULL);
+ argvAdd(deptype, dep);
+ rfree(dep);
+ }
+ }
+ }
+}
+
+static void processSections(elfInfo *ei)
+{
+ Elf_Scn * scn = NULL;
+ while ((scn = elf_nextscn(ei->elf, scn)) != NULL) {
+ GElf_Shdr shdr_mem, *shdr;
+ shdr = gelf_getshdr(scn, &shdr_mem);
+ if (shdr == NULL)
+ break;
+
+ switch (shdr->sh_type) {
+ case SHT_GNU_verdef:
+ processVerDef(scn, shdr, ei);
+ break;
+ case SHT_GNU_verneed:
+ processVerNeed(scn, shdr, ei);
+ break;
+ case SHT_DYNAMIC:
+ processDynamic(scn, shdr, ei);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static int processFile(const char *fn, int dtype)
+{
+ int rc = 1;
+ int fdno = -1;
+ struct stat st;
+ GElf_Ehdr *ehdr, ehdr_mem;
+ elfInfo *ei = rcalloc(1, sizeof(*ei));
+
+ fdno = open(fn, O_RDONLY);
+ if (fdno < 0 || fstat(fdno, &st) < 0)
+ goto exit;
+
+ (void) elf_version(EV_CURRENT);
+ ei->elf = elf_begin(fdno, ELF_C_READ, NULL);
+ if (ei->elf == NULL || elf_kind(ei->elf) != ELF_K_ELF)
+ goto exit;
+
+ ehdr = gelf_getehdr(ei->elf, &ehdr_mem);
+ if (ehdr == NULL)
+ goto exit;
+
+ if (ehdr->e_type == ET_DYN || ehdr->e_type == ET_EXEC) {
+/* on alpha, everything is 64bit but we dont want the (64bit) markers */
+#if !defined(__alpha__)
+ ei->isElf64 = (ehdr->e_ident[EI_CLASS] == ELFCLASS64);
+#else
+ ei->isElf64 = 0;
+#endif
+ ei->isDSO = (ehdr->e_type == ET_DYN);
+ ei->isExec = (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH));
+
+ processSections(ei);
+ }
+
+ /*
+ * For DSOs which use the .gnu_hash section and don't have a .hash
+ * section, we need to ensure that we have a new enough glibc.
+ */
+ if (ei->isExec && ei->gotGNUHASH && !ei->gotHASH && !soname_only) {
+ argvAdd(&ei->requires, "rtld(GNU_HASH)");
+ }
+
+ /* For DSO's, provide the basename of the file if DT_SONAME not found. */
+ if (ei->isDSO && !ei->gotDEBUG && !ei->gotSONAME) {
+ const char *marker = ei->isElf64 ? "()(64bit)" : "";
+ const char *bn = strrchr(fn, '/');
+ char *dep;
+ bn = bn ? bn + 1 : fn;
+ dep = rstrscat(NULL, bn, marker, NULL);
+ argvAdd(&ei->provides, dep);
+ rfree(dep);
+ }
+
+ rc = 0;
+ /* dump the requested dependencies for this file */
+ for (ARGV_t dep = dtype ? ei->requires : ei->provides; dep && *dep; dep++) {
+ fprintf(stdout, "%s\n", *dep);
+ }
+
+exit:
+ if (fdno >= 0) close(fdno);
+ if (ei) {
+ argvFree(ei->provides);
+ argvFree(ei->requires);
+ if (ei->elf) elf_end(ei->elf);
+ rfree(ei);
+ }
+ return rc;
+}
+
+int main(int argc, char *argv[])
+{
+ char fn[BUFSIZ];
+ int provides = 0;
+ int requires = 0;
+ poptContext optCon;
+
+ struct poptOption opts[] = {
+ { "provides", 'P', POPT_ARG_VAL, &provides, -1, NULL, NULL },
+ { "requires", 'R', POPT_ARG_VAL, &requires, -1, NULL, NULL },
+ { "filter-private", 0, POPT_ARG_VAL, &filter_private, -1, NULL, NULL },
+ { "soname-only", 0, POPT_ARG_VAL, &soname_only, -1, NULL, NULL },
+ POPT_AUTOHELP
+ POPT_TABLEEND
+ };
+
+ optCon = poptGetContext(argv[0], argc, (const char **) argv, opts, 0);
+ if (argc < 2 || poptGetNextOpt(optCon) == 0) {
+ poptPrintUsage(optCon, stderr, 0);
+ exit(EXIT_FAILURE);
+ }
+
+ while (fgets(fn, sizeof(fn), stdin) != NULL) {
+ fn[strlen(fn)-1] = '\0';
+ (void) processFile(fn, requires);
+ }
+
+ poptFreeContext(optCon);
+ return 0;
+}
diff --git a/tools/hashtab.c b/tools/hashtab.c
new file mode 100644
index 0000000..7bd576d
--- /dev/null
+++ b/tools/hashtab.c
@@ -0,0 +1,523 @@
+/* An expandable hash tables datatype.
+ Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+ Contributed by Vladimir Makarov (vmakarov@cygnus.com).
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty 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 Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+/* This package implements basic hash table functionality. It is possible
+ to search for an entry, create an entry and destroy an entry.
+
+ Elements in the table are generic pointers.
+
+ The size of the table is not fixed; if the occupancy of the table
+ grows too high the hash table will be expanded.
+
+ The abstract data implementation is based on generalized Algorithm D
+ from Knuth's book "The art of computer programming". Hash table is
+ expanded by creation of new hash table and transferring elements from
+ the old table to the new table. */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "tools/hashtab.h"
+
+/* This macro defines reserved value for empty table entry. */
+
+#define EMPTY_ENTRY ((void *) 0)
+
+/* This macro defines reserved value for table entry which contained
+ a deleted element. */
+
+#define DELETED_ENTRY ((void *) 1)
+
+static unsigned long higher_prime_number (unsigned long);
+static hashval_t hash_pointer (const void *);
+static int eq_pointer (const void *, const void *);
+static int htab_expand (htab_t);
+static void **find_empty_slot_for_expand (htab_t, hashval_t);
+
+/* At some point, we could make these be NULL, and modify the
+ hash-table routines to handle NULL specially; that would avoid
+ function-call overhead for the common case of hashing pointers. */
+htab_hash htab_hash_pointer = hash_pointer;
+htab_eq htab_eq_pointer = eq_pointer;
+
+/* The following function returns a nearest prime number which is
+ greater than N, and near a power of two. */
+
+static unsigned long
+higher_prime_number (n)
+ unsigned long n;
+{
+ /* These are primes that are near, but slightly smaller than, a
+ power of two. */
+ static unsigned long primes[] = {
+ (unsigned long) 2,
+ (unsigned long) 7,
+ (unsigned long) 13,
+ (unsigned long) 31,
+ (unsigned long) 61,
+ (unsigned long) 127,
+ (unsigned long) 251,
+ (unsigned long) 509,
+ (unsigned long) 1021,
+ (unsigned long) 2039,
+ (unsigned long) 4093,
+ (unsigned long) 8191,
+ (unsigned long) 16381,
+ (unsigned long) 32749,
+ (unsigned long) 65521,
+ (unsigned long) 131071,
+ (unsigned long) 262139,
+ (unsigned long) 524287,
+ (unsigned long) 1048573,
+ (unsigned long) 2097143,
+ (unsigned long) 4194301,
+ (unsigned long) 8388593,
+ (unsigned long) 16777213,
+ (unsigned long) 33554393,
+ (unsigned long) 67108859,
+ (unsigned long) 134217689,
+ (unsigned long) 268435399,
+ (unsigned long) 536870909,
+ (unsigned long) 1073741789,
+ (unsigned long) 2147483647,
+ /* 4294967291L */
+ ((unsigned long) 2147483647) + ((unsigned long) 2147483644),
+ };
+
+ unsigned long* low = &primes[0];
+ unsigned long* high = &primes[sizeof(primes) / sizeof(primes[0])];
+
+ while (low != high)
+ {
+ unsigned long* mid = low + (high - low) / 2;
+ if (n > *mid)
+ low = mid + 1;
+ else
+ high = mid;
+ }
+
+ /* If we've run out of primes, abort. */
+ if (n > *low)
+ {
+ fprintf (stderr, "Cannot find prime bigger than %lu\n", n);
+ abort ();
+ }
+
+ return *low;
+}
+
+/* Returns a hash code for P. */
+
+static hashval_t
+hash_pointer (p)
+ const void * p;
+{
+ return (hashval_t) ((long)p >> 3);
+}
+
+/* Returns non-zero if P1 and P2 are equal. */
+
+static int
+eq_pointer (p1, p2)
+ const void * p1;
+ const void * p2;
+{
+ return p1 == p2;
+}
+
+/* This function creates table with length slightly longer than given
+ source length. The created hash table is initiated as empty (all the
+ hash table entries are EMPTY_ENTRY). The function returns the created
+ hash table. Memory allocation may fail; it may return NULL. */
+
+htab_t
+htab_try_create (size, hash_f, eq_f, del_f)
+ size_t size;
+ htab_hash hash_f;
+ htab_eq eq_f;
+ htab_del del_f;
+{
+ htab_t result;
+
+ size = higher_prime_number (size);
+ result = (htab_t) calloc (1, sizeof (struct htab));
+ if (result == NULL)
+ return NULL;
+
+ result->entries = (void **) calloc (size, sizeof (void *));
+ if (result->entries == NULL)
+ {
+ free (result);
+ return NULL;
+ }
+
+ result->size = size;
+ result->hash_f = hash_f;
+ result->eq_f = eq_f;
+ result->del_f = del_f;
+ result->return_allocation_failure = 1;
+ return result;
+}
+
+/* This function frees all memory allocated for given hash table.
+ Naturally the hash table must already exist. */
+
+void
+htab_delete (htab)
+ htab_t htab;
+{
+ int i;
+
+ if (htab->del_f)
+ for (i = htab->size - 1; i >= 0; i--)
+ if (htab->entries[i] != EMPTY_ENTRY
+ && htab->entries[i] != DELETED_ENTRY)
+ (*htab->del_f) (htab->entries[i]);
+
+ free (htab->entries);
+ free (htab);
+}
+
+/* This function clears all entries in the given hash table. */
+
+void
+htab_empty (htab)
+ htab_t htab;
+{
+ int i;
+
+ if (htab->del_f)
+ for (i = htab->size - 1; i >= 0; i--)
+ if (htab->entries[i] != EMPTY_ENTRY
+ && htab->entries[i] != DELETED_ENTRY)
+ (*htab->del_f) (htab->entries[i]);
+
+ memset (htab->entries, 0, htab->size * sizeof (void *));
+}
+
+/* Similar to htab_find_slot, but without several unwanted side effects:
+ - Does not call htab->eq_f when it finds an existing entry.
+ - Does not change the count of elements/searches/collisions in the
+ hash table.
+ This function also assumes there are no deleted entries in the table.
+ HASH is the hash value for the element to be inserted. */
+
+static void **
+find_empty_slot_for_expand (htab, hash)
+ htab_t htab;
+ hashval_t hash;
+{
+ size_t size = htab->size;
+ hashval_t hash2 = 1 + hash % (size - 2);
+ unsigned int index = hash % size;
+
+ for (;;)
+ {
+ void **slot = htab->entries + index;
+
+ if (*slot == EMPTY_ENTRY)
+ return slot;
+ else if (*slot == DELETED_ENTRY)
+ abort ();
+
+ index += hash2;
+ if (index >= size)
+ index -= size;
+ }
+}
+
+/* The following function changes size of memory allocated for the
+ entries and repeatedly inserts the table elements. The occupancy
+ of the table after the call will be about 50%. Naturally the hash
+ table must already exist. Remember also that the place of the
+ table entries is changed. If memory allocation failures are allowed,
+ this function will return zero, indicating that the table could not be
+ expanded. If all goes well, it will return a non-zero value. */
+
+static int
+htab_expand (htab)
+ htab_t htab;
+{
+ void **oentries;
+ void **olimit;
+ void **p;
+
+ oentries = htab->entries;
+ olimit = oentries + htab->size;
+
+ htab->size = higher_prime_number (htab->size * 2);
+
+ if (htab->return_allocation_failure)
+ {
+ void **nentries = (void **) calloc (htab->size, sizeof (void **));
+ if (nentries == NULL)
+ return 0;
+ htab->entries = nentries;
+ }
+
+ htab->n_elements -= htab->n_deleted;
+ htab->n_deleted = 0;
+
+ p = oentries;
+ do
+ {
+ void * x = *p;
+
+ if (x != EMPTY_ENTRY && x != DELETED_ENTRY)
+ {
+ void **q = find_empty_slot_for_expand (htab, (*htab->hash_f) (x));
+
+ *q = x;
+ }
+
+ p++;
+ }
+ while (p < olimit);
+
+ free (oentries);
+ return 1;
+}
+
+/* This function searches for a hash table entry equal to the given
+ element. It cannot be used to insert or delete an element. */
+
+void *
+htab_find_with_hash (htab, element, hash)
+ htab_t htab;
+ const void * element;
+ hashval_t hash;
+{
+ unsigned int index;
+ hashval_t hash2;
+ size_t size;
+ void * entry;
+
+ htab->searches++;
+ size = htab->size;
+ index = hash % size;
+
+ entry = htab->entries[index];
+ if (entry == EMPTY_ENTRY
+ || (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element)))
+ return entry;
+
+ hash2 = 1 + hash % (size - 2);
+
+ for (;;)
+ {
+ htab->collisions++;
+ index += hash2;
+ if (index >= size)
+ index -= size;
+
+ entry = htab->entries[index];
+ if (entry == EMPTY_ENTRY
+ || (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element)))
+ return entry;
+ }
+}
+
+/* Like htab_find_slot_with_hash, but compute the hash value from the
+ element. */
+
+void *
+htab_find (htab, element)
+ htab_t htab;
+ const void * element;
+{
+ return htab_find_with_hash (htab, element, (*htab->hash_f) (element));
+}
+
+/* This function searches for a hash table slot containing an entry
+ equal to the given element. To delete an entry, call this with
+ INSERT = 0, then call htab_clear_slot on the slot returned (possibly
+ after doing some checks). To insert an entry, call this with
+ INSERT = 1, then write the value you want into the returned slot.
+ When inserting an entry, NULL may be returned if memory allocation
+ fails. */
+
+void **
+htab_find_slot_with_hash (htab, element, hash, insert)
+ htab_t htab;
+ const void * element;
+ hashval_t hash;
+ enum insert_option insert;
+{
+ void **first_deleted_slot;
+ unsigned int index;
+ hashval_t hash2;
+ size_t size;
+
+ if (insert == INSERT && htab->size * 3 <= htab->n_elements * 4
+ && htab_expand (htab) == 0)
+ return NULL;
+
+ size = htab->size;
+ hash2 = 1 + hash % (size - 2);
+ index = hash % size;
+
+ htab->searches++;
+ first_deleted_slot = NULL;
+
+ for (;;)
+ {
+ void * entry = htab->entries[index];
+ if (entry == EMPTY_ENTRY)
+ {
+ if (insert == NO_INSERT)
+ return NULL;
+
+ htab->n_elements++;
+
+ if (first_deleted_slot)
+ {
+ *first_deleted_slot = EMPTY_ENTRY;
+ return first_deleted_slot;
+ }
+
+ return &htab->entries[index];
+ }
+
+ if (entry == DELETED_ENTRY)
+ {
+ if (!first_deleted_slot)
+ first_deleted_slot = &htab->entries[index];
+ }
+ else if ((*htab->eq_f) (entry, element))
+ return &htab->entries[index];
+
+ htab->collisions++;
+ index += hash2;
+ if (index >= size)
+ index -= size;
+ }
+}
+
+/* Like htab_find_slot_with_hash, but compute the hash value from the
+ element. */
+
+void **
+htab_find_slot (htab, element, insert)
+ htab_t htab;
+ const void * element;
+ enum insert_option insert;
+{
+ return htab_find_slot_with_hash (htab, element, (*htab->hash_f) (element),
+ insert);
+}
+
+/* This function deletes an element with the given value from hash
+ table. If there is no matching element in the hash table, this
+ function does nothing. */
+
+void
+htab_remove_elt (htab, element)
+ htab_t htab;
+ void * element;
+{
+ void **slot;
+
+ slot = htab_find_slot (htab, element, NO_INSERT);
+ if (*slot == EMPTY_ENTRY)
+ return;
+
+ if (htab->del_f)
+ (*htab->del_f) (*slot);
+
+ *slot = DELETED_ENTRY;
+ htab->n_deleted++;
+}
+
+/* This function clears a specified slot in a hash table. It is
+ useful when you've already done the lookup and don't want to do it
+ again. */
+
+void
+htab_clear_slot (htab, slot)
+ htab_t htab;
+ void **slot;
+{
+ if (slot < htab->entries || slot >= htab->entries + htab->size
+ || *slot == EMPTY_ENTRY || *slot == DELETED_ENTRY)
+ abort ();
+
+ if (htab->del_f)
+ (*htab->del_f) (*slot);
+
+ *slot = DELETED_ENTRY;
+ htab->n_deleted++;
+}
+
+/* This function scans over the entire hash table calling
+ CALLBACK for each live entry. If CALLBACK returns false,
+ the iteration stops. INFO is passed as CALLBACK's second
+ argument. */
+
+void
+htab_traverse (htab, callback, info)
+ htab_t htab;
+ htab_trav callback;
+ void * info;
+{
+ void **slot = htab->entries;
+ void **limit = slot + htab->size;
+
+ do
+ {
+ void * x = *slot;
+
+ if (x != EMPTY_ENTRY && x != DELETED_ENTRY)
+ if (!(*callback) (slot, info))
+ break;
+ }
+ while (++slot < limit);
+}
+
+/* Return the current size of given hash table. */
+
+size_t
+htab_size (htab)
+ htab_t htab;
+{
+ return htab->size;
+}
+
+/* Return the current number of elements in given hash table. */
+
+size_t
+htab_elements (htab)
+ htab_t htab;
+{
+ return htab->n_elements - htab->n_deleted;
+}
+
+/* Return the fraction of fixed collisions during all work with given
+ hash table. */
+
+double
+htab_collisions (htab)
+ htab_t htab;
+{
+ if (htab->searches == 0)
+ return 0.0;
+
+ return (double) htab->collisions / (double) htab->searches;
+}
diff --git a/tools/hashtab.h b/tools/hashtab.h
new file mode 100644
index 0000000..9ed18ae
--- /dev/null
+++ b/tools/hashtab.h
@@ -0,0 +1,143 @@
+/* An expandable hash tables datatype.
+ Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+ Contributed by Vladimir Makarov (vmakarov@cygnus.com).
+
+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 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. */
+
+/* This package implements basic hash table functionality. It is possible
+ to search for an entry, create an entry and destroy an entry.
+
+ Elements in the table are generic pointers.
+
+ The size of the table is not fixed; if the occupancy of the table
+ grows too high the hash table will be expanded.
+
+ The abstract data implementation is based on generalized Algorithm D
+ from Knuth's book "The art of computer programming". Hash table is
+ expanded by creation of new hash table and transferring elements from
+ the old table to the new table. */
+
+#ifndef __HASHTAB_H__
+#define __HASHTAB_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* The type for a hash code. */
+typedef unsigned int hashval_t;
+
+/* Callback function pointer types. */
+
+/* Calculate hash of a table entry. */
+typedef hashval_t (*htab_hash) (const void *);
+
+/* Compare a table entry with a possible entry. The entry already in
+ the table always comes first, so the second element can be of a
+ different type (but in this case htab_find and htab_find_slot
+ cannot be used; instead the variants that accept a hash value
+ must be used). */
+typedef int (*htab_eq) (const void *, const void *);
+
+/* Cleanup function called whenever a live element is removed from
+ the hash table. */
+typedef void (*htab_del) (void *);
+
+/* Function called by htab_traverse for each live element. The first
+ arg is the slot of the element (which can be passed to htab_clear_slot
+ if desired), the second arg is the auxiliary pointer handed to
+ htab_traverse. Return 1 to continue scan, 0 to stop. */
+typedef int (*htab_trav) (void **, void *);
+
+/* Hash tables are of the following type. The structure
+ (implementation) of this type is not needed for using the hash
+ tables. All work with hash table should be executed only through
+ functions mentioned below. */
+
+struct htab
+{
+ /* Pointer to hash function. */
+ htab_hash hash_f;
+
+ /* Pointer to comparison function. */
+ htab_eq eq_f;
+
+ /* Pointer to cleanup function. */
+ htab_del del_f;
+
+ /* Table itself. */
+ void **entries;
+
+ /* Current size (in entries) of the hash table */
+ size_t size;
+
+ /* Current number of elements including also deleted elements */
+ size_t n_elements;
+
+ /* Current number of deleted elements in the table */
+ size_t n_deleted;
+
+ /* The following member is used for debugging. Its value is number
+ of all calls of `htab_find_slot' for the hash table. */
+ unsigned int searches;
+
+ /* The following member is used for debugging. Its value is number
+ of collisions fixed for time of work with the hash table. */
+ unsigned int collisions;
+
+ /* This is non-zero if we are allowed to return NULL for function calls
+ that allocate memory. */
+ int return_allocation_failure;
+};
+
+typedef struct htab *htab_t;
+
+/* An enum saying whether we insert into the hash table or not. */
+enum insert_option {NO_INSERT, INSERT};
+
+/* The prototypes of the package functions. */
+
+/* This function is like htab_create, but may return NULL if memory
+ allocation fails, and also signals that htab_find_slot_with_hash and
+ htab_find_slot are allowed to return NULL when inserting. */
+extern htab_t htab_try_create (size_t, htab_hash, htab_eq, htab_del);
+extern void htab_delete (htab_t);
+extern void htab_empty (htab_t);
+
+extern void *htab_find (htab_t, const void *);
+extern void **htab_find_slot (htab_t, const void *, enum insert_option);
+extern void *htab_find_with_hash (htab_t, const void *, hashval_t);
+extern void **htab_find_slot_with_hash (htab_t, const void *, hashval_t,
+ enum insert_option);
+extern void htab_clear_slot (htab_t, void **);
+extern void htab_remove_elt (htab_t, void *);
+
+extern void htab_traverse (htab_t, htab_trav, void *);
+
+extern size_t htab_size (htab_t);
+extern size_t htab_elements (htab_t);
+extern double htab_collisions (htab_t);
+
+/* A hash function for pointers. */
+extern htab_hash htab_hash_pointer;
+
+/* An equality function for pointers. */
+extern htab_eq htab_eq_pointer;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __HASHTAB_H */
diff --git a/tools/javadeps.c b/tools/javadeps.c
new file mode 100644
index 0000000..9d15787
--- /dev/null
+++ b/tools/javadeps.c
@@ -0,0 +1,1288 @@
+/*
+RPM and it's source code are covered under two separate licenses.
+
+The entire code base may be distributed under the terms of the GNU General
+Public License (GPL), which appears immediately below. Alternatively,
+all of the source code in the lib subdirectory of the RPM source code
+distribution as well as any code derived from that code may instead be
+distributed under the GNU Library General Public License (LGPL), at the
+choice of the distributor. The complete text of the LGPL appears
+at the bottom of this file.
+
+This alternatively is allowed to enable applications to be linked against
+the RPM library (commonly called librpm) without forcing such applications
+to be distributed under the GPL.
+
+Any questions regarding the licensing of RPM should be addressed to
+marc@redhat.com and ewt@redhat.com.
+*/
+
+/*
+ Simple progam for pullng all the referenced java classes out of a
+ class file. Java files are supposed to be platform independent, so
+ this program should work on all platforms. This code is based on
+ the information found in:
+
+ "Java Virtual Machine" by Jon Meyer & Troy Downing.
+ O'Reilly & Associates, INC. (First Edition, March 1997)
+ ISBN: 1-56592-194-1
+
+ Jonathan Ross, Ken Estes
+ Mail.com
+ */
+
+
+/*
+ Remember that:
+
+ JAR consists of a zip archive, as defined by PKWARE, containing
+ a manifest file and potentially signature files, as defined in
+ the Manifest and Signature specification. So we use infozip's
+ 'unzip -p' found at http://www.cdrom.com/pub/infozip/.
+
+ Additional documentation, about this fact, at:
+
+ http://java.sun.com/products/jdk/1.1/docs/guide/jar/jarGuide.html
+ http://java.sun.com/products/jdk/1.2/docs/guide/jar/jarGuide.html
+
+ http://java.sun.com/products/jdk/1.1/docs/guide/jar/manifest.html
+ http://java.sun.com/products/jdk/1.2/docs/guide/jar/manifest.html
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <netdb.h>
+#include <rpm/rpmutil.h>
+#include <ctype.h>
+#include "debug.h"
+
+/*---------typedefs---------*/
+
+
+/* The symbol table is a waste of memory..
+ but it's easy to code! */
+
+typedef struct {
+ short poolSize;
+ char **stringList;
+ short *classRef;
+ short *typeRef;
+} symbolTable_t;
+
+
+/*---------Global Variables, in all caps---------*/
+
+/*name of this program*/
+const char *PROGRAM_NAME=0;
+
+/*name of the current class file*/
+const char *FILE_NAME=0;
+
+/*the name of the last class file seen*/
+char *CLASS_NAME=0;
+
+/*The string to put before each line,
+ this is a formatted version of CLASS_NAME */
+char *OUTPUT_PREFIX=0;
+
+/*arguments chosen*/
+int ARG_PROVIDES=0;
+int ARG_REQUIRES=0;
+int ARG_RPMFORMAT=0;
+int ARG_DEPSFORMAT=0;
+int ARG_KEYWORDS=0;
+int ARG_STARPROV=0;
+
+/*keywords found in class file*/
+char *KEYWORD_VERSION=0;
+char *KEYWORD_REVISION=0;
+char *KEYWORD_EPOCH=0;
+
+/*
+
+ Quantify says over 80 percent of the time of the program is spent
+ in printf (and the functions it calls) AND I verified that only
+ about a quarter of the classes found in the dependency analysis are
+ unique. After the change no function used more then 26% of the over
+ all time.
+
+ I implement a simple mechanism to remove most duplicate
+ dependencies. The print_table is a table of string, with
+ duplicates, which are to be printed. Just before the program
+ exists it is sorted and all unique entries are printed. If it
+ fills up during then it is flushed using the same technique as
+ above.
+
+ The functions which manipulate the table are:
+ void print_table_flush(void)
+ void print_table_add(char *str)
+
+*/
+
+
+#define MAX_PRINT_TABLE 10000
+char *PRINT_TABLE[MAX_PRINT_TABLE];
+int SIZE_PRINT_TABLE;
+
+/*--------- declare all functions ---------*/
+
+void usage (void);
+void outofmemory(void);
+void die(const char *format, ...);
+size_t my_fread(void *ptr, size_t size, size_t nitems, FILE *stream);
+void check_range(short entryNum, short value, short poolSize);
+char *is_lower_equal (char *string, const char *pattern);
+int findJavaMagic (FILE *fileHandle);
+int my_strcmp (const void *a, const void *b);
+void print_table_flush(void);
+void print_table_add(char *str);
+char *formatClassName(char *pSomeString, char terminator, char print_star);
+void dumpRefType(char *pSomeString);
+void genSymbolTable (FILE *fileHandle, symbolTable_t *symbolTable);
+void freeSymbolTable (symbolTable_t *symbolTable);
+char *findClassName (FILE *fileHandle, symbolTable_t *symbolTable);
+void dumpProvides(char *className);
+void dumpRequires(symbolTable_t *symbolTable);
+void processJavaFile (FILE *fileHandle);
+
+/*--------- functions ---------*/
+
+void
+usage (void)
+{
+ printf("NAME:\n\tjavadeps - Examine Java class files and\n"
+ "\t\t\treturn information about their dependencies.\n\n");
+ printf("USAGE:\n");
+ printf("\t javadeps { --provides | --requires } \n"
+ "\t\t [--rpmformat] [--depsformat] [--keywords] \n"
+ "\t\t [--] classfile-name ... \n\n"
+ "\t javadeps [--help]\n\n");
+ printf("\n\n");
+ printf("DESCRIPTION:\n\n");
+ printf("List the dependencies or the fully qualified class names, of the \n"
+ "classfiles listed on the command line. \n\n");
+ printf("OPTIONS:\n\n");
+ printf("--requires For each class files listed in the arguments,\n"
+ " -r print the list of class files that would be\n"
+ " required to run these java programs. This does not \n"
+ " include anyting instantiated by reflection.\n\n");
+ printf("--provides For each class files listed in the arguments, \n"
+ " -p Print the fully qualified java classes,\n"
+ " that they provide.\n\n");
+ printf("--rpmformat Format the output to match that used by RPM's \n"
+ " -F (Red Hat Package Manager) dependency analysis \n"
+ " database. The default is not --rpmformat.\n\n");
+ printf("--depsformat print the name of the class which \n"
+ " -d This is mostly used in conjunctions with --requires \n"
+ " to list the class file dependencies in a format "
+ " similar to traditional Makefile dependencies. The "
+ " default is not --depsformat.\n\n");
+ printf("--keywords Make use of any keywords embeded in the classfile.\n"
+ " -k The default is not --keyword.\n\n");
+ printf("--starprov Add the star notation provides to the provides list.\n"
+ " -s The default is not --starprov. This is only for use\n"
+ " with (Sun) jhtml dependencies, and since jhtml is \n"
+ " deprecated so is this option.\n\n");
+ printf("--help Display this page and exit.\n\n");
+ printf("-- This stops the processing of arguments, making it \n"
+ " easier for users to have filenames like '--keywords',\n"
+ " without the command line parser getting confused.\n\n");
+ printf("\n\n");
+ printf("If any of the class file names in the argument list is '-' then\n"
+ "<stdin> will be read instead of reading from a file. The\n"
+ "contents of <stdin> should be the contents of a class file and \n"
+ "not a list of class files to read. It is assumed that when run \n"
+ "with '-', this program is in a pipeline preceded by the \n"
+ "command 'unzip -p filename.jar' so that <stdin> may contain\n"
+ "the contents of several classfiles concatenated together.\n");
+ printf("\n\n");
+ printf("If --keywords is specified then the following strings are \n"
+ "searched for (case insensitive) in the class file string table\n"
+ "and, if a string is found with a prefix matching the keyword then \n"
+ "the dependencies are changed accordingly. There may be multiple \n"
+ "string tables entries prefixed with RPM_Provides and RPM_Requires. \n"
+ "This would indicate that the dependency is the union\n"
+ "of all entries.\n"
+ "\n\n"
+ "Keyword List:\n\n"
+ "'$Revision: ' This RCS/CVS compatible keyword is assumed to \n"
+ " contain the version number of the class file \n"
+ " it is found in. Care should be taken with this\n"
+ " option as RPM's notion of which version is later\n"
+ " may not corrispond with your own, especially\n"
+ " if you use branches. This keyword\n"
+ " only effects the output of --provides and only\n"
+ " when RPM_Version is not defined.\n\n"
+ "'RPM_Version: ' This is an alternative method of specifying the\n"
+ " version number of the class. It will override\n"
+ " $Revision if set. This keyword only effects\n"
+ " the output of --provides \n\n"
+ "'RPM_Epoch: ' This string contains the epoch to use with the \n"
+ " version number stored in Revision. If not \n"
+ " specified, the epoch is assumed to be zero.\n"
+ " This keyword only effects the output of\n "
+ " --provides and only when $Revision number is\n"
+ " used.\n\n"
+ "'RPM_Provides: ' This string lists additional capabilities\n"
+ " provided by the java class. The string should\n"
+ " be a white space ([\\t\\v\\n\\r\\f\\ ])\n"
+ " separated list of dependency strings. Each\n"
+ " dependency string must be of the same format as\n"
+ " would be valid in the Requires or Provides line\n"
+ " of the specfile. This keyword only effects the\n"
+ " output of --provides.\n\n"
+ "'RPM_Requires: ' This string lists additional requirements of\n"
+ " the java class. The string should be a white \n"
+ " space ([\\t \v\\n\\r\\f\\ ]) separated list of \n"
+ " dependency strings. Each dependency string must\n"
+ " be of the same format as would be valid in the \n"
+ " Requires or Provides line of the specfile. This\n"
+ " keyword only effects the output of --requires.\n "
+ " \n\n"
+ "Note that there is no means of setting the release number. This\n"
+ "is necessary because release numbers are incremented when the\n"
+ "source does not change but the package needs to be rebuilt. So\n"
+ "relase numbers can not be stored in the source. The release is\n"
+ "assumed to be zero. \n\n"
+ "");
+ printf("EXAMPLES (Java Keywords): \n\n"
+ "\t public static final String REVISION = \"$Revision: 2.12 $\";\n"
+ "\t public static final String EPOCH = \"4\";\n"
+ "\t public static final String REQUIRES = \"RPM_Requires: "
+ "java(gnu.regexp.RE) java(com.ibm.site.util.Options)>=1.5\";\n"
+ "");
+ printf("\n\n");
+ printf("EXAMPLES (Arguments): \n\n"
+ "\tjavadeps --requires -- filename.class\n\n"
+ "\tjavadeps --provides -- filename.class\n\n"
+ "\tjavadeps --help\n\n"
+ "\n"
+ "\tjavadeps --requires --rpmformat --keywords -- filename.class\n\n"
+ "\tjavadeps --requires --depsformat -- filename.class\n\n"
+ "\tjavadeps --requires -- filename1.class filename2.class\n\n"
+ "\tcat filename2.class | javadeps --requires -- filename1.class -\n\n"
+ "\tunzip -p filename.jar | javadeps --requires -- - \n\n"
+ "");
+ printf("This program is distributed with RPM the Redhat Package \n"
+ "Managment system. Further information about RPM can be found at \n"
+ "\thttp://www.rpm.org/\n\n");
+ printf("\n\n");
+ exit(-1);
+}
+
+RPM_GNUC_NORETURN
+void outofmemory(void) {
+
+ /* Its doubtful we could do a printf if there is really a memory
+ issue but at least halt the program */
+
+ fprintf(stderr, "Could not allocate memory\n");
+ exit(-1);
+}
+
+RPM_GNUC_NORETURN
+void die(const char *format, ...) {
+ /* Most errors are fatal.
+ This function throws a fatal error and
+ accepts arguments like printf does*/
+
+ char *newformat = NULL, *newmsg = NULL;
+ va_list ap;
+
+ if (
+ !(newformat = (char*) malloc(1024)) ||
+ !(newmsg = (char*) malloc(1024))
+ )
+ outofmemory();
+
+ /* Rewrite format line, to include additional information. The
+ format line we chose depends on how much information is availible
+ at the time of the error. Display the maximum ammount of
+ information. */
+
+ /* notice the FILE_NAME is useless for jar files. We would want to
+ print the name of the classfile which caused the error. That
+ is hard since we only know that name when we are done parsing
+ the file, and most errors will occur before that.*/
+
+ if ( (!FILE_NAME) ) {
+
+ sprintf (newformat, "\n%s: %s",
+ PROGRAM_NAME, format);
+
+ } else if ( (FILE_NAME) && (!CLASS_NAME) ) {
+
+ sprintf (newformat, "\n%s: Java classfile: %s, %s",
+ PROGRAM_NAME, FILE_NAME, format);
+
+ } else if (CLASS_NAME) {
+ sprintf (newformat, "\n%s: Java classfile: %s, classname: %s, %s",
+ PROGRAM_NAME, FILE_NAME, CLASS_NAME, format);
+ }
+
+ va_start(ap, format);
+ vsprintf (newmsg, newformat, ap);
+ va_end(ap);
+
+ /* print error to where it needs to go:
+ stdout, stderr, or syslog
+ */
+
+ fprintf(stderr, "%s", newmsg);
+
+ free(newformat);
+ free(newmsg);
+
+ exit(-1);
+}
+
+
+/* wrap fread for safety. It is a fatal error to get an unexpected
+ EOF inside a class file. */
+
+size_t my_fread(void *ptr, size_t size, size_t nitems, FILE *stream) {
+ size_t rc=0;
+ /*these variables are helpful in the debugger*/
+ int eof=0;
+ int error=0;
+
+
+ rc = fread(ptr, size, nitems, stream);
+ if ( (size!=0) && (rc == 0) ) {
+ eof = feof(stream);
+ error = ferror(stream);
+ die("Error reading from file, or unexpected EOF\n");
+ }
+ return rc;
+}
+
+
+void check_range(short entryNum, short value, short poolSize) {
+
+ if (value > poolSize) {
+ die("Symbol Table Entry Number: %d, Value: %d, "
+ "is out of range of the constant pool. Pool Size: %d\n",
+ entryNum, value, poolSize);
+ }
+ return ;
+}
+
+
+
+/* If lower case conversion of string is equal to pattern return a
+ pointer into string, just after the match. If the string does not
+ patch the pattern the null pointer is returned. This does not
+ change string.
+
+ This is similar to strcasecmp, but I expect my patterns to be a
+ prefix of my strings. */
+
+char
+*is_lower_equal (char *string, const char *pattern)
+{
+
+ while ( (tolower(*string) == *pattern) &&
+ *string && *pattern ) {
+ string++;
+ pattern++;
+ }
+
+ if ( *pattern == 0 ) {
+ return string;
+ }
+
+ return NULL;
+}
+
+
+/*
+ Read fileHandle until we find the next instance of the Java
+ Classfile magic number indicating a java file or find EOF or
+ fileread error. Since we are reading from stdin which may contain
+ the concatination of many class files we can not be sure that the
+ magic number will be the first few bytes.
+
+ Return 1 on success 0 on failure. */
+
+#define mod4(num) ( (num) & 3 )
+
+
+int findJavaMagic (FILE *fileHandle)
+{
+ int offset=0;
+ int foundMagic = 0;
+ size_t rc;
+
+ /* what were looking for */
+ unsigned char magicInt[4] = {0xCA, 0xFE, 0xBA, 0xBE};
+ /*the hex reads in decimal: 202 254 186 190 */
+
+ /* a circular buffer indicating the last few bytes we read */
+ unsigned char buffer[4] = {0};
+
+ foundMagic = 0;
+ while( !foundMagic ) {
+
+ rc = fread(&buffer[offset], 1, 1, fileHandle);
+ if ( !rc ) {
+
+ /* Either this was not a java file or we were given a jar file
+ and have already found the last java file in it.*/
+
+ if ( feof(fileHandle) ) {
+ return 0;
+ }
+
+ if ( ferror(fileHandle) ) {
+ die ("Error reading character from file.\n");
+ };
+
+ }
+
+ /* offset points to the most recent char we read so offest+1
+ points to the oldest char we saved. */
+
+ foundMagic = (
+ (magicInt[0] == buffer[mod4(offset+1)]) &&
+ (magicInt[1] == buffer[mod4(offset+2)]) &&
+ (magicInt[2] == buffer[mod4(offset+3)]) &&
+ (magicInt[3] == buffer[mod4(offset+0)]) &&
+ 1
+ );
+
+ offset = mod4(offset+1);
+
+ } /*end while*/
+
+ return foundMagic;
+}
+
+#undef mod4
+
+
+int
+my_strcmp (const void *a, const void *b) {
+char **a1; char **b1;
+int ret;
+
+a1 = (char **)a;
+b1 = (char **)b;
+ret = strcmp(*a1,*b1);
+ return ret;
+}
+
+/* print the unique strings found in PRINT_TABLE and clear it out */
+
+void
+print_table_flush(void) {
+ int i;
+ char *last_string;
+
+ if (!SIZE_PRINT_TABLE) {
+ return ;
+ }
+
+ /* The qsort line gives a warning on some unicies who insist that
+ strcmp takes arguments of type pointers to void not the
+ traditional pointers to char. */
+
+ qsort( (void *) PRINT_TABLE, (size_t) SIZE_PRINT_TABLE,
+ sizeof(char *), &my_strcmp);
+
+ printf("%s\n",PRINT_TABLE[0]);
+ last_string = PRINT_TABLE[0];
+ PRINT_TABLE[0] = NULL;
+
+ for (i = 1; i < SIZE_PRINT_TABLE; i++) {
+ if ( strcmp(last_string, PRINT_TABLE[i]) ){
+ printf("%s\n",PRINT_TABLE[i]);
+ free(last_string);
+ last_string = PRINT_TABLE[i];
+ } else {
+ free(PRINT_TABLE[i]);
+ }
+ PRINT_TABLE[i] = NULL;
+ }
+
+ free(last_string);
+ SIZE_PRINT_TABLE = 0;
+ return ;
+}
+
+
+/* add an element to PRINT_TABLE for later printing to stdout. We do
+ not make a copy of the string so each string must be unique,
+ (different calls must pass pointers to different areas of memory)
+ and the string must not be freed anywhere else in the code and the
+ string must be from memory which can be freed.*/
+
+void
+print_table_add(char *str) {
+
+ if (SIZE_PRINT_TABLE == MAX_PRINT_TABLE) {
+ print_table_flush();
+ }
+
+ if (OUTPUT_PREFIX) {
+ char *new_str;
+
+ new_str= (char*) malloc(strlen(OUTPUT_PREFIX)+strlen(str)+1);
+ if (!new_str){
+ outofmemory();
+ }
+
+ new_str[0]='\0';
+ strcat(new_str,OUTPUT_PREFIX);
+ strcat(new_str,str);
+ free(str);
+ str=new_str;
+ }
+
+ PRINT_TABLE[SIZE_PRINT_TABLE] = str;
+ SIZE_PRINT_TABLE++;
+ return ;
+}
+
+
+static void
+print_list(char *in_string) {
+
+ /* This function is no longer needed due to fixes in RPM's
+ processing of dependencies. Keep the code until I get a chance
+ to use RPM3.0 personally */
+
+ if (in_string) {
+ printf("%s\n", in_string);
+ }
+
+/*
+ Old function did:
+
+ Given a list separated by whitespace, put each element in the print
+ table with an added "\n" */
+
+ /*
+ char *WhiteSpace_Set = "\t\v\n\r\f ";
+ char *newEnd, *out_string;
+ int copy_len;
+
+ in_string += strspn(in_string, WhiteSpace_Set);
+
+ while (*in_string) {
+ newEnd = strpbrk(in_string, WhiteSpace_Set);
+
+ if (newEnd) {
+ copy_len = newEnd-in_string;
+ } else {
+ if (*in_string) {
+ copy_len = strlen(in_string);
+ } else {
+ copy_len = 0;
+ }
+ }
+
+ out_string = malloc(copy_len+10);
+ if ( !out_string ) {
+ outofmemory();
+ }
+ out_string[0]= '\0';
+
+ if (copy_len) {
+ strncat(out_string, in_string, copy_len);
+ in_string+=copy_len;
+ strcat(out_string, "\n");
+ print_table_add(out_string);
+ }
+
+ in_string += strspn(in_string+copy_len, WhiteSpace_Set);
+ }
+
+ */
+ return ;
+}
+
+
+/* Print a properly formatted java class name, and returns the length
+ of the class string . Do not print \n here as we may wish to
+ append the version number IFF we are printing the name of this classfile
+
+ We also provide the class with the leaf node replaced with '*'.
+ This would not be necessary if we only had to worry about java
+ Class files. However our parsing of jhtml files depends on this
+ information. This is deprecated since jhtml is deprecated and
+ this method allows for very inaccurate dependencies.
+
+ Also users may wish to refer to dependencies using star notation
+ (though this would be less accurate then explicit dependencies
+ since any leaf class will satify a star dependency). */
+
+char
+*formatClassName(char *in_string, char terminator,
+ char print_star)
+{
+ char *leaf_class=0, *out_string=0;
+ char *ClassName_Break_Set=0;
+
+
+ out_string = (char*) malloc(strlen(in_string) + 10);
+ if ( !out_string ) {
+ outofmemory();
+ }
+ out_string[0]= '\0';
+
+ /*these characters end the current parse of the string in function
+ formatClassName.*/
+
+ ClassName_Break_Set = (char*) malloc(3);
+ if ( !ClassName_Break_Set ) {
+ outofmemory();
+ }
+ ClassName_Break_Set[0] = '/';
+ /*terminator can be '\0' this must go after '/'*/
+ ClassName_Break_Set[1] = terminator;
+ ClassName_Break_Set[2] = '\0';
+
+ if(ARG_RPMFORMAT) {
+ strcat(out_string, "java(");
+ }
+ if (print_star) {
+ /* print the path to the leaf class but do not print the leaf
+ class, stop at the last '/' we fix this back below*/
+ leaf_class = strrchr(in_string, '/');
+ if (leaf_class) {
+ leaf_class[0] = terminator;
+ }
+ }
+
+ while (*in_string != terminator) {
+ char *newEnd=0;
+ int copy_len;
+
+ /* handle the break_set */
+
+ if (in_string[0] == '\0' ) {
+ die("Classname does not terminate with: '%c', '%s'\n",
+ terminator, in_string);
+ } else {
+ if (in_string[0] == '/' ) {
+ /* convert '/' to '.' */
+ strcat(out_string, ".");
+ in_string++;
+ }
+ }
+
+ newEnd = strpbrk(in_string, ClassName_Break_Set);
+
+ if (newEnd) {
+ copy_len = newEnd-in_string;
+ } else {
+ if (terminator == '\0') {
+ copy_len = strlen(in_string);
+ } else {
+ copy_len = 0;
+ }
+ }
+
+ /* handle upto but not including the break_set*/
+ if (copy_len) {
+ strncat(out_string, in_string, copy_len);
+ in_string+=copy_len;
+ }
+
+ } /*end while*/
+
+ if (leaf_class) {
+ /* print the star and fix the leaf class*/
+ strcat(out_string, ".*");
+ leaf_class[0] = '/';
+ }
+ if(ARG_RPMFORMAT) {
+ strcat(out_string, ")");
+ }
+
+ free(ClassName_Break_Set);
+ return out_string;
+}
+
+
+/* Parse out just the class names from a java type and moves the string
+ pointer to the end of the string. */
+
+void
+dumpRefType(char *string)
+{
+ /* All class types start with a 'L' and and end with a ';'. We want
+ everyting in between. There might be more then one per string
+ like (params for a method call) */
+
+ string = strchr(string, 'L');
+ while (string) {
+ string++;
+ print_table_add(formatClassName(string, ';', 0));
+ string = strchr(string, ';');
+ string = strchr(string, 'L');
+ }
+
+ return ;
+}
+
+
+/* Print out the classes referenced in the symbol table */
+
+void
+dumpRequires(symbolTable_t *symbolTable) {
+ int tem;
+ int ref = 0;
+
+ for(tem=1; tem < symbolTable->poolSize; tem++ ) {
+
+ /* dump all the classes in the const table. */
+ ref = symbolTable->classRef[tem];
+ if(ref) {
+ char *string = symbolTable->stringList[ref];
+ if( !*string ) {
+ die("class num: %d, referenced string num: %d, "
+ "which is null.\n",
+ tem, ref);
+ }
+ if ( string[0] == '[' ) {
+ /*
+ This is an array. We need to ingore
+ strings like:
+ '[B'
+ '[[B'
+ '[[I'
+
+ This hack leaves blank lines in the output
+ when a string not containing a class is
+ sent to dumpRefType.
+ */
+ string = strchr(string, 'L');
+ if (string) {
+ dumpRefType(string);
+ }
+ } else {
+ print_table_add(formatClassName(string, '\0', 0));
+ }
+ }
+
+ /* dump all the references */
+ ref = symbolTable->typeRef[tem];
+ if (ref) {
+ char *string = symbolTable->stringList[ref];
+ if ( !*string ) {
+ die("type num: %d, referenced string num: %d, "
+ "which is null.\n",
+ tem, ref);
+ }
+ /* this is a java type... parse out the class names */
+ dumpRefType(string);
+ }
+
+ } /*end for*/
+
+ return ;
+}
+
+
+/* Read a java class files symbol table into memory.
+*/
+
+
+void genSymbolTable (FILE *fileHandle, symbolTable_t *symbolTable)
+{
+ char ignore[10];
+ int i=0;
+
+
+ /* We are called just after fileHandle saw the magic number, seek a
+ few bytes in to find the poolsize */
+
+ my_fread(&ignore, 4, 1, fileHandle);
+
+ my_fread(&(symbolTable->poolSize), 2, 1, fileHandle);
+ symbolTable->poolSize=ntohs(symbolTable->poolSize);
+
+ /* new the tables */
+
+ symbolTable->stringList = (char**) calloc(symbolTable->poolSize,
+ sizeof(char*));
+ if(!symbolTable->stringList){
+ outofmemory();
+ }
+
+ symbolTable->classRef = (short*) calloc(symbolTable->poolSize,
+ sizeof(short*));
+ if(!symbolTable->classRef){
+ outofmemory();
+ }
+
+ symbolTable->typeRef = (short*) calloc(symbolTable->poolSize,
+ sizeof(short*));
+ if(!symbolTable->typeRef){
+ outofmemory();
+ }
+
+ /* zero 'em all out. */
+ for(i=0; i < symbolTable->poolSize; i++) {
+ symbolTable->stringList[i] = NULL;
+ symbolTable->classRef[i] = 0;
+ symbolTable->typeRef[i] = 0;
+ }
+
+
+ /* for the number of entries
+ (it starts at 1)
+ */
+
+ for(i=1; i < symbolTable->poolSize; i++) {
+ unsigned short type = 0;
+ unsigned short value = 0;
+ unsigned char tag = 0;
+
+ /* read the type of this entry */
+
+ my_fread(&tag, 1, 1, fileHandle);
+ switch(tag) {
+ case 1: /* master string pool. */
+ {
+ /* record all these strings */
+ char *someString;
+ unsigned short length = 0;
+
+ /* I am not sure if these strings must be null
+ terminated. I termiante them to be safe. */
+
+ my_fread(&length, 2, 1, fileHandle);
+ length=ntohs(length);
+
+ someString = (char*) malloc(length+1);
+ if(!someString){
+ outofmemory();
+ }
+ my_fread(someString, length, 1, fileHandle);
+ someString[length]=0;
+ symbolTable->stringList[i] = someString;
+
+ if (ARG_KEYWORDS) {
+ char *ptr=0;
+
+ /* Each keyword can appear multiple times. Don't
+ bother with datastructures to store these strings,
+ if we need to print it print it now. */
+
+ /* it would be better if instead of printing the
+ strings "raw" I turn the space separated list
+ into a "\n" separated list*/
+
+ if (ARG_REQUIRES) {
+ ptr = is_lower_equal(someString, "rpm_requires: ");
+ if(ptr){
+ print_list(ptr);
+ }
+ }
+ if (ARG_PROVIDES) {
+ ptr = is_lower_equal(someString, "rpm_provides: ");
+ if(ptr){
+ print_list(ptr);
+ }
+ }
+ /* I wish there was a good way to handle this
+ ptr = is_lower_equal(someString, "rpm_conflicts: ");
+ */
+ ptr = is_lower_equal(someString, "$revision: ");
+ if(ptr){
+ KEYWORD_REVISION=ptr;
+ /* terminate the string before " $" */
+ ptr = strchr(KEYWORD_REVISION, ' ');
+ if (ptr) {
+ *ptr = 0;
+ }
+ }
+ ptr = is_lower_equal(someString, "rpm_version: ");
+ if(ptr){
+ KEYWORD_VERSION=ptr;
+ /* terminate the string at first whitespace */
+ ptr = strchr(KEYWORD_VERSION, ' ');
+ if (ptr) {
+ *ptr = 0;
+ }
+ }
+ ptr = is_lower_equal(someString, "rpm_epoch: ");
+ if(ptr){
+ KEYWORD_EPOCH=ptr;
+ /* terminate the string at first whitespace */
+ ptr = strchr(KEYWORD_EPOCH, ' ');
+ if (ptr) {
+ *ptr = 0;
+ }
+ }
+ }
+ break;
+ }
+ case 2: /* unknow type!! */
+ die("Unknown type in constant table. "
+ "Entry: %d. \n", i);
+ break;
+ case 3: /* int */
+ my_fread(&ignore, 4, 1, fileHandle);
+ break;
+ case 4: /* float */
+ my_fread(&ignore, 4, 1, fileHandle);
+ break;
+ case 5: /* long (counts as 2) */
+ my_fread(&ignore, 8, 1, fileHandle);
+ i++;
+ break;
+ case 6: /* double (counts as 2) */
+ my_fread(&ignore, 8, 1, fileHandle);
+ i++;
+ break;
+ case 7: /* Class */
+ my_fread(&value, 2, 1, fileHandle);
+ symbolTable->classRef[i]=ntohs(value);
+ /* record which const it's referencing */
+ check_range(i, symbolTable->classRef[i], symbolTable->poolSize);
+ break;
+ case 8: /* String */
+ my_fread(&ignore, 2, 1, fileHandle);
+ break;
+ case 9: /* field reference */
+ my_fread(&ignore, 4, 1, fileHandle);
+ break;
+ case 10: /* method reference */
+ my_fread(&ignore, 4, 1, fileHandle);
+ break;
+ case 11: /* interface method reference */
+ my_fread(&ignore, 4, 1, fileHandle);
+ break;
+ case 12: /* constant name/type */
+ my_fread(&ignore, 2, 1, fileHandle);
+ my_fread(&type, 2, 1, fileHandle);
+ symbolTable->typeRef[i]=ntohs(type);
+ /* record the name, and the type it's referencing. */
+ check_range(i, symbolTable->typeRef[i], symbolTable->poolSize);
+ break;
+ default:
+ die("Unknown tag type: %d.\n",
+ "Entry: %d. \n", tag, i);
+ break;
+ }
+ }
+
+ return ;
+}
+
+
+/* Find the proper name of the current Java Class file and return a
+ formatted of the name.
+*/
+
+char*
+findClassName (FILE *fileHandle, symbolTable_t *symbolTable) {
+ char ignore[10];
+ unsigned short type = 0;
+ unsigned short jclass = 0;
+ char *className;
+
+ /* seek a little past the end of the table */
+
+ my_fread(&ignore, 2, 1, fileHandle);
+
+ /* read the name of this classfile */
+
+ my_fread(&type, 2, 1, fileHandle);
+ type=ntohs(type);
+ jclass = symbolTable->classRef[type];
+ if( !jclass ||
+ !symbolTable->stringList[jclass] ) {
+ die("Couln't find class: %d, provided by file.\n", jclass);
+ }
+
+ className=symbolTable->stringList[jclass];
+
+ return className;
+
+}
+
+
+/* Print the name of the class that we have found and whos symbol
+ table is loaded into memory.
+
+ The name may need to have the version number printed after it, if
+ there are keywords in the symbol table.
+
+ Additionally the class name may need to be printed twice, once with
+ the regular name of the class and once with the leaf class replaced
+ with a star.
+
+*/
+
+void
+dumpProvides(char *className) {
+ char *out_string;
+#ifdef UNUSED
+ char *formattedClassName;
+ char *newline;
+#endif
+
+ /* Provide the star version of this class for jhtml
+ dependencies. This option is deprecated since jhtml is
+ deprecated. */
+
+ if (ARG_STARPROV) {
+ print_table_add(formatClassName(className, '\0', 1));
+ }
+
+ /* If we have information about the version number of this class
+ then add it to the formatted name and print this as well. */
+
+ out_string = formatClassName(className, '\0', 0);
+
+ {
+ int len = 10;
+
+ if (out_string) {
+ len += strlen(out_string);
+ }
+ if (KEYWORD_EPOCH) {
+ len += strlen(KEYWORD_EPOCH);
+ }
+ if (KEYWORD_VERSION) {
+ len += strlen(KEYWORD_VERSION);
+ }
+ if (KEYWORD_REVISION) {
+ len += strlen(KEYWORD_REVISION);
+ }
+
+ out_string = realloc(out_string, len );
+ if (!out_string){
+ outofmemory();
+ }
+
+ }
+
+ if( KEYWORD_VERSION || KEYWORD_REVISION ){
+ /* It is easier to remove the extra new line here in one place
+ then to try and add a newline every where that formatClassName
+ is called */
+ char *newline;
+
+ /* I am not using rpm 3.0 yet so I need both the dependencies with
+ and without the version numbers, when I upgrade I will remove
+ this block (with copy_string) and change the "=" to " = " ten
+ lines down.*/
+ {
+ char *copy_string;
+ copy_string = (char*) malloc(strlen(out_string)+1);
+ if (!copy_string){
+ outofmemory();
+ }
+ copy_string = strcpy(copy_string, out_string);
+ print_table_add(copy_string);
+ }
+
+ newline = strrchr(out_string, '\n');
+ if (newline) {
+ newline[0] = '\0';
+ }
+ strcat(out_string, " = ");
+ if(KEYWORD_EPOCH){
+ strcat(out_string, KEYWORD_EPOCH);
+ strcat(out_string, ":");
+ }
+ if(KEYWORD_VERSION){
+ strcat(out_string, KEYWORD_VERSION);
+ } else {
+ strcat(out_string, KEYWORD_REVISION);
+ }
+ strcat(out_string, "\n");
+ }
+
+ print_table_add(out_string);
+ out_string=NULL;
+
+ return ;
+}
+
+
+
+
+
+void freeSymbolTable (symbolTable_t *symbolTable)
+{
+ int i=0;
+
+ for(i=1; i < symbolTable->poolSize; i++) {
+ if( symbolTable->stringList[i] ) {
+ free(symbolTable->stringList[i]);
+ symbolTable->stringList[i] = 0;
+ }
+ }
+
+ free(symbolTable->stringList);
+ symbolTable->stringList=0;
+
+ free(symbolTable->classRef);
+ symbolTable->classRef=0;
+
+ free(symbolTable->typeRef);
+ symbolTable->typeRef=0;
+
+ return ;
+}
+
+
+/* Process each file.
+ This procedure must be called directly after finding
+ the magic number.
+*/
+
+void processJavaFile (FILE *fileHandle) {
+ symbolTable_t symbolTable= {0};
+ char *format_class_name;
+
+ genSymbolTable(fileHandle, &symbolTable);
+ CLASS_NAME=findClassName(fileHandle, &symbolTable);
+
+ if(ARG_DEPSFORMAT) {
+ const char *prefix_seperator = ": ";
+
+ format_class_name = formatClassName(CLASS_NAME, '\0', 0);
+
+ OUTPUT_PREFIX = (char*) malloc(strlen(format_class_name)+
+ strlen(prefix_seperator)+1);
+ if (!OUTPUT_PREFIX){
+ outofmemory();
+ }
+ OUTPUT_PREFIX = strcpy(OUTPUT_PREFIX, format_class_name);
+ strcat(OUTPUT_PREFIX, prefix_seperator);
+ }
+
+ if(ARG_PROVIDES) {
+ dumpProvides(CLASS_NAME);
+ }
+ if(ARG_REQUIRES) {
+ dumpRequires(&symbolTable);
+ }
+
+ free(OUTPUT_PREFIX);
+ OUTPUT_PREFIX = 0;
+ freeSymbolTable(&symbolTable);
+
+ return ;
+
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fileHandle;
+ int i = 0;
+ int rc = 0;
+ int foundMagic=0;
+
+ PROGRAM_NAME=argv[0];
+
+ if(argv[1] == NULL) {
+ usage();
+ }
+
+ /* parse arguments which are not filenames*/
+
+ for (i = 1; argv[i] != NULL; i++) {
+
+ if (0) {
+ /*
+ First entry a dummy to get the
+ other entries to align correctly
+ */
+ ;
+ } else if ( !strcmp("-p",argv[i]) || !strcmp("--provides",argv[i]) ) {
+ ARG_PROVIDES = 1;
+ } else if ( !strcmp("-r",argv[i]) || !strcmp("--requires",argv[i]) ) {
+ ARG_REQUIRES = 1;
+ } else if ( !strcmp("-h",argv[i]) || !strcmp("--help",argv[i]) ||
+ !strcmp("-?",argv[i]) ) {
+
+ usage();
+ } else if ( !strcmp("-F",argv[i]) || !strcmp("--rpmformat",argv[i]) ) {
+ ARG_RPMFORMAT=1;
+ } else if ( !strcmp("-d",argv[i]) || !strcmp("--depsformat",argv[i]) ) {
+ ARG_DEPSFORMAT=1;
+ } else if ( !strcmp("-k",argv[i]) || !strcmp("--keywords",argv[i]) ) {
+ ARG_KEYWORDS=1;
+ } else if ( !strcmp("-s",argv[i]) || !strcmp("--starprov",argv[i]) ) {
+ ARG_STARPROV=1;
+ } else if ( !strcmp("--",argv[i]) ) {
+ i++;
+ break;
+ } else {
+ /* we do not recognize the argument, must be a filename*/
+ break;
+ }
+ } /*end for arguments which are not filenames*/
+
+ /* check arguments for consistancy */
+
+ if ( !ARG_PROVIDES && !ARG_REQUIRES ) {
+ die ("Must specify either --provides or --requires.\n");
+ }
+
+ if ( ARG_PROVIDES && ARG_REQUIRES ) {
+ die ("Can not specify both --provides and --requires.\n");
+ }
+
+ if ( ARG_REQUIRES && ARG_STARPROV) {
+ die ("Can not specify both --requires and --starpov.\n");
+ }
+
+ if(argv[i] == NULL) {
+ die ("Must specify Java class files.\n");
+ }
+
+ /* parse arguments which are filenames. */
+
+ for ( /*null initializer*/; argv[i] != NULL; i++) {
+
+ /*open the correct file and process it*/
+
+ if ( !strcmp("-", argv[i]) ) {
+ /* use stdin, might be a jar file */
+ fileHandle = stdin;
+ FILE_NAME = "<stdin>";
+
+ foundMagic = findJavaMagic(fileHandle);
+ while (foundMagic) {
+ processJavaFile(fileHandle);
+ foundMagic = findJavaMagic(fileHandle);
+ }
+ } else {
+ /* Open a disk file*/
+ fileHandle = fopen(argv[i], "r");
+ if( fileHandle == 0 ) {
+ die ("Could not open file: %s.\n", argv[i]);
+ }
+ fileHandle = fileHandle;
+ FILE_NAME = argv[i];
+
+ foundMagic = findJavaMagic(fileHandle);
+ if (foundMagic) {
+ processJavaFile(fileHandle);
+ }
+ }
+
+ rc = fclose(fileHandle);
+ if( rc ) {
+ die ("Could not close file: %s.\n", FILE_NAME);
+ }
+ CLASS_NAME=0;
+ } /*end parsing arguments which are filenames*/
+
+ print_table_flush();
+ return 0;
+}
diff --git a/tools/rpmdeps.c b/tools/rpmdeps.c
new file mode 100644
index 0000000..438209c
--- /dev/null
+++ b/tools/rpmdeps.c
@@ -0,0 +1,105 @@
+#include "system.h"
+const char *__progname;
+
+#include <rpm/rpmbuild.h>
+#include <rpm/argv.h>
+#include <rpm/rpmds.h>
+#include <rpm/rpmfc.h>
+
+#include "debug.h"
+
+char *progname;
+
+static int print_provides;
+
+static int print_requires;
+
+static void rpmdsPrint(const char * msg, rpmds ds, FILE * fp)
+{
+ if (fp == NULL) fp = stderr;
+
+ if (msg)
+ fprintf(fp, "===================================== %s\n", msg);
+
+ ds = rpmdsInit(ds);
+ while (rpmdsNext(ds) >= 0)
+ fprintf(fp, "%s\n", rpmdsDNEVR(ds)+2);
+}
+
+static struct poptOption optionsTable[] = {
+
+ { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
+ N_("Common options for all rpm modes and executables:"),
+ NULL },
+
+ { "provides", 'P', POPT_ARG_VAL, &print_provides, -1,
+ NULL, NULL },
+ { "requires", 'R', POPT_ARG_VAL, &print_requires, -1,
+ NULL, NULL },
+
+ POPT_AUTOALIAS
+ POPT_AUTOHELP
+ POPT_TABLEEND
+};
+
+int
+main(int argc, char *argv[])
+{
+ poptContext optCon;
+ ARGV_t av = NULL;
+ rpmfc fc;
+ int ac = 0;
+ int ec = 1;
+char buf[BUFSIZ];
+
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ optCon = rpmcliInit(argc, argv, optionsTable);
+ if (optCon == NULL)
+ goto exit;
+
+ av = (ARGV_t) poptGetArgs(optCon);
+ ac = argvCount(av);
+
+ if (ac == 0) {
+ char * b, * be;
+ av = NULL;
+ while ((b = fgets(buf, sizeof(buf), stdin)) != NULL) {
+ buf[sizeof(buf)-1] = '\0';
+ be = b + strlen(buf) - 1;
+ while (strchr("\r\n", *be) != NULL)
+ *be-- = '\0';
+ argvAdd(&av, b);
+ }
+ ac = argvCount(av);
+ }
+
+ /* Make sure file names are sorted. */
+ argvSort(av, NULL);
+
+
+ /* Build file/package class and dependency dictionaries. */
+ fc = rpmfcCreate(getenv("RPM_BUILD_ROOT"), 0);
+ if (rpmfcClassify(fc, av, NULL) || rpmfcApply(fc))
+ goto exit;
+
+if (_rpmfc_debug) {
+rpmfcPrint(buf, fc, NULL);
+}
+
+ if (print_provides)
+ rpmdsPrint(NULL, rpmfcProvides(fc), stdout);
+ if (print_requires)
+ rpmdsPrint(NULL, rpmfcRequires(fc), stdout);
+
+ fc = rpmfcFree(fc);
+
+ ec = 0;
+
+exit:
+ optCon = rpmcliFini(optCon);
+ return ec;
+}
diff --git a/tools/rpmgraph.c b/tools/rpmgraph.c
new file mode 100644
index 0000000..bbb9d29
--- /dev/null
+++ b/tools/rpmgraph.c
@@ -0,0 +1,258 @@
+#include "system.h"
+const char *__progname;
+
+#include <rpm/rpmcli.h>
+#include <rpm/rpmlib.h> /* rpmReadPackageFile */
+#include <rpm/rpmdb.h>
+#include <rpm/rpmps.h>
+#include <rpm/rpmte.h>
+#include <rpm/rpmts.h>
+#include <rpm/rpmds.h>
+
+#include <rpm/rpmlog.h>
+#include <rpm/rpmfileutil.h>
+
+#include "lib/manifest.h"
+#include "debug.h"
+
+static int noDeps = 1;
+
+static rpmVSFlags vsflags = 0;
+
+static int
+rpmGraph(rpmts ts, struct rpmInstallArguments_s * ia, const char ** fileArgv)
+{
+ rpmps ps;
+ char ** pkgURL = NULL;
+ char * pkgState = NULL;
+ const char ** fnp;
+ char * fileURL = NULL;
+ int numPkgs = 0;
+ int numFailed = 0;
+ int prevx = 0;
+ int pkgx = 0;
+ char ** argv = NULL;
+ int argc = 0;
+ char ** av = NULL;
+ int ac = 0;
+ Header h;
+ rpmRC rpmrc;
+ int rc = 0;
+ rpmVSFlags ovsflags;
+ int i;
+
+ if (fileArgv == NULL)
+ return 0;
+
+ /* Build fully globbed list of arguments in argv[argc]. */
+ for (fnp = fileArgv; *fnp; fnp++) {
+ av = _free(av);
+ ac = 0;
+ rc = rpmGlob(*fnp, &ac, &av);
+ if (rc || ac == 0) continue;
+
+ argv = xrealloc(argv, (argc+2) * sizeof(*argv));
+ memcpy(argv+argc, av, ac * sizeof(*av));
+ argc += ac;
+ argv[argc] = NULL;
+ }
+ av = _free(av); ac = 0;
+
+restart:
+ /* Allocate sufficient storage for next set of args. */
+ if (pkgx >= numPkgs) {
+ numPkgs = pkgx + argc;
+ pkgURL = xrealloc(pkgURL, (numPkgs + 1) * sizeof(*pkgURL));
+ memset(pkgURL + pkgx, 0, ((argc + 1) * sizeof(*pkgURL)));
+ pkgState = xrealloc(pkgState, (numPkgs + 1) * sizeof(*pkgState));
+ memset(pkgState + pkgx, 0, ((argc + 1) * sizeof(*pkgState)));
+ }
+
+ /* Copy next set of args. */
+ for (i = 0; i < argc; i++) {
+ fileURL = _free(fileURL);
+ fileURL = argv[i];
+ argv[i] = NULL;
+
+ pkgURL[pkgx] = fileURL;
+ fileURL = NULL;
+ pkgx++;
+ }
+ fileURL = _free(fileURL);
+
+ /* Continue processing file arguments, building transaction set. */
+ for (fnp = (const char **) pkgURL+prevx; *fnp != NULL; fnp++, prevx++) {
+ const char * fileName;
+ FD_t fd;
+
+ (void) urlPath(*fnp, &fileName);
+
+ /* Try to read the header from a package file. */
+ fd = Fopen(*fnp, "r.ufdio");
+ if (fd == NULL || Ferror(fd)) {
+ rpmlog(RPMLOG_ERR, _("open of %s failed: %s\n"), *fnp,
+ Fstrerror(fd));
+ if (fd) {
+ Fclose(fd);
+ fd = NULL;
+ }
+ numFailed++; *fnp = NULL;
+ continue;
+ }
+
+ /* Read the header, verifying signatures (if present). */
+ ovsflags = rpmtsSetVSFlags(ts, vsflags);
+ rpmrc = rpmReadPackageFile(ts, fd, *fnp, &h);
+ ovsflags = rpmtsSetVSFlags(ts, ovsflags);
+ Fclose(fd);
+ fd = NULL;
+
+ switch (rpmrc) {
+ case RPMRC_FAIL:
+ default:
+ rpmlog(RPMLOG_ERR, _("%s cannot be installed\n"), *fnp);
+ numFailed++; *fnp = NULL;
+ break;
+ case RPMRC_OK:
+ rc = rpmtsAddInstallElement(ts, h, (fnpyKey)fileName, 0, NULL);
+ break;
+ case RPMRC_NOTFOUND:
+ goto maybe_manifest;
+ break;
+ }
+ h = headerFree(h);
+ continue;
+
+maybe_manifest:
+ /* Try to read a package manifest. */
+ fd = Fopen(*fnp, "r.fpio");
+ if (fd == NULL || Ferror(fd)) {
+ rpmlog(RPMLOG_ERR, _("open of %s failed: %s\n"), *fnp,
+ Fstrerror(fd));
+ if (fd) {
+ Fclose(fd);
+ fd = NULL;
+ }
+ numFailed++; *fnp = NULL;
+ break;
+ }
+
+ /* Read list of packages from manifest. */
+ rc = rpmReadPackageManifest(fd, &argc, &argv);
+ if (rc)
+ rpmlog(RPMLOG_NOTICE, _("%s: read manifest failed: %s\n"),
+ fileURL, Fstrerror(fd));
+ Fclose(fd);
+ fd = NULL;
+
+ /* If successful, restart the query loop. */
+ if (rc == 0) {
+ prevx++;
+ goto restart;
+ }
+
+ numFailed++; *fnp = NULL;
+ break;
+ }
+
+ if (numFailed > 0) goto exit;
+
+ if (!noDeps) {
+ rc = rpmtsCheck(ts);
+ if (rc) {
+ numFailed += numPkgs;
+ goto exit;
+ }
+ ps = rpmtsProblems(ts);
+ if (rpmpsNumProblems(ps) > 0) {
+ rpmlog(RPMLOG_ERR, _("Failed dependencies:\n"));
+ rpmpsPrint(NULL, ps);
+ numFailed += numPkgs;
+ }
+ ps = rpmpsFree(ps);
+ }
+
+ rc = rpmtsOrder(ts);
+ if (rc)
+ goto exit;
+
+ { rpmtsi pi;
+ rpmte p;
+ rpmte q;
+ int oType = TR_ADDED;
+
+ fprintf(stdout, "digraph XXX {\n");
+
+ fprintf(stdout, " rankdir=LR\n");
+
+ fprintf(stdout, "//===== Packages:\n");
+ pi = rpmtsiInit(ts);
+ while ((p = rpmtsiNext(pi, oType)) != NULL) {
+ q = rpmteParent(p);
+ if (q != NULL)
+ fprintf(stdout, " \"%s\" -> \"%s\"\n", rpmteN(p), rpmteN(q));
+ else {
+ fprintf(stdout, " \"%s\"\n", rpmteN(p));
+ fprintf(stdout, " { rank=max ; \"%s\" }\n", rpmteN(p));
+ }
+ }
+ pi = rpmtsiFree(pi);
+
+ fprintf(stdout, "}\n");
+ }
+
+ rc = 0;
+
+exit:
+ for (i = 0; i < numPkgs; i++)
+ pkgURL[i] = _free(pkgURL[i]);
+ pkgState = _free(pkgState);
+ pkgURL = _free(pkgURL);
+ argv = _free(argv);
+
+ return rc;
+}
+
+static struct poptOption optionsTable[] = {
+ { "check", '\0', POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN, &noDeps, 0,
+ N_("don't verify package dependencies"), NULL },
+ { "nolegacy", '\0', POPT_BIT_SET, &vsflags, RPMVSF_NEEDPAYLOAD,
+ N_("don't verify header+payload signature"), NULL },
+ { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
+ N_("Common options for all rpm modes and executables:"),
+ NULL },
+
+ POPT_AUTOALIAS
+ POPT_AUTOHELP
+ POPT_TABLEEND
+};
+
+int
+main(int argc, char *argv[])
+{
+ rpmts ts = NULL;
+ struct rpmInstallArguments_s * ia = &rpmIArgs;
+ poptContext optCon;
+ int ec = 0;
+
+ optCon = rpmcliInit(argc, argv, optionsTable);
+ if (optCon == NULL)
+ exit(EXIT_FAILURE);
+
+ ts = rpmtsCreate();
+ if (rpmcliQueryFlags & VERIFY_DIGEST)
+ vsflags |= _RPMVSF_NODIGESTS;
+ if (rpmcliQueryFlags & VERIFY_SIGNATURE)
+ vsflags |= _RPMVSF_NOSIGNATURES;
+ if (rpmcliQueryFlags & VERIFY_HDRCHK)
+ vsflags |= RPMVSF_NOHDRCHK;
+ (void) rpmtsSetVSFlags(ts, vsflags);
+
+ ec = rpmGraph(ts, ia, poptGetArgs(optCon));
+
+ ts = rpmtsFree(ts);
+
+ optCon = rpmcliFini(optCon);
+
+ return ec;
+}