/* Copyright (C) 1998, 1999, 2000, 2002 Red Hat, Inc.
   Written by Ulrich Drepper <drepper@redhat.com>, 1998.

   This program is Open Source software; you can redistribute it and/or
   modify it under the terms of the Open Software License version 1.0 as
   published by the Open Source Initiative.

   You should have received a copy of the Open Software License along
   with this program; if not, you may obtain a copy of the Open Software
   License version 1.0 from http://www.opensource.org/licenses/osl.php or
   by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
   3001 King Ranch Road, Ukiah, CA 95482.   */

#include <config.h>

#include <error.h>
#include <fcntl.h>
#include <gelf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>


static const char *machines[] =
{
#define MACHINE(name) [name] = #name
  MACHINE (EM_NONE),
  MACHINE (EM_M32),
  MACHINE (EM_SPARC),
  MACHINE (EM_386),
  MACHINE (EM_68K),
  MACHINE (EM_88K),
  MACHINE (EM_860),
  MACHINE (EM_MIPS),
  MACHINE (EM_MIPS_RS3_LE),
  MACHINE (EM_PARISC),
  MACHINE (EM_VPP500),
  MACHINE (EM_SPARC32PLUS),
  MACHINE (EM_960),
  MACHINE (EM_PPC),
  MACHINE (EM_PPC64),
  MACHINE (EM_V800),
  MACHINE (EM_FR20),
  MACHINE (EM_RH32),
  MACHINE (EM_RCE),
  MACHINE (EM_ARM),
  MACHINE (EM_FAKE_ALPHA),
  MACHINE (EM_SH),
  MACHINE (EM_SPARCV9),
  MACHINE (EM_TRICORE),
  MACHINE (EM_ARC),
  MACHINE (EM_H8_300),
  MACHINE (EM_H8_300H),
  MACHINE (EM_H8S),
  MACHINE (EM_H8_500),
  MACHINE (EM_IA_64),
  MACHINE (EM_MIPS_X),
  MACHINE (EM_COLDFIRE),
  MACHINE (EM_68HC12),
  MACHINE (EM_MMA),
  MACHINE (EM_PCP),
  MACHINE (EM_NCPU),
  MACHINE (EM_NDR1),
  MACHINE (EM_STARCORE),
  MACHINE (EM_ME16),
  MACHINE (EM_ST100),
  MACHINE (EM_TINYJ),
  MACHINE (EM_FX66),
  MACHINE (EM_ST9PLUS),
  MACHINE (EM_ST7),
  MACHINE (EM_68HC16),
  MACHINE (EM_68HC11),
  MACHINE (EM_68HC08),
  MACHINE (EM_68HC05),
  MACHINE (EM_SVX),
  MACHINE (EM_ST19),
  MACHINE (EM_VAX)
};


int
main (int argc, char *argv[])
{
  int fd;
  Elf *elf;
  Elf_Cmd cmd;
  size_t n;
  int arg = 1;
  int verbose = 0;

  /* Recognize optional verbosity flag.  */
  if (arg < argc && strcmp (argv[arg], "-v") == 0)
    {
      verbose = 1;
      ++arg;
    }

  /* Any more arguments available.  */
  if (arg >= argc)
    error (EXIT_FAILURE, 0, "No input file given");

  /* Open the input file.  */
  fd = open (argv[arg], O_RDONLY);
  if (fd == -1)
    {
      perror ("cannot open input file");
      exit (1);
    }

  /* Set the ELF version we are using here.  */
  if (elf_version (EV_CURRENT) == EV_NONE)
    {
      puts ("ELF library too old");
      exit (1);
    }

  /* Start reading the file.  */
  cmd = ELF_C_READ;
  elf = elf_begin (fd, cmd, NULL);
  if (elf == NULL)
    {
      printf ("elf_begin: %s\n", elf_errmsg (-1));
      exit (1);
    }

  /* If it is no archive punt.  */
  if (elf_kind (elf) != ELF_K_AR)
    {
      printf ("%s is not an archive\n", argv[1]);
      exit (1);
    }

  if (verbose)
    {
      /* The verbose variant.  We print a lot of information.  */
      Elf *subelf;
      char buf[100];
      time_t t;

      /* Get the elements of the archive one after the other.  */
      while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
	{
	  /* The the header for this element.  */
	  Elf_Arhdr *arhdr = elf_getarhdr (subelf);

	  if (arhdr == NULL)
	    {
	      printf ("cannot get arhdr: %s\n", elf_errmsg (-1));
	      break;
	    }

	  switch (elf_kind (subelf))
	    {
	    case ELF_K_ELF:
	      fputs ("ELF file:\n", stdout);
	      break;

	    case ELF_K_AR:
	      fputs ("archive:\n", stdout);
	      break;

	    default:
	      fputs ("unknown file:\n", stdout);
	      break;
	    }

	  /* Print general information.  */
	  t = arhdr->ar_date;
	  strftime (buf, sizeof buf, "%Y-%m-%dT%H:%M:%S%z", gmtime (&t));
	  printf ("  name         : \"%s\"\n"
		  "  time         : %s\n"
		  "  uid          : %ld\n"
		  "  gid          : %ld\n"
		  "  mode         : %o\n"
		  "  size         : %ld\n"
		  "  rawname      : \"%s\"\n",
		  arhdr->ar_name,
		  buf,
		  (long int) arhdr->ar_uid,
		  (long int) arhdr->ar_gid,
		  arhdr->ar_mode,
		  (long int) arhdr->ar_size,
		  arhdr->ar_rawname);

	  /* For ELF files we can provide some more information.  */
	  if (elf_kind (subelf) == ELF_K_ELF)
	    {
	      GElf_Ehdr ehdr;

	      /* Get the ELF header.  */
	      if (gelf_getehdr (subelf, &ehdr) == NULL)
		printf ("  *** cannot get ELF header: %s\n", elf_errmsg (-1));
	      else
		{
		  printf ("  binary class : %s\n",
			  ehdr.e_ident[EI_CLASS] == ELFCLASS32
			  ? "ELFCLASS32" : "ELFCLASS64");
		  printf ("  data encoding: %s\n",
			  ehdr.e_ident[EI_DATA] == ELFDATA2LSB
			  ? "ELFDATA2LSB" : "ELFDATA2MSB");
		  printf ("  binary type  : %s\n",
			  ehdr.e_type == ET_REL
			  ? "relocatable"
			  : (ehdr.e_type == ET_EXEC
			     ? "executable"
			     : (ehdr.e_type == ET_DYN
				? "dynamic"
				: "core file")));
		  printf ("  machine      : %s\n",
			  (ehdr.e_machine >= (sizeof (machines)
					      / sizeof (machines[0]))
			   || machines[ehdr.e_machine] == NULL)
			  ? "???"
			  : machines[ehdr.e_machine]);
		}
	    }

	  /* Get next archive element.  */
	  cmd = elf_next (subelf);
	  if (elf_end (subelf) != 0)
	    printf ("error while freeing sub-ELF descriptor: %s\n",
		    elf_errmsg (-1));
	}
    }
  else
    {
      /* The simple version.  Only print a bit of information.  */
      Elf_Arsym *arsym = elf_getarsym (elf, &n);

      if (n == 0)
	printf ("no symbol table in archive: %s\n", elf_errmsg (-1));
      else
	{
	  --n;

	  while (n-- > 0)
	    printf ("name = \"%s\", offset = %ld, hash = %lx\n",
		    arsym[n].as_name, (long int) arsym[n].as_off,
		    arsym[n].as_hash);
	}
    }

  /* Free the ELF handle.  */
  if (elf_end (elf) != 0)
    printf ("error while freeing ELF descriptor: %s\n", elf_errmsg (-1));

  /* Close the underlying file.  */
  close (fd);

  return 0;
}