#!/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 [--date ] # 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 = "9999"; 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 =< 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 () { 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: "; >ime($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++; >ime($e_end - $e_start); $tm_secondi += ($e_end - $e_start); unshift(@merge_times, ($e_end - $e_start)); } } } $e_end = CORE::time(); >ime($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); } >ime($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: "; >ime($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: "; >ime($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 () { 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 = 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 () { chomp(); print(); } } print "\" CXXFLAGS=\""; if (open(pkg_cxxflag, "$db_pkg_dir/CXXFLAGS")) { while () { chomp(); print(); } } print "\" LDFLAGS=\""; if (open(pkg_ldflag, "$db_pkg_dir/LDFLAGS")) { while () { 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 < 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) { ¤t; } 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) { >ime($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: "; >ime($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'; }