commit:     c74a5abf3fd8db03adb531f95ecff5316d997ab3
Author:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Wed Feb 27 20:41:45 2019 +0000
Commit:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Wed Feb 27 20:50:21 2019 +0000
URL:        https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=c74a5abf

qlop: rewrite from scratch

This new implementation achieves a few things:
- unified code path for all modes of operation (thus consistent results)
- more flexibility to implement new features
- simplification of the codebase

Short version of what this commit changes:
- existing flags -t -g have been replaced with -a and -t
- -c has been been replaced with -r and no longer uses proc processing
  code (thus works everywhere)
- addition of an ETA for running emerges (subject to improvements)
- allow reading a file of atoms (e.g. /var/lib/portage/world)
- summary mode -c to compute grand total, e.g. to compute world compile
  time

Bug: https://bugs.gentoo.org/161244
Bug: https://bugs.gentoo.org/603024
Bug: https://bugs.gentoo.org/636334
Bug: https://bugs.gentoo.org/658824
Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>

 man/include/qlop.desc         |   25 +-
 man/include/qlop.optdesc.yaml |   23 +-
 man/qlop.1                    |   66 +-
 qlop.c                        | 1479 +++++++++++++++++++++--------------------
 tests/qlop/dotest             |   20 +-
 tests/qlop/list01.good        |    4 +-
 tests/qlop/list02.good        |    6 +-
 tests/qlop/list03.good        |    8 +-
 tests/qlop/list04.good        |    2 +-
 tests/qlop/list05.good        |    2 +-
 tests/qlop/list06.good        |    4 +-
 tests/qlop/list07.good        |    4 +-
 tests/qlop/list08.good        |    4 +-
 tests/qlop/list09.good        |    6 +-
 14 files changed, 892 insertions(+), 761 deletions(-)

diff --git a/man/include/qlop.desc b/man/include/qlop.desc
index d99cc94..0c14e00 100644
--- a/man/include/qlop.desc
+++ b/man/include/qlop.desc
@@ -1,4 +1,25 @@
 .I qlop
 reads from $EMERGE_LOG_DIR/emerge.log and tries to extract
-information about merges, unmerges and syncs.  For packages, it can
-calculate average merge times or just list them.
+information about merges, unmerges and syncs.  It can
+calculate average merge times or just list them.  When given no
+arguments or just \fB-v\fR, \fIqlop\fR acts as if \fB-must\fR was given
+and thus lists the time taken for all occurrances of merges, unmerges
+and sync operations found in the log.
+.P
+By default, packages are printed as CATEGORY/PN.  Use \fB-v\fR to print
+the package version and revision numbers, e.g\. CATEGORY/PF.  Note that
+when using this option, averages (\fB-a\fR) are reported per version
+instead of per package.
+.P
+The non-option arguments to \fIqlop\fR can be any valid atoms.  Multiple
+can be given to match.  Since all of these are treated as atoms, version
+specifications can be used such as \fB<bash-5\fR.  This allows to zoom
+in further on specific packages.
+.P
+After version \fB0.74\fR of portage-utils, \fIqlop\fR was changed
+considerably to be more consistent and more advanced.  Most notably,
+this has changed default date output and commmand line flags.  Instead
+of reporting the time the operation finished, \fIqlop\fR now reports the
+time the operation started.  The behaviour of the old \fB-q\fR flag is
+best matched by the new \fB-t\fR flag.  Similar, the old \fB-t\fR flag
+is matched by the new \fB-a\fR flag.

diff --git a/man/include/qlop.optdesc.yaml b/man/include/qlop.optdesc.yaml
index 3ff62c3..a8fae61 100644
--- a/man/include/qlop.optdesc.yaml
+++ b/man/include/qlop.optdesc.yaml
@@ -18,18 +18,27 @@ date: |
     .IP FORMAT|DATE
     Use \fIFORMAT\fR as input for \fBstrptime\fR(3) to parse \fIDATE\fR.
     .RE
-gauge: |
-    Gauge number of times a package has been merged.  This shows the
-    merge time for each individual merge of package.
 time: |
-    Calculate merge time for a specific package.  This is the average
-    time for all merges of package.
+    Show the time it took to merge, unmerge or sync.
+average: |
+    Calculate average merge, unmerge or sync time.  This is the average
+    time for all occurrences found respecting any date limits.
+summary: |
+    Show a grand total for averages.  This option implies \fB-a\fR.
+    Useful to compute time it takes to recompile all packages or a set
+    of packages (e.g\. world).
 human: |
-    Print seconds in human readable format (needs \fB\-t\fR), using
+    Print elapssed time in human readable format.  This form uses
     minutes, hours and days instead of just seconds.
 current: |
     Show current emerging packages.  This relies on
     .I FEATURES=sandbox
     in order to detect running merges.
 verbose: |
-    Print package versions and revisions.
+    Print package versions and revisions (PF) instead of package (PN).
+running: |
+    Print operations currently in progress.  An ETA is calculated based
+    on the average for the operation.  If the elapsed exceeds the
+    average, the ETA is calculated against the longest time observed for
+    the operation.  If the elapsed time exceeds this too, or no previous
+    occurrences for the operation exist, \fIunknown\fR is printed.

diff --git a/man/qlop.1 b/man/qlop.1
index bdfe3af..46a6f94 100644
--- a/man/qlop.1
+++ b/man/qlop.1
@@ -8,35 +8,68 @@ qlop \- emerge log analyzer
 .SH DESCRIPTION
 .I qlop
 reads from $EMERGE_LOG_DIR/emerge.log and tries to extract
-information about merges, unmerges and syncs.  For packages, it can
-calculate average merge times or just list them.
+information about merges, unmerges and syncs.  It can
+calculate average merge times or just list them.  When given no
+arguments or just \fB-v\fR, \fIqlop\fR acts as if \fB-must\fR was given
+and thus lists the time taken for all occurrances of merges, unmerges
+and sync operations found in the log.
+.P
+By default, packages are printed as CATEGORY/PN.  Use \fB-v\fR to print
+the package version and revision numbers, e.g\. CATEGORY/PF.  Note that
+when using this option, averages (\fB-a\fR) are reported per version
+instead of per package.
+.P
+The non-option arguments to \fIqlop\fR can be any valid atoms.  Multiple
+can be given to match.  Since all of these are treated as atoms, version
+specifications can be used such as \fB<bash-5\fR.  This allows to zoom
+in further on specific packages.
+.P
+After version \fB0.74\fR of portage-utils, \fIqlop\fR was changed
+considerably to be more consistent and more advanced.  Most notably,
+this has changed default date output and commmand line flags.  Instead
+of reporting the time the operation finished, \fIqlop\fR now reports the
+time the operation started.  The behaviour of the old \fB-q\fR flag is
+best matched by the new \fB-t\fR flag.  Similar, the old \fB-t\fR flag
+is matched by the new \fB-a\fR flag.
 .SH OPTIONS
 .TP
-\fB\-g\fR, \fB\-\-gauge\fR
-Gauge number of times a package has been merged.  This shows the
-merge time for each individual merge of package.
+\fB\-c\fR, \fB\-\-summary\fR
+Show a grand total for averages.  This option implies \fB-a\fR.
+Useful to compute time it takes to recompile all packages or a set
+of packages (e.g\. world).
 .TP
 \fB\-t\fR, \fB\-\-time\fR
-Calculate merge time for a specific package.  This is the average
-time for all merges of package.
+Show the time it took to merge, unmerge or sync.
+.TP
+\fB\-a\fR, \fB\-\-average\fR
+Calculate average merge, unmerge or sync time.  This is the average
+time for all occurrences found respecting any date limits.
 .TP
 \fB\-H\fR, \fB\-\-human\fR
-Print seconds in human readable format (needs \fB\-t\fR), using
+Print elaspsed time in human readable format.  This form uses
 minutes, hours and days instead of just seconds.
 .TP
-\fB\-l\fR, \fB\-\-list\fR
+\fB\-m\fR, \fB\-\-merge\fR
 Show merge history.
 .TP
-\fB\-u\fR, \fB\-\-unlist\fR
+\fB\-u\fR, \fB\-\-unmerge\fR
 Show unmerge history.
 .TP
+\fB\-U\fR, \fB\-\-autoclean\fR
+Show autoclean unmerge history.
+.TP
 \fB\-s\fR, \fB\-\-sync\fR
 Show sync history.
 .TP
-\fB\-c\fR, \fB\-\-current\fR
-Show current emerging packages.  This relies on
-.I FEATURES=sandbox
-in order to detect running merges.
+\fB\-e\fR, \fB\-\-endtime\fR
+Report time at which the operation finished (iso started).
+.TP
+\fB\-r\fR, \fB\-\-running\fR
+Print operations currently in progress.  An ETA is calculated based
+on the average for the operation.  If the elapsed exceeds the
+average, the ETA is calculated against the longest time observed for
+the operation.  If the elapsed time exceeds this too, or no previous
+occurrences for the operation exist, \fIunknown\fR is printed.
 .TP
 \fB\-d\fR \fI<arg>\fR, \fB\-\-date\fR \fI<arg>\fR
 Limit the selection of packages to the date given, or to the range
@@ -62,11 +95,14 @@ Use \fIFORMAT\fR as input for \fBstrptime\fR(3) to parse 
\fIDATE\fR.
 \fB\-f\fR \fI<arg>\fR, \fB\-\-logfile\fR \fI<arg>\fR
 Read emerge logfile instead of $EMERGE_LOG_DIR/emerge.log.
 .TP
+\fB\-w\fR \fI<arg>\fR, \fB\-\-atoms\fR \fI<arg>\fR
+Read package atoms to report from file.
+.TP
 \fB\-\-root\fR \fI<arg>\fR
 Set the ROOT env var.
 .TP
 \fB\-v\fR, \fB\-\-verbose\fR
-Print package versions and revisions.
+Print package versions and revisions (PF) instead of package (PN).
 .TP
 \fB\-q\fR, \fB\-\-quiet\fR
 Tighter output; suppress warnings.

diff --git a/qlop.c b/qlop.c
index a042fa7..26b27b2 100644
--- a/qlop.c
+++ b/qlop.c
@@ -1,38 +1,47 @@
 /*
- * Copyright 2005-2018 Gentoo Foundation
+ * Copyright 2005-2019 Gentoo Foundation
  * Distributed under the terms of the GNU General Public License v2
  *
  * Copyright 2005-2010 Ned Ludd        - <[email protected]>
  * Copyright 2005-2014 Mike Frysinger  - <[email protected]>
+ * Copyright 2018-     Fabian Groffen  - <[email protected]>
  */
 
 #ifdef APPLET_qlop
 
 #define QLOP_DEFAULT_LOGFILE "emerge.log"
 
-#define QLOP_FLAGS "gtHluscd:f:" COMMON_FLAGS
+#define QLOP_FLAGS "ctaHmuUserd:f:w:" COMMON_FLAGS
 static struct option const qlop_long_opts[] = {
-       {"gauge",     no_argument, NULL, 'g'},
+       {"summary",   no_argument, NULL, 'c'},
        {"time",      no_argument, NULL, 't'},
+       {"average",   no_argument, NULL, 'a'},
        {"human",     no_argument, NULL, 'H'},
-       {"list",      no_argument, NULL, 'l'},
-       {"unlist",    no_argument, NULL, 'u'},
+       {"merge",     no_argument, NULL, 'm'},
+       {"unmerge",   no_argument, NULL, 'u'},
+       {"autoclean", no_argument, NULL, 'U'},
        {"sync",      no_argument, NULL, 's'},
-       {"current",   no_argument, NULL, 'c'},
+       {"endtime",   no_argument, NULL, 'e'},
+       {"running",   no_argument, NULL, 'r'},
        {"date",       a_argument, NULL, 'd'},
        {"logfile",    a_argument, NULL, 'f'},
+       {"atoms",      a_argument, NULL, 'w'},
        COMMON_LONG_OPTS
 };
 static const char * const qlop_opts_help[] = {
-       "Gauge number of times a package has been merged",
-       "Calculate merge time for a specific package",
-       "Print seconds in human readable format (needs -t)",
+       "Print summary of average merges (implies -a)",
+       "Print time taken to complete action",
+       "Print average time taken to complete action",
+       "Print elapsed time in human readable format (use with -t or -a)",
        "Show merge history",
        "Show unmerge history",
+       "Show autoclean unmerge history",
        "Show sync history",
+       "Report time at which the operation finished (iso started)",
        "Show current emerging packages",
        "Limit selection to this time (1st -d is start, 2nd -d is end)",
        "Read emerge logfile instead of $EMERGE_LOG_DIR/" QLOP_DEFAULT_LOGFILE,
+       "Read package atoms to report from file",
        COMMON_OPTS_HELP
 };
 static const char qlop_desc[] =
@@ -44,672 +53,18 @@ static const char qlop_desc[] =
        "  -d '%d.%m.%Y|25.12.2015'  (format is specified)";
 #define qlop_usage(ret) usage(ret, QLOP_FLAGS, qlop_long_opts, qlop_opts_help, 
qlop_desc, lookup_applet_idx("qlop"))
 
-#define QLOP_LIST    0x01
-#define QLOP_UNLIST  0x02
-
-static void
-print_seconds_for_earthlings(const unsigned long t)
-{
-       unsigned dd, hh, mm, ss;
-       unsigned long tt = t;
-       ss = tt % 60; tt /= 60;
-       mm = tt % 60; tt /= 60;
-       hh = tt % 24; tt /= 24;
-       dd = tt;
-       if (dd) printf("%s%u%s day%s, ", GREEN, dd, NORM, (dd == 1 ? "" : "s"));
-       if (hh) printf("%s%u%s hour%s, ", GREEN, hh, NORM, (hh == 1 ? "" : 
"s"));
-       if (mm) printf("%s%u%s minute%s, ", GREEN, mm, NORM, (mm == 1 ? "" : 
"s"));
-       printf("%s%u%s second%s", GREEN, ss, NORM, (ss == 1 ? "" : "s"));
-}
-
-static const char *
-chop_ctime(time_t t)
-{
-       static char ctime_out[50];
-       int ret = snprintf(ctime_out, sizeof(ctime_out), "%s", ctime(&t));
-       /* Assume no error! */
-       ctime_out[ret - 1] = '\0';
-       return ctime_out;
-}
-
-static unsigned long
-show_merge_times(char *package, const char *logfile, int average, char 
human_readable,
-                 time_t start_time, time_t end_time)
-{
-       FILE *fp;
-       char cat[126], buf[2][BUFSIZ];
-       char *pkg, *p, *q;
-       char ep[BUFSIZ];
-       unsigned long count, merge_time;
-       time_t t[2];
-       depend_atom *atom;
-       unsigned int parallel_emerge;
-
-       t[0] = t[1] = 0UL;
-       count = merge_time = 0;
-       cat[0] = 0;
-
-       /* setup cat and pkg vars */
-       if ((p = strchr(package, '/')) != NULL) {
-               pkg = p + 1;
-               strncpy(cat, package, sizeof(cat));
-               if ((p = strchr(cat, '/')) != NULL)
-                       *p = 0;
-       } else {
-               pkg = package;
-       }
-
-       if ((fp = fopen(logfile, "r")) == NULL) {
-               warnp("Could not open logfile '%s'", logfile);
-               return 1;
-       }
-
-       /* loop over lines searching for cat/pkg */
-       parallel_emerge = 0;
-       while (fgets(buf[0], sizeof(buf[0]), fp) != NULL) {
-               if ((p = strchr(buf[0], '\n')) != NULL)
-                       *p = '\0';
-               if ((p = strchr(buf[0], ':')) == NULL)
-                       continue;
-               *p++ = '\0';
-               if (strstr(p, pkg) == NULL)
-                       continue;
-
-               t[0] = atol(buf[0]);
-               if (t[0] < start_time || t[0] > end_time)
-                       continue;
-
-               /* copy message (stripping timestamp) */
-               strncpy(buf[1], p, BUFSIZ);
-               rmspace(buf[1]);
-
-               if (strncmp(buf[1], "Started emerge on:", 18) == 0) {
-                       /* a parallel emerge was launched */
-                       parallel_emerge++;
-                       continue;
-               }
-
-               if (strncmp(buf[1], "*** terminating.", 16) == 0) {
-                       if (parallel_emerge > 0) {
-                               /* a parallel emerge has finished */
-                               parallel_emerge--;
-                               continue;
-                       } else {
-                               /* the main emerge was stopped? if there's more 
lines
-                                * this file is just corrupt or truncated at 
the front */
-                               continue;
-                       }
-               }
-
-               if (strncmp(buf[1], ">>> emerge (", 12) == 0) {
-                       /* construct the matching end marker */
-                       snprintf(ep, BUFSIZ, "completed %s", &buf[1][4]);
-
-                       /* skip over "(X of Y)" */
-                       if ((p = strchr(buf[1], ')')) == NULL) {
-                               *ep = '\0';
-                               continue;
-                       }
-                       *p++ = '\0';
-
-                       /* get the package as atom */
-                       strncpy(buf[0], p, BUFSIZ);
-                       rmspace(buf[0]);
-                       if ((p = strchr(buf[0], ' ')) == NULL) {
-                               *ep = '\0';
-                               continue;
-                       }
-                       *p = '\0';
-                       if ((atom = atom_explode(buf[0])) == NULL) {
-                               *ep = '\0';
-                               continue;
-                       }
-
-                       /* match atom against our search */
-                       if ((*cat && ((strcmp(cat, atom->CATEGORY) == 0) &&
-                                                       (strcmp(pkg, atom->PN) 
== 0))) ||
-                                       (strcmp(pkg, atom->PN) == 0))
-                       {
-                               while (fgets(buf[0], sizeof(buf[0]), fp) != 
NULL) {
-                                       if ((p = strchr(buf[0], '\n')) != NULL)
-                                               *p = '\0';
-                                       if ((p = strchr(buf[0], ':')) == NULL)
-                                               continue;
-                                       *p++ = '\0';
-
-                                       t[1] = atol(buf[0]);
-                                       strcpy(buf[1], p);
-                                       rmspace(buf[1]);
-
-                                       if (strncmp(buf[1], "Started emerge 
on:", 18) == 0) {
-                                               /* a parallel emerge was 
launched */
-                                               parallel_emerge++;
-                                               continue;
-                                       }
-
-                                       if (strncmp(buf[1], "*** terminating.", 
16) == 0) {
-                                               if (parallel_emerge > 0) {
-                                                       /* a parallel emerge 
has finished */
-                                                       parallel_emerge--;
-                                                       continue;
-                                               } else {
-                                                       /* the main emerge was 
stopped? if there's
-                                                        * more lines this file 
is just corrupt or
-                                                        * truncated at the 
front */
-                                                       break;
-                                               }
-                                       }
-
-                                       /* pay attention to malformed log files 
(when the
-                                        * end of an emerge process is not 
indicated by the
-                                        * line '*** terminating'). We assume 
that the log
-                                        * is malformed when we find a parallel 
emerge
-                                        * process which is trying to emerge 
the same
-                                        * package
-                                        */
-                                       if (strncmp(buf[1], ">>> emerge (", 12) 
== 0 &&
-                                                       parallel_emerge > 0)
-                                       {
-                                               /* find package name */
-                                               p = strchr(buf[1], ')');
-                                               q = strchr(ep, ')');
-                                               if (!p || !q)
-                                                       continue;
-
-                                               /* is this emerge doing the 
same thing as we're
-                                                * looking for? that means we 
failed */
-                                               if (strcmp(p, q) == 0) {
-                                                       parallel_emerge--;
-                                                       /* update the main 
emerge reference data */
-                                                       snprintf(ep, BUFSIZ, 
"completed %s", &buf[1][4]);
-                                                       t[0] = t[1];
-                                                       continue;
-                                               }
-                                       }
-
-                                       /* if this line matches "completed 
emerge (X of Y) ..."
-                                        * we're finally somewhere */
-                                       if (strncmp(&buf[1][4], ep, BUFSIZ - 4) 
== 0) {
-                                               if (!average) {
-                                                       buf[1][0] = '\0';
-                                                       if (verbose) {
-                                                               if 
(atom->PR_int)
-                                                                       
snprintf(buf[1], sizeof(buf[1]),
-                                                                               
        "-%s-r%i", atom->PV,  atom->PR_int);
-                                                               else
-                                                                       
snprintf(buf[1], sizeof(buf[1]),
-                                                                               
        "-%s", atom->PV);
-                                                       }
-                                                       printf("%s%s%s%s: %s: ",
-                                                                       BLUE, 
atom->PN, buf[1], NORM,
-                                                                       
chop_ctime(t[0]));
-                                                       if (human_readable)
-                                                               
print_seconds_for_earthlings(t[1] - t[0]);
-                                                       else
-                                                               
printf("%s%"PRIu64"%s seconds",
-                                                                               
GREEN, (uint64_t)(t[1] - t[0]), NORM);
-                                                       printf("\n");
-                                               }
-                                               merge_time += (t[1] - t[0]);
-                                               count++;
-                                               break;
-                                       }
-                               }
-                       }
-                       atom_implode(atom);
-               }
-       }
-       fclose(fp);
-       if (count == 0)
-               return 0;
-       if (average == 1) {
-               printf("%s%s%s: ", BLUE, pkg, NORM);
-               if (human_readable)
-                       print_seconds_for_earthlings(merge_time / count);
-               else
-                       printf("%s%lu%s seconds average", GREEN, merge_time / 
count, NORM);
-               printf(" for %s%lu%s merges\n", GREEN, count, NORM);
-       } else {
-               printf("%s%s%s: %s%lu%s times\n", BLUE, pkg, NORM, GREEN, 
count, NORM);
-       }
-       return 0;
-}
-
-static void
-show_emerge_history(int listflag, array_t *atoms, const char *logfile,
-                    time_t start_time, time_t end_time)
-{
-       FILE *fp;
-       int linelen;
-       size_t buflen;
-       char *buf, merged;
-       char *p, *q;
-       bool showit;
-       size_t i;
-       time_t t;
-       depend_atom *atom, *logatom;
-
-       if ((fp = fopen(logfile, "r")) == NULL) {
-               warnp("Could not open logfile '%s'", logfile);
-               return;
-       }
-
-       buf = NULL;
-       while ((linelen = getline(&buf, &buflen, fp)) >= 0) {
-               if (linelen < 30)
-                       continue;
-
-               rmspace_len(buf, (size_t)linelen);
-               if ((p = strchr(buf, ':')) == NULL)
-                       continue;
-               *p = 0;
-               q = p + 3;
-               /* Make sure there's leading white space and not a truncated
-                * string. #573106 */
-               if (p[1] != ' ' || p[2] != ' ')
-                       continue;
-
-               t = (time_t) atol(buf);
-               if (t < start_time || t > end_time)
-                       continue;
-
-               if ((listflag & QLOP_LIST) && !strncmp(q, "::: completed emerge 
(", 22)) {
-                       merged = 1;
-                       if ((p = strchr(q, ')')) == NULL)
-                               continue;
-                       q = p + 2;
-                       if ((p = strchr(q, ' ')) == NULL)
-                               continue;
-                       *p = 0;
-               } else if ((listflag & QLOP_UNLIST) && !strncmp(q, ">>> unmerge 
success: ", 21)) {
-                       merged = 0;
-                       if ((p = strchr(q, ':')) == NULL)
-                               continue;
-                       q = p + 2;
-               } else
-                       continue;
-
-               logatom = atom_explode(q);
-               if (array_cnt(atoms)) {
-                       showit = false;
-                       array_for_each(atoms, i, atom)
-                               if (atom_compare(atom, logatom) == EQUAL) {
-                                       showit = true;
-                                       break;
-                               }
-               } else
-                       showit = true;
-
-               if (showit) {
-                       if (!quiet)
-                               printf("%s %s %s%s%s\n", chop_ctime(t), (merged 
? ">>>" : "<<<"), (merged ? GREEN : RED), q, NORM);
-                       else {
-                               if (quiet == 1)
-                                       printf("%s ", chop_ctime(t));
-                               if (quiet <= 2)
-                                       printf("%s ", (merged ? ">>>" : "<<<"));
-                               printf("%s%s/%s%s\n", (merged ? GREEN : RED), 
logatom->CATEGORY, logatom->PN, NORM);
-                       }
-               }
-               atom_implode(logatom);
-       }
-
-       free(buf);
-       fclose(fp);
-}
-
-/* The format of the sync log has changed over time.
-
-Old format:
-1106804103: Started emerge on: Jan 27, 2005 05:35:03
-1106804103:  *** emerge  sync
-1106804103:  === sync
-1106804103: >>> starting rsync with rsync://192.168.0.5/gentoo-portage
-1106804537: === Sync completed with rsync://192.168.0.5/gentoo-portage
-1106804538:  *** terminating.
-
-New format:
-1431764402: Started emerge on: May 16, 2015 04:20:01
-1431764402:  *** emerge --quiet --keep-going --verbose --nospinner --oneshot 
--quiet-build=n --sync
-1431764402:  === sync
-1431764402: >>> Syncing repository 'gentoo' into '/usr/portage'...
-1431764402: >>> Starting rsync with rsync://[2a01:90:200:10::1a]/gentoo-portage
-1431764460: === Sync completed for gentoo
-1431764493:  *** terminating.
-*/
-static void
-show_sync_history(const char *logfile, time_t start_time, time_t end_time)
-{
-       FILE *fp;
-       int linelen;
-       size_t buflen;
-       char *buf, *p;
-       time_t t;
-
-       if ((fp = fopen(logfile, "r")) == NULL) {
-               warnp("Could not open logfile '%s'", logfile);
-               return;
-       }
-
-       buf = NULL;
-       /* Just find the finish lines. */
-       while ((linelen = getline(&buf, &buflen, fp)) >= 0) {
-               /* This cuts out like ~10% of the log. */
-               if (linelen < 35)
-                       continue;
-
-               /* Make sure there's a timestamp in here. */
-               if ((p = strchr(buf, ':')) == NULL)
-                       continue;
-               p += 2;
-
-               if (strncmp(p, "=== Sync completed ", 19) != 0)
-                       continue;
-               p += 19;
-
-               rmspace_len(buf, (size_t)linelen);
-
-               t = (time_t)atol(buf);
-               if (t < start_time || t > end_time)
-                       continue;
-
-               if (!strncmp(p, "with ", 5))
-                       p += 5;
-               else if (!strncmp(p, "for ", 4))
-                       /* This shows just the repo name not the remote host 
... */
-                       p += 4;
-               else
-                       continue;
-               printf("%s >>> %s%s%s\n", chop_ctime(t), GREEN, p, NORM);
-       }
-
-       free(buf);
-       fclose(fp);
-}
-
-static void show_current_emerge(void);
-#ifdef __linux__
-# include <asm/param.h>
-#endif
-#if defined __linux__ || defined __GNU__
-# include <elf.h>
-static unsigned long hz = 0;
-static void init_hz(void)
-{
-#ifdef HZ
-       hz = HZ;
-#endif
-       /* kernel pushes elf notes onto stack */
-       unsigned long *elf_note = (unsigned long *)environ;
-       while (!*elf_note++)
-               continue;
-       while (elf_note[0]) {
-               if (elf_note[0] == AT_CLKTCK) {
-                       hz = elf_note[1];
-                       break;
-               }
-               elf_note += 2;
-       }
-       if (!hz)
-               hz = 100;
-}
-
-static char *
-root_readlink(const int pid)
-{
-       static char path[_Q_PATH_MAX];
-       char buf[_Q_PATH_MAX];
-       memset(&path, 0, sizeof(path));
-       snprintf(buf, sizeof(buf), "/proc/%d/root", pid);
-       if (readlink(buf, path, sizeof(path) - 1) == -1)
-               return NULL;
-       else
-               return path;
-}
-
-void show_current_emerge(void)
-{
-       DIR *proc;
-       struct dirent *de;
-       pid_t pid;
-       static char *cmdline, *bufstat;
-       static size_t cmdline_len, bufstat_len;
-       char path[50];
-       char *p, *q;
-       unsigned long long start_time = 0;
-       double uptime_secs;
-       time_t start_date;
-
-       if ((proc = opendir("/proc")) == NULL) {
-               warnp("Could not open /proc");
-               return;
-       }
-
-       if (!hz)
-               init_hz();
-
-       while ((de = readdir(proc)) != NULL) {
-
-               if ((pid = (pid_t)atol(de->d_name)) == 0)
-                       continue;
-
-               /* portage renames the cmdline so the package name is first */
-               snprintf(path, sizeof(path), "/proc/%i/cmdline", pid);
-               if (!eat_file(path, &cmdline, &cmdline_len))
-                       continue;
-
-               if (cmdline[0] == '[' && (p = strchr(cmdline, ']')) != NULL &&
-                               strstr(cmdline, "sandbox") != NULL)
-               {
-                       *p = '\0';
-                       p = cmdline + 1;
-                       q = p + strlen(p) + 1;
-
-                       /* open the stat file to figure out how long we have 
been running */
-                       snprintf(path, sizeof(path), "/proc/%i/stat", pid);
-                       if (!eat_file(path, &bufstat, &bufstat_len))
-                               continue;
-
-                       /* ripped from procps/proc/readproc.c */
-                       if ((q = strchr(bufstat, ')')) == NULL)
-                               continue;
-                       /* grab the start time */
-                       sscanf(q + 2,
-                               "%*c "
-                               "%*d %*d %*d %*d %*d "
-                               "%*u %*u %*u %*u %*u "
-                               "%*u %*u %*u %*u "
-                               "%*d %*d "
-                               "%*d "
-                               "%*d "
-                               "%llu ",
-                               &start_time);
-                       /* get uptime */
-                       if (!eat_file("/proc/uptime", &bufstat, &bufstat_len))
-                               continue;
-                       sscanf(bufstat, "%lf", &uptime_secs);
-
-                       /* figure out when this thing started and then show it 
*/
-                       start_date = time(0) - (uptime_secs - (start_time / 
hz));
-                       printf(
-                               " %s*%s %s%s%s\n"
-                               "     started: %s%s%s\n"
-                               "     elapsed: ", /*%s%llu%s seconds\n",*/
-                               BOLD, NORM, BLUE, p, NORM,
-                               GREEN, chop_ctime(start_date), NORM);
-                       print_seconds_for_earthlings(uptime_secs - (start_time 
/ hz));
-                       puts(NORM);
-                       p = root_readlink(pid);
-                       if (p && strcmp(p, "/"))
-                               printf("     chroot:  %s%s%s\n", GREEN, p, 
NORM);
-               }
-       }
-
-       closedir(proc);
-
-       if (start_time == 0 && verbose)
-               puts("No emerge processes located");
-}
-#elif defined(__FreeBSD__)
-# include <kvm.h>
-# include <sys/param.h>
-# include <sys/sysctl.h>
-# include <sys/user.h>
-void show_current_emerge(void)
-{
-       kvm_t *kd = NULL;
-       struct kinfo_proc *ip;
-       int i; int total_processes;
-       char *p, *q;
-       time_t start_date = 0;
-
-       if (! (kd = kvm_open("/dev/null", "/dev/null", "/dev/null",
-                                       O_RDONLY, "kvm_open")))
-       {
-               warnp("Could not open kvm: %s", kvm_geterr(kd));
-               return;
-       }
-
-       ip = kvm_getprocs(kd, KERN_PROC_PROC, 0, &total_processes);
-
-       for (i = 0; i < total_processes; i++) {
-               char **proc_argv = NULL;
-               char *buf = NULL;
-
-               if (strcmp(ip[i].ki_comm, "sandbox") != 0)
-                       continue;
-
-               proc_argv = kvm_getargv(kd, &(ip[i]), 0);
-
-               if (!proc_argv || (buf = xstrdup(proc_argv[0])) == NULL ||
-                   buf[0] != '[' || (p = strchr(buf, ']')) == NULL) {
-                       free(buf);
-                       continue;
-               }
-
-               *p = '\0';
-               p = buf+1;
-               q = p + strlen(p) + 1;
-
-               printf(
-                       " %s*%s %s%s%s\n"
-                       "     started: %s%s%s\n"
-                       "     elapsed: ", /*%s%llu%s seconds\n",*/
-                       BOLD, NORM, BLUE, p, NORM,
-                       GREEN, chop_ctime(ip[i].ki_start.tv_sec), NORM);
-               print_seconds_for_earthlings(time(0) - ip[i].ki_start.tv_sec);
-               puts(NORM);
-
-               free(buf);
-       }
-
-       if (start_date == 0 && verbose)
-               puts("No emerge processes located");
-}
-#elif defined(__MACH__)
-# include <sys/sysctl.h>
-void show_current_emerge(void)
-{
-       int mib[3];
-       size_t size = 0;
-       struct kinfo_proc *ip, *raip;
-       int ret, total_processes, i;
-       char *p, *q;
-       time_t start_date = 0;
-       char args[512];
-
-       mib[0] = CTL_KERN;
-       mib[1] = KERN_PROC;
-       mib[2] = KERN_PROC_ALL; /* could restrict to _UID (effective uid) */
-
-       /* probe once to get the current size; estimate only, but OS tries
-        * to round up if it can predict a sudden growth, so optimise below
-        * for the optimistic case */
-       ret = sysctl(mib, 3, NULL, &size, NULL, 0);
-       ip = xmalloc(sizeof(*ip) * size);
-       while (1) {
-               ret = sysctl(mib, 3, ip, &size, NULL, 0);
-               if (ret >= 0 && errno == ENOMEM) {
-                       size += size / 10; /* may be a bit overdone... */
-                       raip = realloc(ip, sizeof(struct kinfo_proc) * size);
-                       if (raip == NULL) {
-                               free(ip);
-                               warnp("Could not extend allocated block to "
-                                               "%zd bytes for process 
information",
-                                               sizeof(struct kinfo_proc) * 
size);
-                               return;
-                       }
-                       ip = raip;
-               } else if (ret < 0) {
-                       free(ip);
-                       warnp("Could not retrieve process information");
-                       return;
-               } else {
-                       break;
-               }
-       }
-
-       total_processes = size / sizeof(struct kinfo_proc);
-
-       /* initialise mib for argv retrieval calls */
-       mib[0] = CTL_KERN;
-       mib[1] = KERN_PROCARGS;
-
-       for (i = 0; i < total_processes; i++) {
-               char *buf = NULL;
-               size_t argssize = sizeof(args);
-
-               if (strcmp(ip[i].kp_proc.p_comm, "sandbox") != 0)
-                       continue;
-
-               mib[2] = ip[i].kp_proc.p_pid;
-               if (sysctl(mib, 3, args, &argssize, NULL, 0) != 0) {
-                       free(ip);
-                       return;
-               }
-
-               /* this is magic to get back up in the stack where the arguments
-                * start */
-               for (buf = args; buf < &args[argssize]; buf++)
-                       if (*buf == '\0')
-                               break;
-               if (buf == &args[argssize]) {
-                       free(ip);
-                       continue;
-               }
-               if ((buf = xstrdup(buf)) == NULL ||
-                   buf[0] != '[' || (p = strchr(buf, ']')) == NULL) {
-                       free(buf);
-                       continue;
-               }
-
-               *p = '\0';
-               p = buf+1;
-               q = p + strlen(p) + 1;
-
-               printf(
-                       " %s*%s %s%s%s\n"
-                       "     started: %s%s%s\n"
-                       "     elapsed: ", /*%s%llu%s seconds\n",*/
-                       BOLD, NORM, BLUE, p, NORM,
-                       GREEN, chop_ctime(ip[i].kp_proc.p_starttime.tv_sec), 
NORM);
-               print_seconds_for_earthlings(time(0) - 
ip[i].kp_proc.p_starttime.tv_sec);
-               puts(NORM);
-
-               free(buf);
-       }
-
-       free(ip);
-
-       if (start_date == 0 && verbose)
-               puts("No emerge processes located");
-}
-#else
-void show_current_emerge(void)
-{
-       errf("not supported on your OS");
-}
-#endif
+struct qlop_mode {
+       char do_time:1;
+       char do_merge:1;
+       char do_unmerge:1;
+       char do_autoclean:1;
+       char do_sync:1;
+       char do_running:1;
+       char do_average:1;
+       char do_summary:1;
+       char do_human:1;
+       char do_endtime:1;
+};
 
 static bool
 parse_date(const char *sdate, time_t *t)
@@ -813,33 +168,688 @@ parse_date(const char *sdate, time_t *t)
        return (*t == -1) ? false : true;
 }
 
+static char _date_buf[48];
+static char *fmt_date(struct qlop_mode *flags, time_t ts, time_t te)
+{
+       time_t t;
+
+       t = flags->do_endtime ? te : ts;
+       strftime(_date_buf, sizeof(_date_buf), "%Y-%m-%dT%H:%M:%S", 
localtime(&t));
+       return _date_buf;
+}
+
+static char _elapsed_buf[256];
+static char *fmt_elapsedtime(struct qlop_mode *flags, time_t e)
+{
+       if (flags->do_human) {
+               time_t dd;
+               time_t hh;
+               time_t mm;
+               time_t ss;
+               size_t bufpos = 0;
+
+               ss = e % 60;
+               e /= 60;
+               mm = e % 60;
+               e /= 60;
+               hh = e % 24;
+               e /= 24;
+               dd = e;
+
+               if (dd > 0)
+                       bufpos += snprintf(_elapsed_buf + bufpos,
+                                       sizeof(_elapsed_buf) - bufpos,
+                                       "%s%zd%s day%s",
+                                       GREEN, (size_t)dd, NORM, dd == 1 ? "" : 
"s");
+               if (hh > 0)
+                       bufpos += snprintf(_elapsed_buf + bufpos,
+                                       sizeof(_elapsed_buf) - bufpos,
+                                       "%s%s%zd%s hour%s",
+                                       bufpos == 0 ? "" : ", ",
+                                       GREEN, (size_t)hh, NORM, hh == 1 ? "" : 
"s");
+               if (mm > 0)
+                       bufpos += snprintf(_elapsed_buf + bufpos,
+                                       sizeof(_elapsed_buf) - bufpos,
+                                       "%s%s%zd%s minute%s",
+                                       bufpos == 0 ? "" : ", ",
+                                       GREEN, (size_t)mm, NORM, mm == 1 ? "" : 
"s");
+               if (ss > 0 || (mm + hh + dd) == 0)
+                       bufpos += snprintf(_elapsed_buf + bufpos,
+                                       sizeof(_elapsed_buf) - bufpos,
+                                       "%s%s%zd%s second%s",
+                                       bufpos == 0 ? "" : ", ",
+                                       GREEN, (size_t)ss, NORM, ss == 1 ? "" : 
"s");
+       } else {
+               snprintf(_elapsed_buf, sizeof(_elapsed_buf), "%s%zd%s seconds",
+                               GREEN, (size_t)e, NORM);
+       }
+
+       return _elapsed_buf;
+}
+
+static char _atom_buf[BUFSIZ];
+static char *fmt_atom(struct qlop_mode *flags, depend_atom *atom)
+{
+       (void)flags;
+
+       if (verbose) {
+               size_t len = snprintf(_atom_buf, sizeof(_atom_buf), "%s/%s-%s",
+                               atom->CATEGORY, atom->PN, atom->PV);
+               if (atom->PR_int > 0)
+                       snprintf(_atom_buf + len, sizeof(_atom_buf) - len, 
"-r%d",
+                               atom->PR_int);
+       } else {
+               snprintf(_atom_buf, sizeof(_atom_buf), "%s/%s",
+                               atom->CATEGORY, atom->PN);
+       }
+
+       return _atom_buf;
+}
+
+/* The format of the sync log has changed over time.
+
+Old format:
+1106804103: Started emerge on: Jan 27, 2005 05:35:03
+1106804103:  *** emerge  sync
+1106804103:  === sync
+1106804103: >>> starting rsync with rsync://192.168.0.5/gentoo-portage
+1106804537: === Sync completed with rsync://192.168.0.5/gentoo-portage
+1106804538:  *** terminating.
+
+New format:
+1431764402: Started emerge on: May 16, 2015 04:20:01
+1431764402:  *** emerge --quiet --keep-going --verbose --nospinner --oneshot 
--quiet-build=n --sync
+1431764402:  === sync
+1431764402: >>> Syncing repository 'gentoo' into '/usr/portage'...
+1431764402: >>> Starting rsync with rsync://[2a01:90:200:10::1a]/gentoo-portage
+1431764460: === Sync completed for gentoo
+1431764493:  *** terminating.
+
+*** packages
+
+1547475773:  >>> emerge (53 of 74) app-shells/bash-5.0 to /gentoo/prefix64/
+1547475774:  === (53 of 74) Cleaning 
(app-shells/bash-5.0::/path/to/app-shells/bash/bash-5.0.ebuild)
+1547475774:  === (53 of 74) Compiling/Merging 
(app-shells/bash-5.0::/path/to/app-shells/bash/bash-5.0.ebuild)
+1547475913:  === (53 of 74) Merging 
(app-shells/bash-5.0::/path/to/app-shells/bash/bash-5.0.ebuild)
+1547475916:  >>> AUTOCLEAN: app-shells/bash:0
+1547475916:  === Unmerging... (app-shells/bash-4.4_p23)
+1547475918:  >>> unmerge success: app-shells/bash-4.4_p23
+1547475921:  === (53 of 74) Post-Build Cleaning 
(app-shells/bash-5.0::/path/to/app-shells/bash/bash-5.0.ebuild)
+1547475921:  ::: completed emerge (53 of 74) app-shells/bash-5.0 to 
/gentoo/prefix64/
+
+1550953093: Started emerge on: Feb 23, 2019 21:18:12
+1550953093:  *** emerge --ask --verbose --depclean pwgen
+1550953093:  >>> depclean
+1550953118: === Unmerging... (app-admin/pwgen-2.08)
+1550953125:  >>> unmerge success: app-admin/pwgen-2.08
+1550953125:  *** exiting successfully.
+1550953125:  *** terminating.
+*/
+static int do_emerge_log(
+               const char *log,
+               struct qlop_mode *flags,
+               array_t *atoms,
+               time_t tbegin,
+               time_t tend)
+{
+       FILE *fp;
+       char buf[BUFSIZ];
+       char *p;
+       char *q;
+       time_t tstart;
+       time_t sync_start = 0;
+       time_t sync_time = 0;
+       size_t sync_cnt = 0;
+       time_t elapsed;
+       depend_atom *atom;
+       depend_atom *atomw;
+       DECLARE_ARRAY(merge_matches);
+       DECLARE_ARRAY(merge_averages);
+       DECLARE_ARRAY(unmerge_matches);
+       DECLARE_ARRAY(unmerge_averages);
+       size_t i;
+       size_t parallel_emerge = 0;
+
+       struct pkg_match {
+               char id[BUFSIZ];
+               depend_atom *atom;
+               time_t tbegin;
+               time_t time;
+               size_t cnt;
+       };
+       struct pkg_match *pkg;
+       struct pkg_match *pkgw;
+
+       if ((fp = fopen(log, "r")) == NULL) {
+               warnp("Could not open logfile '%s'", log);
+               return 1;
+       }
+
+       if (array_cnt(atoms) == 0) {
+               /* assemble list of atoms */
+               while (fgets(buf, sizeof(buf), fp) != NULL) {
+                       if ((p = strchr(buf, ':')) == NULL)
+                               continue;
+                       *p++ = '\0';
+
+                       tstart = atol(buf);
+                       if (tstart < tbegin || tstart > tend)
+                               continue;
+
+                       atom = NULL;
+                       if (strncmp(p, "  >>> emerge ", 13) == 0 &&
+                                       (p = strchr(p + 13, ')')) != NULL)
+                       {
+                               p += 2;
+                               q = strchr(p, ' ');
+                               if (q != NULL) {
+                                       *q = '\0';
+                                       atom = atom_explode(p);
+                               }
+                       } else if (strncmp(p, "  === Unmerging... (", 20) == 0 
||
+                                       strncmp(p, " === Unmerging... (", 19) 
== 0)
+                       {
+                               if (p[1] == ' ')
+                                       p++;
+                               p += 19;
+                               q = strchr(p, ')');
+                               if (q != NULL) {
+                                       *q = '\0';
+                                       atom = atom_explode(p);
+                               }
+                       }
+                       if (atom != NULL) {
+                               /* strip off version info, if we generate a list
+                                * ourselves, we will always print everything, 
so as
+                                * well can keep memory footprint a bit lower 
by only
+                                * having package matches */
+                               atom->PV = NULL;
+                               atom->PVR = NULL;
+                               atom->PR_int = 0;
+
+                               atomw = NULL;
+                               array_for_each(atoms, i, atomw) {
+                                       if (atom_compare(atom, atomw) == EQUAL)
+                                               break;
+                                       atomw = NULL;
+                               }
+                               if (atomw == NULL) {
+                                       xarraypush_ptr(atoms, atom);
+                               } else {
+                                       atom_implode(atom);
+                               }
+                       }
+               }
+
+               rewind(fp);
+       }
+
+       /* loop over lines searching for atoms */
+       while (fgets(buf, sizeof(buf), fp) != NULL) {
+               if ((p = strchr(buf, ':')) == NULL)
+                       continue;
+               *p++ = '\0';
+
+               /* keeping track of parallel merges needs to be done before
+                * applying dates, for a subset of the log might show emerge
+                * finished without knowledge of another instance */
+               if (flags->do_running &&
+                               (strncmp(p, "  *** emerge ", 13) == 0 ||
+                                strncmp(p, "  *** terminating.", 18) == 0 ||
+                                strncmp(p, "  *** exiting ", 14) == 0))
+               {
+                       if (p[7] == 'm') {
+                               parallel_emerge++;
+                       } else if (parallel_emerge > 0) {
+                               parallel_emerge--;
+                               if (parallel_emerge == 0) {
+                                       /* we just finished the only emerge we 
found to be
+                                        * running, so if there were "running" 
(unfinished)
+                                        * merges, they must have been 
terminated */
+                                       sync_start = 0;
+                                       while ((i = array_cnt(merge_matches)) > 
0) {
+                                               i--;
+                                               pkgw = xarrayget(merge_matches, 
i);
+                                               atom_implode(pkgw->atom);
+                                               xarraydelete(merge_matches, i);
+                                       }
+                                       while ((i = array_cnt(unmerge_matches)) 
> 0) {
+                                               i--;
+                                               pkgw = 
xarrayget(unmerge_matches, i);
+                                               atom_implode(pkgw->atom);
+                                               xarraydelete(unmerge_matches, 
i);
+                                       }
+                               }
+                       }
+               }
+
+               tstart = atol(buf);
+               if (tstart < tbegin || tstart > tend)
+                       continue;
+
+               /* are we interested in this line? */
+               if (flags->do_sync && (
+                                       strncmp(p, " === Sync completed ", 20) 
== 0 ||
+                                       strcmp(p, "  === sync\n") == 0))
+               {
+                       /* sync start or stop, we have nothing to detect 
parallel
+                        * syncs with, so don't bother and assume this doesn't
+                        * happen */
+                       if (p[6] == 's') {
+                               sync_start = tstart;
+                       } else {
+                               if (sync_start == 0)
+                                       continue;  /* sync without start, 
exclude */
+                               elapsed = tstart - sync_start;
+
+                               p += 20;
+                               if (strncmp(p, "for ", 4) == 0) {
+                                       p += 4;
+                               } else {  /* "with " */
+                                       p += 5;
+                               }
+                               if ((q = strchr(p, '\n')) != NULL)
+                                       *q = '\0';
+
+                               if (flags->do_average || flags->do_running)
+                               {
+                                       sync_cnt++;
+                                       sync_time += elapsed;
+                                       sync_start = 0;  /* reset */
+                                       continue;
+                               }
+                               if (flags->do_time) {
+                                       printf("%s *** %s%s%s: %s\n",
+                                                       fmt_date(flags, 
sync_start, tstart),
+                                                       GREEN, p, NORM, 
fmt_elapsedtime(flags, elapsed));
+                               } else {
+                                       printf("%s *** %s%s%s\n",
+                                                       fmt_date(flags, 
sync_start, tstart),
+                                                       GREEN, p, NORM);
+                               }
+                               sync_start = 0;  /* reset */
+                       }
+               } else if (flags->do_merge && (
+                                       strncmp(p, "  >>> emerge (", 14) == 0 ||
+                                       strncmp(p, "  ::: completed emerge (", 
24) == 0))
+               {
+                       /* merge start/stop (including potential unmerge of old 
pkg) */
+                       if (p[3] == '>') {  /* >>> emerge */
+                               char *id;
+
+                               q = strchr(p + 14, ')');
+                               if (q == NULL)
+                                       continue;
+
+                               /* keep a copy of the relevant string in case 
we need to
+                                * match this */
+                               id = p + 6;
+
+                               q += 2;  /* ") " */
+                               p = strchr(q, ' ');
+                               if (p == NULL)
+                                       continue;
+
+                               *p = '\0';
+                               atom = atom_explode(q);
+                               *p = ' ';
+                               if (atom == NULL)
+                                       continue;
+
+                               /* see if we need this atom */
+                               atomw = NULL;
+                               array_for_each(atoms, i, atomw) {
+                                       if (atom_compare(atom, atomw) == EQUAL)
+                                               break;
+                                       atomw = NULL;
+                               }
+                               if (atomw == NULL) {
+                                       atom_implode(atom);
+                                       continue;
+                               }
+
+                               pkg = xmalloc(sizeof(struct pkg_match));
+                               snprintf(pkg->id, sizeof(pkg->id), "%s", id);
+                               pkg->atom = atom;
+                               pkg->tbegin = tstart;
+                               pkg->time = (time_t)0;
+                               pkg->cnt = 0;
+                               xarraypush_ptr(merge_matches, pkg);
+                       } else {  /* ::: completed */
+                               array_for_each_rev(merge_matches, i, pkgw) {
+                                       if (strcmp(p + 16, pkgw->id) != 0)
+                                               continue;
+
+                                       /* found, do report */
+                                       elapsed = tstart - pkgw->tbegin;
+
+                                       if (flags->do_average || 
flags->do_running)
+                                       {
+                                               /* find in list of averages */
+                                               size_t n;
+
+                                               pkg = NULL;
+                                               array_for_each(merge_averages, 
n, pkg) {
+                                                       if 
(atom_compare(pkg->atom, pkgw->atom) == EQUAL) {
+                                                               pkg->cnt++;
+                                                               pkg->time += 
elapsed;
+                                                               /* store max 
time for do_running */
+                                                               if (elapsed > 
pkg->tbegin)
+                                                                       
pkg->tbegin = elapsed;
+                                                               
atom_implode(pkgw->atom);
+                                                               
xarraydelete(merge_matches, i);
+                                                               break;
+                                                       }
+                                                       pkg = NULL;
+                                               }
+                                               if (pkg == NULL) {  /* push new 
entry */
+                                                       if (!verbose || 
flags->do_running) {
+                                                               /* strip off 
version info */
+                                                               pkgw->atom->PV 
= NULL;
+                                                               pkgw->atom->PVR 
= NULL;
+                                                               
pkgw->atom->PR_int = 0;
+                                                       }
+                                                       pkgw->id[0] = '\0';
+                                                       pkgw->cnt = 1;
+                                                       pkgw->time = elapsed;
+                                                       pkgw->tbegin = elapsed;
+                                                       
xarraypush_ptr(merge_averages, pkgw);
+                                                       
xarraydelete_ptr(merge_matches, i);
+                                               }
+                                               break;
+                                       }
+                                       if (flags->do_time) {
+                                               printf("%s >>> %s%s%s: %s\n",
+                                                               fmt_date(flags, 
pkgw->tbegin, tstart),
+                                                               BLUE, 
fmt_atom(flags, pkgw->atom), NORM,
+                                                               
fmt_elapsedtime(flags, elapsed));
+                                       } else if (!flags->do_average) {
+                                               printf("%s >>> %s%s%s\n",
+                                                               fmt_date(flags, 
pkgw->tbegin, tstart),
+                                                               BLUE, 
fmt_atom(flags, pkgw->atom), NORM);
+                                       }
+                                       atom_implode(pkgw->atom);
+                                       xarraydelete(merge_matches, i);
+                                       break;
+                               }
+                       }
+               } else if (
+                               (flags->do_unmerge &&
+                                strncmp(p, " === Unmerging... (", 19) == 0) ||
+                               (flags->do_autoclean &&
+                                strncmp(p, "  === Unmerging... (", 20) == 0) ||
+                               ((flags->do_unmerge || flags->do_autoclean) &&
+                                strncmp(p, "  >>> unmerge success: ", 23) == 
0))
+               {
+                       /* unmerge action */
+                       if (p[2] == '=') {
+                               if (p[1] == ' ')
+                                       p++;
+                               p += 19;
+
+                               if ((q = strchr(p, ')')) == NULL)
+                                       continue;
+
+                               *q = '\0';
+                               atom = atom_explode(p);
+                               if (atom == NULL)
+                                       continue;
+
+                               /* see if we need this atom */
+                               atomw = NULL;
+                               array_for_each(atoms, i, atomw) {
+                                       if (atom_compare(atom, atomw) == EQUAL)
+                                               break;
+                                       atomw = NULL;
+                               }
+                               if (atomw == NULL) {
+                                       atom_implode(atom);
+                                       continue;
+                               }
+
+                               pkg = xmalloc(sizeof(struct pkg_match));
+                               snprintf(pkg->id, sizeof(pkg->id), "%s\n", p);  
/* \n !!! */
+                               pkg->atom = atom;
+                               pkg->tbegin = tstart;
+                               pkg->time = (time_t)0;
+                               pkg->cnt = 0;
+                               xarraypush_ptr(unmerge_matches, pkg);
+                       } else {
+                               array_for_each_rev(unmerge_matches, i, pkgw) {
+                                       if (strcmp(p + 23, pkgw->id) != 0)
+                                               continue;
+
+                                       /* found, do report */
+                                       elapsed = tstart - pkgw->tbegin;
+
+                                       if (flags->do_average || 
flags->do_running)
+                                       {
+                                               /* find in list of averages */
+                                               size_t n;
+
+                                               pkg = NULL;
+                                               
array_for_each(unmerge_averages, n, pkg) {
+                                                       if 
(atom_compare(pkg->atom, pkgw->atom) == EQUAL) {
+                                                               pkg->cnt++;
+                                                               pkg->time += 
elapsed;
+                                                               /* store max 
time for do_running */
+                                                               if (elapsed > 
pkg->tbegin)
+                                                                       
pkg->tbegin = elapsed;
+                                                               
atom_implode(pkgw->atom);
+                                                               
xarraydelete(unmerge_matches, i);
+                                                               break;
+                                                       }
+                                                       pkg = NULL;
+                                               }
+                                               if (pkg == NULL) {  /* push new 
entry */
+                                                       if (!verbose || 
flags->do_running) {
+                                                               /* strip off 
version info */
+                                                               pkgw->atom->PV 
= NULL;
+                                                               pkgw->atom->PVR 
= NULL;
+                                                               
pkgw->atom->PR_int = 0;
+                                                       }
+                                                       pkgw->id[0] = '\0';
+                                                       pkgw->cnt = 1;
+                                                       pkgw->time = elapsed;
+                                                       pkgw->tbegin = elapsed;
+                                                       
xarraypush_ptr(unmerge_averages, pkgw);
+                                                       
xarraydelete_ptr(unmerge_matches, i);
+                                               }
+                                               break;
+                                       }
+                                       if (flags->do_time) {
+                                               printf("%s <<< %s%s%s: %s\n",
+                                                               fmt_date(flags, 
pkgw->tbegin, tstart),
+                                                               BLUE, 
fmt_atom(flags, pkgw->atom), NORM,
+                                                               
fmt_elapsedtime(flags, elapsed));
+                                       } else if (!flags->do_average) {
+                                               printf("%s <<< %s%s%s\n",
+                                                               fmt_date(flags, 
pkgw->tbegin, tstart),
+                                                               BLUE, 
fmt_atom(flags, pkgw->atom), NORM);
+                                       }
+                                       atom_implode(pkgw->atom);
+                                       xarraydelete(unmerge_matches, i);
+                                       break;
+                               }
+                       }
+               }
+       }
+       fclose(fp);
+       if (flags->do_running) {
+               /* can't report endtime for non-finished operations */
+               flags->do_endtime = 0;
+               tstart = time(NULL);
+               sync_time /= sync_cnt;
+               if (sync_start > 0) {
+                       if (elapsed >= sync_time)
+                               sync_time = 0;
+                       if (flags->do_time) {
+                               elapsed = tstart - sync_start;
+                               printf("%s *** %s%s%s: %s... ETA: %s\n",
+                                               fmt_date(flags, sync_start, 0),
+                                               YELLOW, "sync", NORM, 
fmt_elapsedtime(flags, elapsed),
+                                               sync_time == 0 ? "unknown" :
+                                                       fmt_elapsedtime(flags, 
sync_time - elapsed));
+                       } else {
+                               printf("%s *** %s%s%s... ETA: %s\n",
+                                               fmt_date(flags, sync_start, 0),
+                                               YELLOW, "sync", NORM,
+                                               sync_time == 0 ? "unknown" :
+                                                       fmt_elapsedtime(flags, 
sync_time - elapsed));
+                       }
+               }
+               array_for_each(merge_matches, i, pkgw) {
+                       size_t j;
+                       time_t maxtime = 0;
+
+                       elapsed = tstart - pkgw->tbegin;
+                       pkg = NULL;
+                       array_for_each(merge_averages, j, pkg) {
+                               if (atom_compare(pkg->atom, pkgw->atom) == 
EQUAL) {
+                                       maxtime = pkg->time / pkg->cnt;
+                                       if (elapsed >= maxtime)
+                                               maxtime = elapsed >= 
pkg->tbegin ? 0 : pkg->tbegin;
+                                       break;
+                               }
+                               pkg = NULL;
+                       }
+
+                       if (flags->do_time) {
+                               printf("%s >>> %s%s%s: %s... ETA: %s\n",
+                                               fmt_date(flags, pkgw->tbegin, 
0),
+                                               YELLOW, fmt_atom(flags, 
pkgw->atom), NORM,
+                                               fmt_elapsedtime(flags, elapsed),
+                                               maxtime == 0 ? "unknown" :
+                                                       fmt_elapsedtime(flags, 
maxtime - elapsed));
+                       } else {
+                               printf("%s >>> %s%s%s... ETA: %s\n",
+                                               fmt_date(flags, pkgw->tbegin, 
0),
+                                               YELLOW, fmt_atom(flags, 
pkgw->atom), NORM,
+                                               maxtime == 0 ? "unknown" :
+                                                       fmt_elapsedtime(flags, 
maxtime - elapsed));
+                       }
+               }
+               array_for_each(unmerge_matches, i, pkgw) {
+                       size_t j;
+                       time_t maxtime = 0;
+
+                       elapsed = tstart - pkgw->tbegin;
+                       pkg = NULL;
+                       array_for_each(unmerge_averages, j, pkg) {
+                               if (atom_compare(pkg->atom, pkgw->atom) == 
EQUAL) {
+                                       maxtime = pkg->time / pkg->cnt;
+                                       if (elapsed >= maxtime)
+                                               maxtime = elapsed >= 
pkg->tbegin ? 0 : pkg->tbegin;
+                                       break;
+                               }
+                               pkg = NULL;
+                       }
+
+                       if (flags->do_time) {
+                               printf("%s <<< %s%s%s: %s... ETA: %s\n",
+                                               fmt_date(flags, pkgw->tbegin, 
0),
+                                               YELLOW, fmt_atom(flags, 
pkgw->atom), NORM,
+                                               fmt_elapsedtime(flags, elapsed),
+                                               maxtime == 0 ? "unknown" :
+                                                       fmt_elapsedtime(flags, 
maxtime - elapsed));
+                       } else {
+                               printf("%s <<< %s%s%s... ETA: %s\n",
+                                               fmt_date(flags, pkgw->tbegin, 
0),
+                                               YELLOW, fmt_atom(flags, 
pkgw->atom), NORM,
+                                               maxtime == 0 ? "unknown" :
+                                                       fmt_elapsedtime(flags, 
maxtime - elapsed));
+                       }
+               }
+       } else if (flags->do_average) {
+               size_t total_merges = 0;
+               size_t total_unmerges = 0;
+               time_t total_time = (time_t)0;
+
+               array_for_each(merge_averages, i, pkg) {
+                       printf("%s%s%s: %s average for %s%zd%s merge%s\n",
+                                       BLUE, fmt_atom(flags, pkg->atom), NORM,
+                                       fmt_elapsedtime(flags, pkg->time / 
pkg->cnt),
+                                       GREEN, pkg->cnt, NORM, pkg->cnt == 1 ? 
"" : "s");
+                       total_merges += pkg->cnt;
+                       total_time += pkg->time;
+               }
+               array_for_each(unmerge_averages, i, pkg) {
+                       printf("%s%s%s: %s average for %s%zd%s unmerge%s\n",
+                                       BLUE, fmt_atom(flags, pkg->atom), NORM,
+                                       fmt_elapsedtime(flags, pkg->time / 
pkg->cnt),
+                                       GREEN, pkg->cnt, NORM, pkg->cnt == 1 ? 
"" : "s");
+                       total_unmerges += pkg->cnt;
+                       total_time += pkg->time;
+               }
+               if (sync_cnt > 0) {
+                       printf("%ssync%s: %s average for %s%zd%s sync%s\n",
+                                       BLUE, NORM, fmt_elapsedtime(flags, 
sync_time / sync_cnt),
+                                       GREEN, sync_cnt, NORM, sync_cnt == 1 ? 
"" : "s");
+                       total_time += sync_time;
+               }
+               if (flags->do_summary) {
+                       /* 123 seconds for 5 merges, 3 unmerges, 1 sync */
+                       printf("%stotal%s: %s for ",
+                                       BLUE, NORM, fmt_elapsedtime(flags, 
total_time));
+                       if (total_merges > 0)
+                               printf("%s%zd%s merge%s",
+                                       GREEN, total_merges, NORM, total_merges 
== 1 ? "" : "s");
+                       if (total_unmerges > 0)
+                               printf("%s%s%zd%s unmerge%s",
+                                               total_merges == 0 ? "" : ", ",
+                                               GREEN, total_unmerges, NORM,
+                                               total_unmerges == 1 ? "" : "s");
+                       if (sync_cnt > 0)
+                               printf("%s%s%zd%s sync%s",
+                                               total_merges + total_unmerges 
== 0 ? "" : ", ",
+                                               GREEN, sync_cnt, NORM, sync_cnt 
== 1 ? "" : "s");
+                       printf("\n");
+               }
+       }
+       return 0;
+}
+
 int qlop_main(int argc, char **argv)
 {
        size_t i;
        int ret;
-       int average = 1;
-       time_t start_time, end_time;
-       char do_time, do_list, do_unlist, do_sync, do_current, 
do_human_readable = 0;
+       time_t start_time;
+       time_t end_time;
+       struct qlop_mode m;
        char *logfile = NULL;
-       int flags;
+       char *atomfile = NULL;
+       char *p;
+       char *q;
        depend_atom *atom;
        DECLARE_ARRAY(atoms);
 
        start_time = 0;
        end_time = LONG_MAX;
-       do_time = do_list = do_unlist = do_sync = do_current = 0;
+       m.do_time = 0;
+       m.do_merge = 0;
+       m.do_unmerge = 0;
+       m.do_autoclean = 0;
+       m.do_sync = 0;
+       m.do_running = 0;
+       m.do_average = 0;
+       m.do_summary = 0;
+       m.do_human = 0;
+       m.do_endtime = 0;
 
        while ((ret = GETOPT_LONG(QLOP, qlop, "")) != -1) {
                switch (ret) {
                        COMMON_GETOPTS_CASES(qlop)
 
-                       case 't': do_time = 1; break;
-                       case 'l': do_list = 1; break;
-                       case 'u': do_unlist = 1; break;
-                       case 's': do_sync = 1; break;
-                       case 'c': do_current = 1; break;
-                       case 'g': do_time = 1; average = 0; break;
-                       case 'H': do_human_readable = 1; break;
+                       case 't': m.do_time = 1;      break;
+                       case 'm': m.do_merge = 1;     break;
+                       case 'u': m.do_unmerge = 1;   break;
+                       case 'U': m.do_autoclean = 1; break;
+                       case 's': m.do_sync = 1;      break;
+                       case 'r': m.do_running = 1;   break;
+                       case 'a': m.do_average = 1;   break;
+                       case 'c': m.do_summary = 1;   break;
+                       case 'H': m.do_human = 1;     break;
+                       case 'e': m.do_endtime = 1;   break;
                        case 'd':
                                if (start_time == 0) {
                                        if (!parse_date(optarg, &start_time))
@@ -851,13 +861,19 @@ int qlop_main(int argc, char **argv)
                                        err("too many -d options");
                                break;
                        case 'f':
-                               if (logfile) err("Only use -f once");
+                               if (logfile != NULL)
+                                       err("Only use -f once");
                                logfile = xstrdup(optarg);
                                break;
+                       case 'w':
+                               if (atomfile != NULL)
+                                       err("Only use -w once");
+                               if (!eat_file(optarg, &atomfile, &i))
+                                       err("failed to open file %s", optarg);
+                               break;
                }
        }
-       if (!do_list && !do_unlist && !do_time && !do_sync && !do_current)
-               qlop_usage(EXIT_FAILURE);
+
        if (logfile == NULL)
                xasprintf(&logfile, "%s/%s", portlogdir, QLOP_DEFAULT_LOGFILE);
 
@@ -870,26 +886,75 @@ int qlop_main(int argc, char **argv)
                else
                        xarraypush_ptr(atoms, atom);
        }
+       for (p = atomfile; p != NULL && *p != '\0'; p = q) {
+               while (isspace((int)(*p)))
+                       p++;
+               q = strchr(p, '\n');
+               if (q != NULL) {
+                       *q = '\0';
+                       q++;
+               }
+               atom = atom_explode(p);
+               if (!atom)
+                       warn("invalid atom: %s", p);
+               else
+                       xarraypush_ptr(atoms, atom);
+       }
+       if (atomfile)
+               free(atomfile);
+
+       /* default operation: -must */
+       if (
+                       m.do_time == 0 &&
+                       m.do_merge == 0 &&
+                       m.do_unmerge == 0 &&
+                       m.do_autoclean == 0 &&
+                       m.do_sync == 0 &&
+                       m.do_running == 0 &&
+                       m.do_average == 0 &&
+                       m.do_summary == 0 &&
+                       m.do_human == 0
+               )
+       {
+               m.do_merge = 1;
+               m.do_unmerge = 1;
+               m.do_sync = 1;
+               m.do_time = 1;
+       }
+
+       /* handle deps */
+       if (m.do_summary)
+               m.do_average = 1;
+
+       /* handle -a / -t conflict */
+       if (m.do_average && m.do_time) {
+               warn("-a (or -c) and -t cannot be used together, dropping -t");
+               m.do_time = 0;
+       }
 
-       flags = 0;
-       if (do_list)
-               flags |= QLOP_LIST;
-       if (do_unlist)
-               flags |= QLOP_UNLIST;
-       if (flags)
-               show_emerge_history(flags, atoms, logfile, start_time, 
end_time);
-
-       if (do_current)
-               show_current_emerge();
-       if (do_sync)
-               show_sync_history(logfile, start_time, end_time);
-
-       if (do_time) {
-               for (i = 0; i < (size_t)argc; ++i)
-                       show_merge_times(argv[i], logfile, average, 
do_human_readable,
-                               start_time, end_time);
+       /* handle -a / -r conflict */
+       if (m.do_average && m.do_running) {
+               warn("-a (or -c) and -r cannot be used together, dropping -a");
+               m.do_average = 0;
        }
 
+       if (m.do_sync && array_cnt(atoms) > 0) {
+               warn("-s cannot be used when specifying atoms, dropping -s");
+               m.do_sync = 0;
+       }
+
+       /* set default for -t, -a or -r */
+       if ((m.do_average || m.do_time || m.do_running) &&
+                       !(m.do_merge || m.do_unmerge || m.do_sync))
+       {
+               m.do_merge = 1;
+               m.do_unmerge = 1;
+               if (array_cnt(atoms) == 0)
+                       m.do_sync = 1;
+       }
+
+       do_emerge_log(logfile, &m, atoms, start_time, end_time);
+
        array_for_each(atoms, i, atom)
                atom_implode(atom);
        xarrayfree_int(atoms);

diff --git a/tests/qlop/dotest b/tests/qlop/dotest
index ac30924..51cdb8a 100755
--- a/tests/qlop/dotest
+++ b/tests/qlop/dotest
@@ -25,26 +25,26 @@ export LC_TIME="C"
 # simple sync check
 test 01 0 "qlop -s -f ${as}/sync.log"
 
-# check all installed pkgs
-test 02 0 "qlop -l -f ${as}/sync.log"
+# check all merged pkgs
+test 02 0 "qlop -mv -f ${as}/sync.log"
 
-# check all uninstalled pkgs
-test 03 0 "qlop -u -f ${as}/sync.log"
+# check all unmerged pkgs
+test 03 0 "qlop -uv -f ${as}/sync.log"
 
 # verify atom parsing works (and not partial strings)
-test 04 0 "qlop -l gcc -f ${as}/sync.log"
+test 04 0 "qlop -mv gcc -f ${as}/sync.log"
 
 # verify atom version parsing works
-test 05 0 "qlop -l '<gcc-5' -f ${as}/sync.log"
+test 05 0 "qlop -mv '<gcc-5' -f ${as}/sync.log"
 
 # check date time parser, note on date parsing,
 # https://bugs.gentoo.org/638032#c6 so the format %d%Y%m isn't compliant
-test 06 0 "qlop -l -f ${as}/sync.log -d 2005-01-01"
-test 07 0 "qlop -l -f ${as}/sync.log -d '%d %Y %m|01 2005 01'"
-test 08 0 "qlop -l -f ${as}/sync.log -d 1104898893"
+test 06 0 "qlop -mv -f ${as}/sync.log -d 2005-01-01"
+test 07 0 "qlop -mv -f ${as}/sync.log -d '%d %Y %m|01 2005 01'"
+test 08 0 "qlop -mv -f ${as}/sync.log -d 1104898893"
 
 # deal with aborted merges
-test 09 0 "qlop -Htgv automake -f ${as}/aborts.log"
+test 09 0 "qlop -Hacv automake -f ${as}/aborts.log"
 
 cleantmpdir
 

diff --git a/tests/qlop/list01.good b/tests/qlop/list01.good
index fc8683c..2689952 100644
--- a/tests/qlop/list01.good
+++ b/tests/qlop/list01.good
@@ -1,2 +1,2 @@
-Thu Jan 27 05:42:17 2005 >>> rsync://192.168.0.5/gentoo-portage
-Sat May 16 08:21:00 2015 >>> gentoo
+2005-01-27T05:35:03 *** rsync://192.168.0.5/gentoo-portage
+2015-05-16T08:20:02 *** gentoo

diff --git a/tests/qlop/list02.good b/tests/qlop/list02.good
index a00a3f6..706bdac 100644
--- a/tests/qlop/list02.good
+++ b/tests/qlop/list02.good
@@ -1,3 +1,3 @@
-Thu Oct 28 06:49:14 2004 >>> dev-util/ccache-2.3
-Thu Jan 27 05:56:39 2005 >>> sys-devel/gcc-config-1.3.9
-Thu Jan 27 06:17:10 2005 >>> sys-devel/gcc-3.4.3-r1
+2004-10-28T06:49:13 >>> dev-util/ccache-2.3
+2005-01-27T05:56:28 >>> sys-devel/gcc-config-1.3.9
+2005-01-27T05:56:39 >>> sys-devel/gcc-3.4.3-r1

diff --git a/tests/qlop/list03.good b/tests/qlop/list03.good
index add774a..cccc8bb 100644
--- a/tests/qlop/list03.good
+++ b/tests/qlop/list03.good
@@ -1,4 +1,4 @@
-Thu Jan 27 05:56:39 2005 <<< sys-devel/gcc-config-1.3.6-r3
-Thu Jan 27 05:58:04 2005 <<< sys-apps/pam-login-3.14
-Thu Jan 27 05:58:06 2005 <<< sys-libs/pam-0.77-r1
-Thu Jan 27 05:58:16 2005 <<< sys-fs/devfsd-1.3.25-r8
+2005-01-27T05:56:38 <<< sys-devel/gcc-config-1.3.6-r3
+2005-01-27T05:58:02 <<< sys-apps/pam-login-3.14
+2005-01-27T05:58:04 <<< sys-libs/pam-0.77-r1
+2005-01-27T05:58:15 <<< sys-fs/devfsd-1.3.25-r8

diff --git a/tests/qlop/list04.good b/tests/qlop/list04.good
index c3de519..aa39ed2 100644
--- a/tests/qlop/list04.good
+++ b/tests/qlop/list04.good
@@ -1 +1 @@
-Thu Jan 27 06:17:10 2005 >>> sys-devel/gcc-3.4.3-r1
+2005-01-27T05:56:39 >>> sys-devel/gcc-3.4.3-r1

diff --git a/tests/qlop/list05.good b/tests/qlop/list05.good
index c3de519..aa39ed2 100644
--- a/tests/qlop/list05.good
+++ b/tests/qlop/list05.good
@@ -1 +1 @@
-Thu Jan 27 06:17:10 2005 >>> sys-devel/gcc-3.4.3-r1
+2005-01-27T05:56:39 >>> sys-devel/gcc-3.4.3-r1

diff --git a/tests/qlop/list06.good b/tests/qlop/list06.good
index e9fccc3..769aa7b 100644
--- a/tests/qlop/list06.good
+++ b/tests/qlop/list06.good
@@ -1,2 +1,2 @@
-Thu Jan 27 05:56:39 2005 >>> sys-devel/gcc-config-1.3.9
-Thu Jan 27 06:17:10 2005 >>> sys-devel/gcc-3.4.3-r1
+2005-01-27T05:56:28 >>> sys-devel/gcc-config-1.3.9
+2005-01-27T05:56:39 >>> sys-devel/gcc-3.4.3-r1

diff --git a/tests/qlop/list07.good b/tests/qlop/list07.good
index e9fccc3..769aa7b 100644
--- a/tests/qlop/list07.good
+++ b/tests/qlop/list07.good
@@ -1,2 +1,2 @@
-Thu Jan 27 05:56:39 2005 >>> sys-devel/gcc-config-1.3.9
-Thu Jan 27 06:17:10 2005 >>> sys-devel/gcc-3.4.3-r1
+2005-01-27T05:56:28 >>> sys-devel/gcc-config-1.3.9
+2005-01-27T05:56:39 >>> sys-devel/gcc-3.4.3-r1

diff --git a/tests/qlop/list08.good b/tests/qlop/list08.good
index e9fccc3..769aa7b 100644
--- a/tests/qlop/list08.good
+++ b/tests/qlop/list08.good
@@ -1,2 +1,2 @@
-Thu Jan 27 05:56:39 2005 >>> sys-devel/gcc-config-1.3.9
-Thu Jan 27 06:17:10 2005 >>> sys-devel/gcc-3.4.3-r1
+2005-01-27T05:56:28 >>> sys-devel/gcc-config-1.3.9
+2005-01-27T05:56:39 >>> sys-devel/gcc-3.4.3-r1

diff --git a/tests/qlop/list09.good b/tests/qlop/list09.good
index 333d7ad..269ec03 100644
--- a/tests/qlop/list09.good
+++ b/tests/qlop/list09.good
@@ -1,3 +1,3 @@
-automake-1.11.6: Mon Apr  1 12:34:09 2013: 53 minutes, 52 seconds
-automake-1.9.6-r3: Fri May 17 12:58:39 2013: 24 minutes, 7 seconds
-automake: 2 times
+sys-devel/automake-1.11.6: 53 minutes, 52 seconds average for 1 merge
+sys-devel/automake-1.9.6-r3: 24 minutes, 7 seconds average for 1 merge
+total: 1 hour, 17 minutes, 59 seconds for 2 merges

Reply via email to