sexagesimal time formatting option
[barcat.git] / barcat
diff --git a/barcat b/barcat
index 253ec8685c81d922d87c5d189af3770e4f160628..1a1112c0ccdfa530e55b1792a7d25bf21f5381c1 100755 (executable)
--- a/barcat
+++ b/barcat
@@ -14,15 +14,20 @@ require Getopt::Long;
 Getopt::Long->import('2.33', qw( :config gnu_getopt ));
 GetOptions(\%opt,
        'ascii|a!',
-       'color|c!',
-       'C' => sub { $opt{color} = 0 },
+       'color|C!',
+       'M' => sub { $opt{color} = 0 },
        'field|f=s' => sub {
                eval {
                        local $_ = $_[1];
-                       $opt{anchor} = /\A[0-9]+\z/ ? qr/(?:\S*\h+){$_}\K/ : qr/$_/;
+                       s/\A[0-9]+\z/(?:\\S*\\h+){$_}\\K/;
+                       s{\A[+]([0-9]*)\z}{
+                               (!!$1 && '(?:\d+\D+\b){'.$1.'}\K') . '\s* (?=\d)'
+                       }e;
+                       $opt{anchor} = qr/$_/;
                } or die $@ =~ s/(?:\ at\ \N+)?\Z/ for option $_[0]/r;
        },
        'human-readable|H!',
+       'sexagesimal!',
        'interval|t:i',
        'trim|length|l=s' => sub {
                my ($optname, $optval) = @_;
@@ -57,6 +62,7 @@ GetOptions(\%opt,
        'indicators:s',
        'palette=s' => sub {
                $opt{palette} = {
+                       ''     => [],
                        fire   => [qw( 90 31 91 33 93 97 96 )],
                        fire256=> [map {"38;5;$_"} qw(
                                235  52 88 124 160 196
@@ -64,7 +70,22 @@ GetOptions(\%opt,
                        )],
                        whites => [qw( 1;30 0;37 1;37 )],
                        greys  => [map {"38;5;$_"} 0, 232..255, 15],
-               }->{$_[1]} // [ split /[^0-9;]/, $_[1] ];
+                       random => [map {"38;5;$_"} List::Util::shuffle(17..231)],
+                       rainbow=> [map {"38;5;$_"}
+                               196, # r
+                               (map { 196 + $_*6   } 0..4), # +g
+                               (map { 226 - $_*6*6 } 0..4), # -r
+                               (map {  46 + $_     } 0..4), # +b
+                               (map {  51 - $_*6   } 0..4), # -g
+                               (map {  21 + $_*6*6 } 0..4), # +r
+                               (map { 201 - $_     } 0..4), # -b
+                               196,
+                       ],
+               }->{$_[1]} // do {
+                       my @vals = split /[^0-9;]/, $_[1]
+                               or die "Empty palette resulting from \"$_[1]\"\n";
+                       \@vals;
+               };
        },
        'stat|s!',
        'signal-stat=s',
@@ -89,7 +110,7 @@ GetOptions(\%opt,
 }
 
 $opt{width} ||= $ENV{COLUMNS} || qx(tput cols) || 80 unless $opt{spark};
-$opt{color} //= -t *STDOUT;  # enable on tty
+$opt{color} //= $ENV{NO_COLOR} ? 0 : -t *STDOUT;  # enable on tty
 $opt{'graph-format'} //= '-';
 $opt{trim}   *= $opt{width} / 100 if $opt{trimpct};
 $opt{units}   = [split //, ' kMGTPEZYyzafpn'.($opt{ascii} ? 'u' : 'μ').'m']
@@ -100,8 +121,8 @@ $opt{'value-length'} = 1 if $opt{unmodified};
 $opt{'signal-stat'} //= exists $SIG{INFO} ? 'INFO' : 'QUIT';
 $opt{markers} //= '=avg >31.73v <68.27v +50v |0';
 $opt{palette} //= $opt{color} && [31, 90, 32];
-$opt{indicators} = [split //,
-       $opt{indicators} || ($opt{ascii} ? ' .oO' : ' ▁▂▃▄▅▆▇█')
+$opt{indicators} = [split //, $opt{indicators} ||
+       ($opt{ascii} ? ' .oO' : $opt{spark} ? ' ▁▂▃▄▅▆▇█' : ' ▏▎▍▌▋▊▉█')
 ] if defined $opt{indicators} or $opt{spark};
 $opt{hidemin} = ($opt{hidemin} || 1) - 1;
 $opt{input} = (@ARGV && $ARGV[0] =~ m/\A[-0-9]/) ? \@ARGV : undef
@@ -109,7 +130,10 @@ $opt{input} = (@ARGV && $ARGV[0] =~ m/\A[-0-9]/) ? \@ARGV : undef
 
 $opt{'sum-format'} = sub { sprintf '%.8g', $_[0] };
 $opt{'calc-format'} = sub { sprintf '%*.*f', 0, 2, $_[0] };
-$opt{'value-format'} = $opt{units} && sub {
+$opt{'value-format'} = $opt{sexagesimal} ? sub {
+       my $s = $_[0] + .5;
+       sprintf('%d:%02d:%02d', $s/3600, $s/60%60, $s%60);
+} : $opt{units} && sub {
        my $unit = (
                log(abs $_[0] || 1) / log(10)
                - 3 * (abs($_[0]) < .9995)   # shift to smaller unit if below 1
@@ -269,13 +293,13 @@ say(
 
 while ($nr <= $limit) {
        my $val = $values[$nr];
-       my $rel = length $val && $range && ($val - $minval) / $range;
+       my $rel = length $val && $range && min(1, ($val - $minval) / $range);
        my $color = !length $val || !$opt{palette} ? undef :
                $val == $order[0] ? $opt{palette}->[-1] : # max
                $val == $order[-1] ? $opt{palette}->[0] : # min
                $opt{palette}->[ $rel * ($#{$opt{palette}} - 1) + 1 ];
        my $indicator = $opt{indicators} && $opt{indicators}->[
-               !$val || !$#{$opt{indicators}} ? 0 : # blank
+               !length($val) || !$#{$opt{indicators}} ? 0 : # blank
                $#{$opt{indicators}} < 2 ? 1 :
                $val >= $order[0] ? -1 :
                $rel * ($#{$opt{indicators}} - 1e-14) + 1
@@ -353,12 +377,14 @@ Usage:                                               /\_/\
                                                     (u   u)
 Options:
   -a, --[no-]ascii         Restrict user interface to ASCII characters
-  -c, --[no-]color         Force colored output of values and bar markers
-  -f, --field=(N|REGEXP)   Compare values after a given number of whitespace
+  -C, --[no-]color         Force colored output of values and bar markers
+  -f, --field=([+]N|REGEXP)
+                           Compare values after a given number of whitespace
                            separators
       --header             Prepend a chart axis with minimum and maximum
                            values labeled
   -H, --human-readable     Format values using SI unit prefixes
+      --sexagesimal        Convert seconds to HH:MM:SS time format
   -t, --interval[=(N|-LINES)]
                            Output partial progress every given number of
                            seconds or input lines
@@ -414,13 +440,15 @@ Restrict user interface to ASCII characters,
 replacing default UTF-8 by their closest approximation.
 Input is always interpreted as UTF-8 and shown as is.
 
-=item -c, --[no-]color
+=item -C, --[no-]color
 
 Force colored output of values and bar markers.
 Defaults on if output is a tty,
 disabled otherwise such as when piped or redirected.
+Can also be disabled by setting I<-M>
+or the I<NO_COLOR> environment variable.
 
-=item -f, --field=(<number> | <regexp>)
+=item -f, --field=([+]<number> | <regexp>)
 
 Compare values after a given number of whitespace separators,
 or matching a regular expression.
@@ -431,6 +459,7 @@ A string can indicate the starting position of a value
 (such as I<-f:> if preceded by colons),
 or capture the numbers itself,
 for example I<-f'(\d+)'> for the first digits anywhere.
+A shorthand for this is I<+0>, or I<+N> to find the Nth number.
 
 =item --header
 
@@ -443,6 +472,10 @@ turning long numbers like I<12356789> into I<12.4M>.
 Also changes an exponent I<1.602176634e-19> to I<160.2z>.
 Short integers are aligned but kept without decimal point.
 
+=item --sexagesimal
+
+Convert seconds to HH:MM:SS time format.
+
 =item -t, --interval[=(<seconds> | -<lines>)]
 
 Output partial progress every given number of seconds or input lines.
@@ -581,6 +614,20 @@ Compare file sizes (with human-readable numbers):
 
     du -d0 -b * | barcat -H
 
+Same from formatted results, selecting the first numeric value:
+
+    tree -s --noreport | barcat -H -f+
+
+Compare media metadata, like image size or play time:
+
+    exiftool -T -p '$megapixels ($imagesize) $filename' * | barcat
+
+    exiftool -T -p '$duration# $avgbitrate# $filename' * | barcat --sexagesimal
+
+    find -type f -print0 | xargs -0 -L1 \
+    ffprobe -show_format -of json -v error |
+    jq -r '.format|.duration+" "+.bit_rate+" "+.filename' | barcat --sex
+
 Memory usage of user processes with long names truncated:
 
     ps xo rss,pid,cmd | barcat -l40
@@ -639,6 +686,11 @@ Total population history in XML from the World Bank:
     xmlstarlet sel -t -m '*/*' -v wb:date -o ' ' -v wb:value -n |
     barcat -f1 -H
 
+Population and other information for all countries:
+
+    curl http://download.geonames.org/export/dump/countryInfo.txt |
+    grep -v '^#\s' | column -tns$'\t' | barcat -f+2 -u -l150 -s
+
 And of course various Git statistics, such commit count by year:
 
     git log --pretty=%ci | cut -b-4 | uniq -c | barcat
@@ -657,6 +709,10 @@ Sparkline graphics of simple input given as inline parameters:
 
     barcat -_ 3 1 4 1 5 0 9 2 4
 
+Misusing the spark functionality to draw a lolcat line:
+
+    seq $(tput cols) | barcat --spark --indicator=- --palette=rainbow
+
 =head1 AUTHOR
 
 Mischa POSLAWSKY <perl@shiar.org>