header option to prepend value scale
[barcat.git] / barcat
diff --git a/barcat b/barcat
index b051353ecf2d228ffaeade4e1ab19021cfc569b4..268c993db3a76b223e04114c6506489b727e5c25 100755 (executable)
--- a/barcat
+++ b/barcat
@@ -6,7 +6,7 @@ use List::Util qw( min max sum );
 use open qw( :std :utf8 );
 use experimental qw( lexical_subs );
 
 use open qw( :std :utf8 );
 use experimental qw( lexical_subs );
 
-our $VERSION = '1.05';
+our $VERSION = '1.06';
 
 use Getopt::Long '2.33', qw( :config gnu_getopt );
 my %opt;
 
 use Getopt::Long '2.33', qw( :config gnu_getopt );
 my %opt;
@@ -42,13 +42,22 @@ GetOptions(\%opt,
                        " (range expected)\n"
                );
        },
                        " (range expected)\n"
                );
        },
+       'header!',
        'markers|m=s',
        'markers|m=s',
+       'graph-format=s' => sub {
+               $opt{'graph-format'} = substr $_[1], 0, 1;
+       },
        'spark:s' => sub {
        'spark:s' => sub {
-               $opt{spark} = [split //, $_[1] || '������▂▃▄▅▆▇█'];
+               $opt{spark} = [split //, $_[1] || '▁▂▃▄▅▆▇█'];
        },
        'stat|s!',
        },
        'stat|s!',
+       'signal-stat=s',
        'unmodified|u!',
        'width|w=i',
        'unmodified|u!',
        'width|w=i',
+       'version' => sub {
+               say "barcat version $VERSION";
+               exit;
+       },
        'usage|h' => sub {
                local $/;
                my $pod = readline *DATA;
        'usage|h' => sub {
                local $/;
                my $pod = readline *DATA;
@@ -75,22 +84,26 @@ GetOptions(\%opt,
 
 $opt{width} ||= $ENV{COLUMNS} || 80;
 $opt{color} //= -t *STDOUT;  # enable on tty
 
 $opt{width} ||= $ENV{COLUMNS} || 80;
 $opt{color} //= -t *STDOUT;  # enable on tty
+$opt{'graph-format'} //= '-';
 $opt{trim}   *= $opt{width} / 100 if $opt{trimpct};
 $opt{units}   = [split //, ' kMGTPEZYyzafpnμm'] if $opt{'human-readable'};
 $opt{anchor} //= qr/\A/;
 $opt{'value-length'} = 6 if $opt{units};
 $opt{'value-length'} = 1 if $opt{unmodified};
 $opt{trim}   *= $opt{width} / 100 if $opt{trimpct};
 $opt{units}   = [split //, ' kMGTPEZYyzafpnμm'] if $opt{'human-readable'};
 $opt{anchor} //= qr/\A/;
 $opt{'value-length'} = 6 if $opt{units};
 $opt{'value-length'} = 1 if $opt{unmodified};
+$opt{'signal-stat'} //= exists $SIG{INFO} ? 'INFO' : 'QUIT';
 
 my (@lines, @values, @order);
 
 
 my (@lines, @values, @order);
 
+$SIG{$_} = \&show_stat for $opt{'signal-stat'} || ();
 $SIG{ALRM} = sub {
        show_lines();
 $SIG{ALRM} = sub {
        show_lines();
-       alarm $opt{interval} if defined $opt{interval};
+       alarm $opt{interval} if defined $opt{interval} and $opt{interval} > 0;
 };
 };
+$SIG{INT} = \&show_exit;
 
 if (defined $opt{interval}) {
        $opt{interval} ||= 1;
 
 if (defined $opt{interval}) {
        $opt{interval} ||= 1;
-       alarm $opt{interval};
+       alarm $opt{interval} if $opt{interval} > 0;
 
        eval {
                require Tie::Array::Sorted;
 
        eval {
                require Tie::Array::Sorted;
@@ -98,11 +111,6 @@ if (defined $opt{interval}) {
        } or warn $@, "Expect slowdown with large datasets!\n";
 }
 
        } or warn $@, "Expect slowdown with large datasets!\n";
 }
 
-$SIG{INT} = sub {
-       $SIG{INT} = 'DEFAULT';  # reset for subsequent attempts
-       'IGNORE' # continue after assumed eof
-};
-
 my $valmatch = qr/$opt{anchor} ( \h* -? [0-9]* \.? [0-9]+ (?: e[+-]?[0-9]+ )? |)/x;
 while (readline) {
        s/\r?\n\z//;
 my $valmatch = qr/$opt{anchor} ( \h* -? [0-9]* \.? [0-9]+ (?: e[+-]?[0-9]+ )? |)/x;
 while (readline) {
        s/\r?\n\z//;
@@ -120,6 +128,8 @@ while (readline) {
                }
        }
        push @lines, $_;
                }
        }
        push @lines, $_;
+       show_lines() if defined $opt{interval} and $opt{interval} < 0
+               and $. % $opt{interval} == 0;
 }
 
 $SIG{INT} = 'DEFAULT';
 }
 
 $SIG{INT} = 'DEFAULT';
@@ -183,6 +193,8 @@ sub sival {
        );
 }
 
        );
 }
 
+say sprintf '%*s-+%*s-+', $lenval, $minval, $size * ($maxval - $minval) - 3, $maxval if $opt{header};
+
 while ($nr <= $#lines) {
        $nr >= $opt{hidemax} and last if defined $opt{hidemax};
        my $val = $values[$nr];
 while ($nr <= $#lines) {
        $nr >= $opt{hidemax} and last if defined $opt{hidemax};
        my $val = $values[$nr];
@@ -202,7 +214,7 @@ while ($nr <= $#lines) {
        }
        my $line = $lines[$nr] =~ s/\n/$val/r;
        printf '%-*s', $len + length($val), $line;
        }
        my $line = $lines[$nr] =~ s/\n/$val/r;
        printf '%-*s', $len + length($val), $line;
-       print $barmark[$_] // '-' for 1 .. $size && (($values[$nr] || 0) - $minval) * $size + .5;
+       print $barmark[$_] // $opt{'graph-format'} for 1 .. $size && (($values[$nr] || 0) - $minval) * $size + .5;
        say '';
 }
 continue {
        say '';
 }
 continue {
@@ -211,9 +223,8 @@ continue {
 say '' if $opt{spark};
 
 }
 say '' if $opt{spark};
 
 }
-show_lines();
 
 
-if ($opt{stat}) {
+sub show_stat {
        if ($opt{hidemin} or $opt{hidemax}) {
                $opt{hidemin} ||= 1;
                $opt{hidemax} ||= @lines;
        if ($opt{hidemin} or $opt{hidemax}) {
                $opt{hidemin} ||= 1;
                $opt{hidemax} ||= @lines;
@@ -232,6 +243,15 @@ if ($opt{stat}) {
        say '';
 }
 
        say '';
 }
 
+sub show_exit {
+       show_lines();
+       show_stat() if $opt{stat};
+       exit 130 if @_;  # 0x80+signo
+       exit;
+}
+
+show_exit();
+
 __END__
 =encoding utf8
 
 __END__
 =encoding utf8
 
@@ -277,6 +297,10 @@ A string can indicate the starting position of a value
 or capture the numbers itself,
 for example I<-f'(\d+)'> for the first digits anywhere.
 
 or capture the numbers itself,
 for example I<-f'(\d+)'> for the first digits anywhere.
 
+=item --header
+
+Prepend a chart axis with minimum and maximum values labeled.
+
 =item -H, --human-readable
 
 Format values using SI unit prefixes,
 =item -H, --human-readable
 
 Format values using SI unit prefixes,
@@ -284,9 +308,9 @@ 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.
 
 Also changes an exponent I<1.602176634e-19> to I<160.2z>.
 Short integers are aligned but kept without decimal point.
 
-=item -t, --interval[=<seconds>]
+=item -t, --interval[=(<seconds>|-<lines>)]
 
 
-Interval time to output partial progress.
+Output partial progress every given number of seconds or input lines.
 An update can also be forced by sending a I<SIGALRM> alarm signal.
 
 =item -l, --length=[-]<size>[%]
 An update can also be forced by sending a I<SIGALRM> alarm signal.
 
 =item -l, --length=[-]<size>[%]
@@ -305,6 +329,11 @@ Stop output after a number of lines.
 All input is still counted and analyzed for statistics,
 but disregarded for padding and bar size.
 
 All input is still counted and analyzed for statistics,
 but disregarded for padding and bar size.
 
+=item --graph-format=<character>
+
+Glyph to repeat for the graph line.
+Defaults to a dash C<->.
+
 =item -m, --markers=
 
 Statistical positions to indicate on bars.
 =item -m, --markers=
 
 Statistical positions to indicate on bars.
@@ -338,6 +367,18 @@ or 68% of all entries.
 
 =back
 
 
 =back
 
+=item --spark[=<glyphs>]
+
+Replace lines by I<sparklines>,
+single characters corresponding to input values.
+A specified sequence of unicode characters will be used for
+Of a specified sequence of unicode characters,
+the first one will be used for non-values,
+the last one for the maximum,
+the second (if any) for the minimum,
+and any remaining will be distributed over the range of values.
+Unspecified, block fill glyphs U+2581-2588 will be used.
+
 =item -s, --stat
 
 Total statistics after all data.
 =item -s, --stat
 
 Total statistics after all data.