#!/usr/bin/env perl
#
# $Id: genlop,v 1.21 2005/08/16 23:50:32 antonio Exp $
#
#    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
#

# TODO: dberkholz recommended changing --date <string> [--date <string>]
#       to --start-date and --end-date to alleviate confusion like
#       http://bugs.gentoo.org/show_bug.cgi?id=128194#c0

use strict;
use warnings;
use POSIX;
use Term::ANSIColor;
use Date::Manip;
use LWP::Simple;
use File::Basename;
use List::Util qw(min sum);

my $version  = "0.30.12";
my @logfiles = ();
my %COLORS   = (
	'blue'  => 'bold blue',
	'green' => 'bold green',
	'red'   => 'bold red',
);
my ($e_count, $w_count, $tm_secondi, $m_secondi) = (0, 0, 0, 0);

# variabili per la funzione parse
my (
	$date_found,
	$search_found,
	$unmerge_found,
	$list_found,
	$file_found,
	$time_found,
	$help_found,
	$current_found,
	$pretend_found,
	$version_found,
	$info_found,
	$gmt_found,
	$rsync_found,
	$info_ok,
	$info_target,
	$ssearch_found,
	$online_query,
	$secs,
	$mins,
	$hours,
	$days,
	$lhtomsg,
	$last_skipped,
	$eprefix,
	$log_dir
);

# variabili globali del programma
my ($e_start, $e_end, $search_string, $m_count, $ebuild_found);

# variabili di datecompare
my ($fh, $elog);
my @targets;
my $progname = basename($0);

my ($userdate1, $userdate2, $searchdate1, $searchdate2);

sub get_eprefix() {
	$eprefix = qx{portageq envvar EPREFIX};
	die "Couldn't determine EPREFIX from Portage" if $? != 0;
	chomp($eprefix);
}

sub get_logdir() {
	$log_dir = qx{portageq envvar EMERGE_LOG_DIR};
	chomp($log_dir);
	$log_dir = '/var/log' if ($log_dir eq "");
}

sub datecompare ($) 
{
	# datecompare( epoch )
	# returns -1 if epoch is outside searchdates 1 and 2
	# returns 1 if inside
	# returns undefined for errors
	# expects searchdate1 to be before searchdate2 and neither should be in
	# the future (but it's probably ok if searchdate2 is)
	die if (!$searchdate1);
	die if (!$searchdate2);
	my $epochdate = $_[0];
	if (($epochdate <=> $searchdate1) < 0)
	{
		# epoch is outside period
		return -1;
	}
	elsif (($epochdate <=> $searchdate2) > 0)
	{
		# epoch is outside period
		return -1;
	}
	else
	{
		return 1;
	}
	#TODO check that it's actually the case
}

# test a file, before opening it for reading
# second argument is a reference to a variable thet gets populated with
# a filehandle to the first argument
sub open_file
{
	my ($file, $fh) = @_;
	if ($file eq "$eprefix$log_dir/emerge.log" && !-r $file)
	{
		print "$progname: cannot open " . $file . " for reading\n"
			. "maybe you are not a member of the portage group ?\n"
			. "try genlop -h for help\n";
		exit 1;
	}
	if (-T $file)
	{
		open $$fh, '<', "$file"
			or die "could not open $file";
		return 0;
	}

	# if we got here file is unreadable, might simply be compressed...
	# let's try this
	my $nature = qx{file $file}
		or die "could not determine nature of (nonASCII) $file";
	if ($nature =~ /gzip/)
	{
		open $$fh, "gzip -d -c $file |"
			or die "could not open (gzipped) $file";
		return 0;
	}
	elsif ($nature =~ /bzip/)
	{
		open $$fh, "bzip2 -d -c $file |"
			or die "could not open (bzipped) $file";
		return 0;
	}
	else
	{
		# giving up...
		print "could not determine file type of $file\n";
		exit 1;
	}
}

# orders logfiles by date
sub order_logs (@)
{
	my @files = @_;
	my %ordered;
	foreach my $logfile (@files)
	{
		my $handle;
		open_file("$logfile", \$handle);
		my $fline = <$handle>;    # first line
		# Fix "Use of uninitialized value" if nothing is read
		if ($fline)
		{
			$ordered{$logfile} = (split /:/, $fline)[0];
		}
		else
		{
			$ordered{$logfile} = 0;
		}
	}
	return sort { $ordered{$a} <=> $ordered{$b} } keys %ordered;
}

# parse(arg), a hacked-up version of getopts
# sets (global) variables for options and returns.
# non option arguments are pushed into a (global) array
#
# FIXME Getopt::Long would be much better
sub parse ($)
{
	my $arg    = $_[0];
	my $nexist = 0;
	chomp $arg;

	# long (--foo) options
	if ($arg =~ /^--/)
	{
		LSWITCH:
		{
			$current_found = 1, last LSWITCH if ($arg eq "--current");
			$pretend_found = 1, last LSWITCH if ($arg eq "--pretend");
			$help_found    = 1, last LSWITCH if ($arg eq "--help");
			$time_found    = 1, last LSWITCH if ($arg eq "--time");
			$unmerge_found = 1, last LSWITCH if ($arg eq "--unmerge");
			$ENV{'ANSI_COLORS_DISABLED'} = 1, last LSWITCH
				if ($arg eq "--nocolor");
			$list_found    = 1, last LSWITCH if ($arg eq "--list");
			$version_found = 1, last LSWITCH if ($arg eq "--version");
			$search_found  = 1, last LSWITCH if ($arg eq "--search");
			$info_found    = 1, last LSWITCH if ($arg eq "--info");
			$gmt_found     = 1, last LSWITCH if ($arg eq "--gmt");
			$rsync_found   = 1, last LSWITCH if ($arg eq "--rsync");

			if ($arg eq "--date")
			{
				$date_found = 1;
				if (!$userdate1)
				{
					help() if !$ARGV[0];
					$userdate1 = ParseDate(\@ARGV);
					unless (UnixDate($userdate1, "%s"))
					{
						print color 'bold red';
						print "!!! Error:", " invalid date format (try mm/dd/yyyy)";
						print color 'reset';
						print "\n";
						exit -1;
					}
					if ((UnixDate($userdate1, "%s") <=> time) <= 0)
					{
						$searchdate1 = UnixDate($userdate1, "%s");
						$searchdate2 = time;
					}
					else
					{
						die "Date $userdate1 is in the future, not good\n";
					}
				}
				elsif (!$userdate2)
				{
					$userdate2 = ParseDate(\@ARGV);
					unless (UnixDate($userdate2, "%s"))
					{
						print color 'bold red';
						print "!!! Error:", " invalid date format (try mm/dd/yyyy)";
						print color 'reset';
						print "\n";
						exit -1;
					}
					if ((UnixDate($userdate1, "%s") <=> UnixDate($userdate2, "%s")) <= 0)
					{
						$searchdate2 = UnixDate($userdate2, "%s");
					}
					else
					{
						$searchdate2 = $searchdate1;
						$searchdate1 = UnixDate($userdate2, "%s");
					}
				}
				else
				{
					print "too many --date arguments ?\n";
					die;
				}
				last LSWITCH;
			}
			$nexist = $arg;
		}    # END LSWITCH
		if ($nexist)
		{

			# This is the standard error message
			print color 'bold red';
			print "!!! Error: $nexist, invalid option.";
			print color 'reset';
			print "\n";
			exit -1;
		}
		return 0;
	}

	# short bsd-style options
	if ($arg =~ /^-.*/)
	{
		until ($arg eq "-")
		{
			my $opt = chop($arg);
			SSWITCH:
			{
				$help_found                  = 1, last SSWITCH if ($opt eq "h");
				$help_found                  = 1, last SSWITCH if ($opt eq "?");
				$time_found                  = 1, last SSWITCH if ($opt eq "t");
				$unmerge_found               = 1, last SSWITCH if ($opt eq "u");
				$ENV{'ANSI_COLORS_DISABLED'} = 1, last SSWITCH if ($opt eq "n");
				$list_found                  = 1, last SSWITCH if ($opt eq "l");
				$search_found                = 1, last SSWITCH if ($opt eq "s");
				$version_found               = 1, last SSWITCH if ($opt eq "v");
				$info_found                  = 1, last SSWITCH if ($opt eq "i");
				$online_query                = 1, last SSWITCH if ($opt eq "q");
				$ssearch_found               = 1, last SSWITCH if ($opt eq "S");
				$current_found               = 1, last SSWITCH if ($opt eq "c");
				$pretend_found               = 1, last SSWITCH if ($opt eq "p");
				$rsync_found                 = 1, last SSWITCH if ($opt eq "r");
				$gmt_found                   = 1, last SSWITCH if ($opt eq "g");
				$ebuild_found                = 1, last SSWITCH if ($opt eq "e");

				if ($opt eq "f")
				{
					if (!$ARGV[0])
					{
						print color 'bold red';
						print "!!! Error: no logfile specified.";
						print color 'reset';
						print "\n";
						exit -1;
					}
					$file_found = 1;
					if (!-r $ARGV[0])
					{
						print color 'bold yellow';
						print "!!! Warning: logfile $ARGV[0] not readable or not found.";
						print color 'reset';
						print "\n";
						shift @ARGV;
						last SSWITCH;
					}
					print "using logfile " . $ARGV[0] . "\n";
					push @logfiles, shift(@ARGV);
					last SSWITCH;
				}
				$nexist = $opt;
			}    # END SSWITCH
		}
		if ($nexist)
		{
			print color 'bold red';
			print "!!! Error: \-$nexist, invalid option.";
			print color 'reset';
			print "\n";
			exit -1;
		}
		return 0;
	}
	push @targets, $arg;
	return 0;
}

# provides help information
sub help ()
{
	my $genlop  = colored("genlop ",          $COLORS{'blue'});
	my $options = colored("options",          $COLORS{'green'});
	my $f       = colored("-f",               $COLORS{'green'});
	my $catpkg  = colored("category/package", $COLORS{'green'});
	my $Options = colored("Options:",         $COLORS{'green'});
	my $dateStr = colored("--date",           $COLORS{'green'});

	my $help =<<HELP;
Usage: $genlop [$options] [$f logfile] [$catpkg]

$Options

  -c  display the currently compiling packages (if any)
  -e  display package history; default if any option is used.
  -f  read emerge log information from "logfile" instead of "$eprefix$log_dir/emerge.log"
  -h  print this help
  -i  extra infos for the selected package (build specific USE, CFLAGS,
      CXXFLAGS and LDFLAGS variables, average build time, etc)
  -g  display GMT/UTC, not localized time.
  -l  show full merge history.
  -n  no color in output
  -p  estimate build time from a piped "emerge -p" output
  -q  query gentoo.linuxhowtos.org database if no local emerge was found
  -r  search for portage tree sync/rsync history.
  -s  use (case insensitive) regular expressions to match package names
  -S  use case sensitive regular expressions to match package names
  -t  calculate merge time for the specific package(s).
  -u  show when packages have been unmerged.
  -v  display genlop version and exit.

  $dateStr datestring1 [$dateStr datestring2] only shows results between
    datestring1 and datestring2.
    datestring2 defaults to "now" if not explicitly set.
    (e.g. genlop --list --date 3 days ago) shows packages emerged since this
    time three days ago.

This program is licensed under the GPL v2. See COPYING.
For further info about genlop please read the man page.
HELP

	# Color each option green
	$help =~ s/^  (-.)/"  " . &colored($1, $COLORS{'green'})/gme;

	print $help;
	exit 0
}

sub gtime($)
{
	my $gtime = $_[0];
	chomp($gtime);
	$secs  = $gtime % 60;
	$gtime = ($gtime - $secs) / 60;
	$mins  = $gtime % 60;
	$gtime = ($gtime - $mins) / 60;
	$hours = $gtime % 24;
	$gtime = ($gtime - $hours) / 24;
	$days  = $gtime % 7;
	if ($gtime < 0)
	{
		$gtime = 0;
	}
}

sub print_gtime()
{
	if ($days > 0)
	{
		print colored("$days", $COLORS{'green'}), " day";
		print "s" if ($days > 1);
	}
	if ($hours > 0)
	{
		print ", " if ($days > 0);
		print colored("$hours", $COLORS{'green'}), " hour";
		print "s" if ($hours > 1);
	}
	if ($mins > 0)
	{
		print ", " if ($days > 0 or $hours > 0);
		print colored("$mins", $COLORS{'green'}), " minute";
		print "s" if ($mins > 1);
	}
	if ($mins < 1 && $hours < 1 && $days < 1 && $secs > 0 && $current_found)
	{
		print colored("less than a minute", $COLORS{'green'});
	}
	elsif ($mins < 1 && $hours < 1 && $days < 1 && $secs > 0 && $pretend_found)
	{
		print colored("less than a minute", $COLORS{'green'});
	}
	elsif ($secs > 0 && !$pretend_found)
	{
		print " and " if ($days > 0 or $hours > 0 or $mins > 0);
		print colored("$secs", $COLORS{'green'}), " second";
		print "s" if ($secs > 1);
	}
	print ".";
}

sub gen_regexp ($)
{
	# generates the correct regexp depending on what the user asked us.
	# default is to search only the correct package name (eg. mozilla)
	# a different regexp is needed in the following cases:
	# argument is in the form category/
	# argument is in the form category/ebuild
	# argument is in the form category/ebuild-version
	# the user can provide his own regular expression(s) via the -s option
	my $arg = $_[0];
	my ($category, $ebuild, $version);
	my $regexp;
	my @list;

	if ($list_found or $unmerge_found)
	{
		if ($arg =~ m{^=})
		{
			$arg =~ s{^=}{};
			$regexp = qr/(.*$arg).*?/;
		}
		else
		{
			$regexp = qr/(.*)(-[0-9]{1,7}.*?)/i;
		}
		return "$regexp";
	}
	if ($search_found)
	{

		# return user supplied regexp as-is
		if ($arg =~ m{^=})
		{
			$arg =~ s{^=}{};
			$regexp =
				$ssearch_found
				? qr/(.*$arg)(.*?)/
				: qr/(.*$arg)(.*?)/i;
		}
		else
		{
			$regexp =
				$ssearch_found
				? qr/(.*$arg.*?)(-[0-9]{1,7}.*?)/
				: qr/(.*$arg.*?)(-[0-9]{1,7}.*?)/i;
		}
		return "$regexp";
	}

	# check if we were asked only the category
	if ($arg =~ /.*?\/$/)
	{
		$category = $arg;
		$regexp =
			$ssearch_found
			? qr/($category.*?)(-[0-9]{1,7}.*?)/
			: qr/($category.*?)(-[0-9]{1,7}.*?)/i;
		return "$regexp";
	}
	@list = split(/\//, $arg);
	$ebuild = $list[0];
	if ($list[1])
	{
		$category = $list[0];
		$ebuild   = $list[1];
		@list     = ();
		@list     = split(/(-[0-9]{1,7})/, $ebuild);
		if ($list[1])
		{
			$ebuild = $list[0];
			$version = $list[2] ? join('', $list[1], $list[2]) : $list[1];
			$category =~ s{^=}{};
			$regexp =
				$ssearch_found
				? qr!($category\/$ebuild)($version)!
				: qr!($category\/$ebuild)($version)!i;
			return "$regexp";
		}
		$regexp =
			$ssearch_found
			? qr!($category\/$ebuild)(-[0-9]{1,7}.*?)!
			: qr!($category\/$ebuild)(-[0-9]{1,7}.*?)!i;
		return "$regexp";
	}
	$regexp =
		$ssearch_found
		? qr!(.*?/$ebuild)(-[0-9]{1,7}.*?)!
		: qr!(.*?/$ebuild)(-[0-9]{1,7}.*?)!i;
	return "$regexp";
}

# Submitted in bug 157103 by sascha to enable searching against linuxhowtos for compile
# times when genlop has no data to work with
sub lhtoquery($$)
{
	my ( $vcpu, $pcpu, $opcpu ) = (0,0,-1);
	my $modelname = "";
	my $cachesize;
	my $packet   = shift(@_);
	my $countref = shift(@_);
	open(my $cmdline, "/proc/cpuinfo");
	while (<$cmdline>)
	{
		if (m/processor\s*:\s*(\d*)/)
		{
			$vcpu = $1 if ($1 > $vcpu);
		}
		if (m/model name\s*:\s*(.*)$/)
		{
			$modelname = $1;
		}
		if (m/cache size\s*:\s*(.*)$/)
		{
			$cachesize = $1;
		}
		if (m/physical id\s*:\s*(\d*)$/)
		{
			$pcpu++ if ($1 != $opcpu);
			$opcpu = $1;
		}
	}
	$vcpu++;
	$pcpu = 1 if ($pcpu == 0);
	my $cpuname = $pcpu . "x $modelname $cachesize";
	$cpuname =~ s/ /%20/g;
	my $retval = LWP::Simple::get("http://gentoo.linuxhowtos.org/query.php?cpuname=$cpuname&vcpu=$vcpu&packetname=$packet");
	if ($retval =~ m/estimate: (\d*) seconds/)
	{
		$$countref = 1;
		return $1;
	}
	if ($retval =~ /unknown cpu/)
	{
		$lhtomsg = "Your CPU is not yet known, please add it by following the instructions on http://gentoo.linuxhowtos.org/compiletimeestimator/";
	}
	return 0;
}

# --pretend or -p takes an emerge -p `-e -D world, system`, anything you want
# and elaborates its output. for each package is calculated the average build
# time and summed together. this is the estimated merge time
sub pretend()
{
	if ($pretend_found)
	{
		@targets = ();
		print "These are the pretended packages:";
		print " (this may take a while; wait...)\n\n";

		# open STDIN; that's why emerge -p foo is piped to a genlop -p
		while (<STDIN>)
		{
			if ($_ =~ m/^\[e.*\] (.*?)\/(.*?)(\-[0-9])/)
			{
				push @targets, $2;
				print;
			}
		}
		my $last_ebuild;
		foreach my $ebuild_arg (@targets)
		{

			# we track the last ebuild processed with $last_ebuild variable
			$last_ebuild = $ebuild_arg;
			$ebuild_arg =~ s/(\+)/\\$1/g;
			foreach my $logfile (@logfiles)
			{
				my $handle;
				open_file($logfile, \$handle);
				while (<$handle>)
				{
					if (m/^(.*?)\:  \>\>\> emerge.*?\/$ebuild_arg-[0-9].*/)
					{
						$e_start = $1;
					}
					if (m/^(.*?)\:  ::: completed .*?\) .*\/$ebuild_arg-[0-9].* to \//)
					{
						$e_end = $1;
						$tm_secondi += ($e_end - $e_start);
						$e_count++;
					}
				}
			}
			if ($e_count == 0)
			{
				if ($online_query)
				{
					#query gentoo.linuxhowtos.org
					$tm_secondi += lhtoquery($last_ebuild, \$e_count);
				}
			}
			if ($e_count == 0)
			{
				$ebuild_arg =~ s/\\//g;
				print "\n!!! Error: couldn't get previous ", "merge of $ebuild_arg; skipping...";

				# if a pretended package haven't any successfull merge
				# stored in logfile (ie a new package required by
				# another, or a logfile corruption), prints a warning
				# and keep track with $last_skipped
				$last_skipped = $ebuild_arg;
			}
			else
			{
				$m_secondi += $tm_secondi / ($e_count);
				$e_count      = 0;
				$tm_secondi   = 0;
				$last_skipped = "none-skipped";
			}
		}
		if (@targets)
		{
			if ($last_ebuild =~ m/$last_skipped/)
			{
				print color 'bold red';
				print "\n!!! Error: $last_skipped never merged; ", "estimated time unknown.";
				print color 'reset';
				print "\n";
				if ($lhtomsg)
				{
					print color 'bold yellow';
					print "$lhtomsg\n";
					print color 'reset';
				}
				exit;
			}
			print "\n\nEstimated update time: ";
			&gtime($m_secondi);
			&print_gtime;
			print "\n";
		}
		else
		{
			print color 'bold red';
			print "\n!!! Error: no pretended packages found.";
			print color 'reset';
			print "\n";
		}
		exit;
	}
}

sub current()
{
	# support for 'current' merge.
	#
	# this whole 'current' thing is based on having sandboxind enabled
	# we need to check for it, basically sandboxing is on if
	# FEATURES contains 'sandbox' and does not contain 'userpriv'
	# FEATURES contains 'sandbox' and contains both 'userpriv' and 'usersandbox'
	# 20050815 - JeR: On slow systems, running portageq takes a lot of time,
	# sometimes enough to miss all the sandbox action completely. Better to
	# not check for sanity and have users check their FEATURES instead.
	my @merge_times  = ();
	my @targets      = ();
	my @sandbox_pids = ();
	my @sandbox_procs = qx{ps ax -o pid,args | tail -n +2 | sed -e's/^ *//' | grep ' sandbox ' | grep -v -e ' grep ' -e 'pid-ns-init '};
	my ($e_curmerge, $e_lastmerge);
	foreach (@sandbox_procs)
	{
		if (m/^(.*?) \[(.*?)\-[0-9].*?\]/)
		{
			push @sandbox_pids, $1;
			push @targets, $2;
		}
	}
	if (scalar @sandbox_pids == 0)
	{
		print colored("!!!", $COLORS{'red'});
		print " Error: no working merge found.\n";
		print "(the -c option only works if there is" . " an ongoing compilation, see manpage)\n";
		exit;
	}
	if (scalar @targets == 0)
	{
		print colored("!!!", $COLORS{'red'});
		print "oops! should not happen, please file a bug\n";
		print "empty \@targets\n";
		exit 1;
	}
	foreach my $ebuild_arg (@targets)
	{
		my $e_current;
		$e_count = 0;
		$tm_secondi = 0;
		$ebuild_arg =~ s/(\+)/\\$1/g;
		(my $cat = $ebuild_arg) =~ s/\/.*//g;
		$ebuild_arg =~ s/.*\///g;
		foreach my $logfile (@logfiles)
		{
			my $handle;
			open_file($logfile, \$handle);
			while (<$handle>)
			{
				if (m/^(.*?)\:  \>\>\> emerge \((.*?) of (.*?)\)(.*?$cat\/$ebuild_arg-[0-9].*?)to \//)
				{
					$e_start     = $1;
					$e_curmerge  = $2;
					$e_lastmerge = $3;
					$e_current   = $4;
				}
				if (m/^(.*?)\:  ::: completed .*?\) .*$cat\/$ebuild_arg-[0-9].* to \//)
				{
					$e_end = $1;
					$e_count++;
					&gtime($e_end - $e_start);
					$tm_secondi += ($e_end - $e_start);
					unshift(@merge_times, ($e_end - $e_start));
				}
			}
		}
		$e_end = CORE::time();
		&gtime($e_end - $e_start);
		print "\n Currently merging $e_curmerge out of $e_lastmerge\n";
		print colored("\n \*$e_current\n\n", $COLORS{'blue'});
		print "       current merge time: ";
		$current_found = undef;
		&print_gtime();
		$current_found = 1;
		print "\n";
		print "       ETA: ";

		if (!$e_count && $online_query)
		{

			#query gentoo.linuxhowtos.org
			$tm_secondi = lhtoquery($ebuild_arg, \$e_count);
			$e_count = 1;
		}

		if ($e_count && $e_start)
		{
			if ($e_count > 1)
			{
				# For a better prediction we only consider the last 10 merges
				# to have a better bias to latest system changes.
				my $num_elems = min(9, $#merge_times);
				@merge_times = @merge_times[0..$num_elems];

				# Now slice off the worst and best performing time to create
				# a better prediction by removing exceptional performers.
				@merge_times = sort(@merge_times);
				@merge_times = @merge_times[1..8] if $#merge_times > 8;

				$e_count = 1;
				$tm_secondi = sum(@merge_times) / ($#merge_times + 1);
			}

			&gtime($tm_secondi - ($e_end - $e_start));
			if (($e_end - $e_start) >= $tm_secondi)
			{
				print colored("any time now.\n", $COLORS{'green'});
			}
			else
			{
				&print_gtime();
				print "\n";
			}
		}
		else
		{
			print color 'bold red';
			print "unknown.";
			print color 'reset';
			print "\n";
		}
	}
	exit;
}

sub info($)
{
	my $package = $_[0];
	if ($list_found) { &help(); }
	if ($e_count) { $m_count = $e_count - $w_count; }
	if ($m_count == 0)
	{
		print colored("Total merge time unknown.\n\n", $COLORS{'red'});
	}
	else
	{
		print "\n   Total builds: ", colored("$e_count", $COLORS{'green'});
		print "\n   Global build time: ";
		&gtime($tm_secondi);
		&print_gtime();
		if ($w_count)
		{
			print " Global build time of $m_count merges.\n";
		}
		else
		{
			print "\n";
		}
		if ($e_count > 1)
		{
			print "   Average merge time: ";
			&gtime($tm_secondi / $m_count);
			&print_gtime();
			print "\n";
		}
		$e_count    = 0;
		$tm_secondi = 0;

		#$gtime      = 0;
	}
	$e_count = 0;
	print "\n   Info about currently installed ebuild:\n";
	opendir(DIR, "$eprefix/var/db/pkg/") || die "can't open $eprefix/var/db/pkg/ $!\n";
	while (defined(my $categoria = readdir(DIR)))
	{
		if ($package =~ m/^$categoria.*/g)
		{
			opendir(DIR2, "$eprefix/var/db/pkg/$categoria");
			while (defined(my $package_dir = readdir(DIR2)))
			{

				#$package =~ s/(\+)/\\$1/g;
				my $tmp_package = $package;
				$tmp_package =~ s/\+/\\+/g;
				if ("$categoria/$package_dir" =~ m/$tmp_package\-[0-9].*/)
				{
					$info_ok = 1;
					print colored("\n   * $categoria/$package_dir\n", $COLORS{'blue'});
					$package_dir =~ s/(\+)/\\$1/g;

					my $pattern = gen_regexp("$categoria/$package_dir");
					my $e_date;
					foreach my $logfile (@logfiles)
					{
						my $handle;
						open_file($logfile, \$handle);
						while (<$handle>)
						{
							if (m/^([0-9]{10})\:  ::: completed .*?\) $pattern to \//)
							{
								if ($gmt_found)
								{
									$e_date = scalar gmtime "$1";
								}
								else
								{
									$e_date = scalar localtime "$1";
								}
							}
						}
					}
					print "   Install date: ";
					print colored("$e_date\n", $COLORS{'green'});

					# we use 3 array to collect data: before processing they are
					# 	@unused_use: contain packages' USEs
					#	@pkg_use: USE declared before compiling that package
					#	@used_use: empty
					my (@potential_use, @pkg_use, @used_use, @unused_use);

					# each installed package store its information here
					my $db_pkg_dir = "$eprefix/var/db/pkg/$categoria/$package_dir/";
					if ("$categoria/$package_dir" =~ m/.*\/(.*)/g)
					{

						# we search into the installed ebuild for USE flags available
						# and store them in @potential_use.
						open(pkg_ebuild, "$db_pkg_dir/$1.ebuild") || return;
						while (<pkg_ebuild>)
						{
							if ($_ =~ m/^IUSE=\"(\$\{IUSE\} )?(.*)"/g)
							{
								@potential_use = split(/\ /, $2);
							}
						}
					}

					# this file lists every USE flag defined, even ones in make.conf
					# we push'em in @pkg_use
					open(pkg_use, "$db_pkg_dir/USE") || return;
					while (<pkg_use>)
					{
						@pkg_use = split(/\ /, $_);
					}

					# for every possible package USE we search into USEs stored in @pkg_use
					# if a match is found we move it from @potential_use in @used_use.
					# in this way, when every possible package USE are processed, @used_use
					# contain only used ones and @potential_use the not used ones.
					USE: foreach my $use (@potential_use)
					{
						chomp($use);
						foreach my $pkg (@pkg_use)
						{
							chomp($pkg);
							if ($use eq $pkg)
							{
								push(@used_use, $use);
								next USE;
							}
						}
						push(@unused_use, $use);
					}

					# finally we print 'em out
					print "   USE=\"", colored("@used_use", $COLORS{'red'});
					foreach my $unused (@unused_use)
					{
						print colored(" -$unused", $COLORS{'blue'});
					}
					print "\"\n";

					# easy work here: we simply print the CFLAGS, CXXFLAGS, and LDFLAGS files
					print "   CFLAGS=\"";
					if (open(pkg_cflag, "$db_pkg_dir/CFLAGS")) {
						while (<pkg_cflag>)
						{
							chomp();
							print();
						}
					}
					print "\"   CXXFLAGS=\"";
					if (open(pkg_cxxflag, "$db_pkg_dir/CXXFLAGS")) {
						while (<pkg_cxxflag>)
						{
							chomp();
							print();
						}
					}
					print "\"   LDFLAGS=\"";
					if (open(pkg_ldflag, "$db_pkg_dir/LDFLAGS")) {
						while (<pkg_ldflag>)
						{
							chomp();
							print();
						}
					}
					print "\"\n";
				}
			}
		}
	}
	if (!$info_ok) { print "   none installed.\n"; }
}

sub rsync() {
	foreach (@logfiles) {
		my $handle;
		open_file($_, \$handle);
		while(<$handle>) {
			if ($_ =~ m/^(.*?)\: \=\=\= Sync completed/) {
				if ($date_found) {
					if (datecompare($1) <= 0) {
						next;
					}
				}

				if ($gmt_found) {
					print "     rsync'ed at >>> ", colored((scalar gmtime "$1"), $COLORS{'green'}),"\n";
				}
				else {
					print "     rsync'ed at >>> ", colored((scalar localtime "$1"), $COLORS{'green'}),"\n";
				}
			}
			
			if ($_ =~ m/^(.*?)\: \>\>\> Git pull in .*successful$/) {
				if ($date_found) {
					if (datecompare($1) <= 0) {
						next;
					}
				}

				if ($gmt_found) {
					print "     Git pulled at >>> ", colored((scalar gmtime "$1"), $COLORS{'green'}),"\n";
				}
				else {
					print "     Git pulled at >>> ", colored((scalar localtime "$1"), $COLORS{'green'}),"\n";
				}
			}
		}
		close($handle);
	}

	print color 'reset';

	return 0;
}

#######
# *Start*
#######

get_eprefix;
get_logdir;
help() if (!$ARGV[0]);

# parse arguments
parse(shift @ARGV) while ($ARGV[0]);

help() if ($help_found);
if ($version_found)
{
	print <<VERSION;
genlop $version
original code by Giorgio Mandolfo and Antonio Dolcetta

Please file any bugs found online at:
	https://bugs.gentoo.org

Distributed under the GPL v2. See COPYING for details
VERSION
	exit 0;
}

if (    !$targets[0]
	and !$list_found
	and !$unmerge_found
	and !$current_found
	and !$pretend_found
	and !$rsync_found)
{
	help();
}

# FIXME questi a cosa servono ?
if ($rsync_found)   { @targets = (); push @targets, "-r"; }
if ($list_found)    { @targets = (); push @targets, "-l"; }
if ($unmerge_found) { @targets = (); push @targets, "-u"; }
if ($pretend_found) { @targets = (); push @targets, "-p"; }
if ($current_found) { @targets = (); push @targets, "-c"; }

# main code...
#cache_files(\@logfiles, \@logfile_cache);

# Add $log_dir/emerge.log if no log file(s) specified on command line
if (scalar @logfiles == 0)
{
	if ($file_found)
	{
		print color "bold yellow";
		print "!!! Warning: No readable log files found, using $eprefix$log_dir/emerge.log";
		print color 'reset';
		print "\n";
	}
	push @logfiles, "$eprefix$log_dir/emerge.log";
}

if (scalar @logfiles > 1)
{
	@logfiles = order_logs(@logfiles);
}

# - Option -r given? >
if ($rsync_found) {
	rsync();
	exit(0);
}

foreach my $ebuild_arg (@targets)
{

	# this is for packages like gtk+
	$ebuild_arg =~ s/(\+)/\\$1/g;
	my $pattern = gen_regexp($ebuild_arg);

	foreach my $logfile (@logfiles)
	{
		my $handle;
		open_file($logfile, \$handle);
		while (<$handle>)
		{
			if ($date_found)
			{
				if ($_ =~ m/^([0-9]{10})\:/)
				{
					if (datecompare($1) <= 0)
					{
						next;
					}
				}
			}
			if ($pretend_found) { &pretend; }
			if ($current_found) { &current; }
			if ($time_found or $info_found)
			{
				if ($_ =~ m/^([0-9]{10})\:  \>\>\> emerge .*?\) $pattern/)
				{
					$e_start     = $1;
					$info_target = $2;
				}
			}
			if ($_ =~ m/^([0-9]{10})\:  ::: completed .*?\) $pattern to \//)
			{
				my $e_date;
				if ($gmt_found)
				{
					$e_date = scalar gmtime "$1";
				}
				else
				{
					$e_date = scalar localtime "$1";
				}
				$e_end = $1;
				if ($time_found or $info_found)
				{
					&gtime($e_end - $e_start);
					if ($e_end - $e_start > 0)
					{
						$tm_secondi += ($e_end - $e_start);
					}
					else
					{
						$tm_secondi += 0;
					}
				}
				if (!$e_count)
				{
					# TODO: this bunch of repeated code can be refactored.
					my $p_ebuild = " \* $2\n\n";
					$p_ebuild =~ s/\\//g;
					if ($search_found)
					{
						print colored(" \* matches found:\n\n", $COLORS{'blue'});
					}
					elsif ($list_found && $unmerge_found)
					{
						print colored(" \* packages merged and unmerged:\n\n", 
							$COLORS{'blue'});
					}
					elsif ($list_found)
					{
						print colored(" \* packages merged:\n\n", $COLORS{'blue'});
					}
					elsif ($unmerge_found)
					{
						print colored(" \* packages unmerged:\n\n", $COLORS{'blue'});
					}
					else
					{
						if ($ebuild_arg =~ m/\/$/)
						{
							print colored(" \* $ebuild_arg\n\n", $COLORS{'blue'});
						}
						else
						{
							print colored("$p_ebuild", $COLORS{'blue'});
						}
					}
				}
				if ($list_found or $ebuild_found or $time_found or !$info_found)
				{
					my $eb = $2;
					my $extra = $3 || "";
					print "     $e_date >>>", colored(" $eb$extra\n", $COLORS{'green'});
				}
				if ($time_found)
				{
					print "       merge time: ";
					if (($e_end - $e_start) > 0)
					{
						&print_gtime();
						print "\n\n";
					}
					else
					{
						print color 'bold red';
						print "log error; merge time unknown.";
						print color 'reset';
						print "\n\n";
						$w_count++;
					}
				}
				$e_count++;
			}
			if ($unmerge_found or $info_found)
			{
				if (m/^([0-9]{10})\:  \>\>\> unmerge success: ($pattern.*)/g)
				{
					my $u_date;
					if ($gmt_found)
					{
						$u_date = scalar gmtime "$1";
					}
					else
					{
						$u_date = scalar localtime "$1";
					}
					if ($unmerge_found)
					{
						print "     $u_date <<<", colored(" $2\n", $COLORS{'red'});
					}
				}
			}
		}
	}
	unless ($e_count or $unmerge_found or $list_found or $rsync_found)
	{
		if ($e_count == 0)
		{
			if ($online_query)
			{

				#query gentoo.linuxhowtos.org
				$tm_secondi += lhtoquery($ebuild_arg, \$e_count);
			}
		}
		if ($e_count > 0)
		{
			print "Estimated merge time: ";
			&gtime($tm_secondi);
			&print_gtime();
		}
		else
		{
			print color 'bold red';
			print "!!! Error: no merge found for \'$ebuild_arg\'";
			print color 'reset';
		}
		print "\n";
	}
	elsif ($info_found)
	{
		&info($info_target);
	}
	else
	{
		$e_count = 0;
	}
}

if ($lhtomsg)
{
	print color 'bold yellow';
	print "$lhtomsg\n";
	print color 'reset';
}
