package SegmentSet;

require Exporter;

@ISA = qw(Exporter);
@EXPORT_OK = qw( new );

use strict;
use Segment();

sub new($$)
{
  my $proto = shift;
  my $from_readelf = shift;

  my $class = ref($proto) || $proto;
  my $self  = {};

  $self->{PATTERN} = 'start address';
  $self->{FUNC} = \&Segment::from_objdump;
  $self->{FUNC_NAME} = 'Segment::from_objdump';

  if ($from_readelf)
  {
    $self->{PATTERN} = 'Entry point';
    $self->{FUNC} = \&Segment::from_readelf;
    $self->{FUNC_NAME} = 'Segment::readelf';
  }

  bless ($self, $class);
  return $self;
}

sub read($$;$)
{
  my $self = shift;
  my $ref_line = shift;

  my $entry_pattern = $self->{PATTERN};
  for(my $i = 0; $i < $#$ref_line; $i++)
  {
    my $line = $$ref_line[$i];
    if ($line =~ m/^$entry_pattern\s+/)
    {
      my @number = split / +/, $line;
      $self->{ENTRY_POINT} = hex($number[2]);
      next;
    }
    if ($line =~
      m/^There are (\d+) program headers, starting at offset (\d+)/)
    { # occurs only with 'readelf -l'
      $self->{e_phnum} = $1;
      $self->{e_phoff} = $2;
      next;
    }
    if ($line =~ m/^Program Headers?:/)
    {
      return $self->read_segments($ref_line, $i + 1)
    }
  }
  return 'No program headers.';
}

sub read_segments($$$)
{
  my $self = shift;
  my $ref_line = shift;
  my $start = shift;

  my $func = $self->{FUNC};
  my @segment;
  my @load;
  my %count_type;
  for(my $i = $start; $i <= $#$ref_line; $i++)
  {
    my $line = $$ref_line[$i];
    if ($line =~ m/^\s+([A-Z]+)\s+/)
    {
      my $segment = Segment->new();
      my $msg = $segment->$func($line, $$ref_line[$i + 1]);
      return $self->{FUNC_NAME} . ' ' . $msg if (defined($msg));

      push @segment, $segment;
      $count_type{$1}++;
      push(@load, $segment) if ($1 eq 'LOAD');
      next;
    }
    last if (0 == length($line));
  }
  $self->{SEGMENT} = \@segment;
  $self->{LOAD} = \@load;
  $self->{COUNT_TYPE} = \%count_type;
  return undef;
}
