use utf8;
use List::Util qw( min max sum );
use open qw( :std :utf8 );
+use re '/msx';
-our $VERSION = '1.06';
+our $VERSION = '1.07';
use Getopt::Long '2.33', qw( :config gnu_getopt );
my %opt;
'field|f=s' => sub {
eval {
local $_ = $_[1];
- $opt{anchor} = /^[0-9]+$/ ? qr/(?:\S*\h+){$_}\K/ : qr/$_/;
- } or die $@ =~ s/(?: at .+)?$/ for option $_[0]/r;
+ $opt{anchor} = /\A[0-9]+\z/ ? qr/(?:\S*\h+){$_}\K/ : qr/$_/;
+ } or die $@ =~ s/(?:\ at\ \N+)?\Z/ for option $_[0]/r;
},
'human-readable|H!',
'interval|t:i',
'trim|length|l=s' => sub {
my ($optname, $optval) = @_;
$optval =~ s/%$// and $opt{trimpct}++;
- $optval =~ m/^-?[0-9]+$/ or die(
+ $optval =~ m/\A-?[0-9]+\z/ or die(
"Value \"$optval\" invalid for option $optname",
" (number or percentage expected)\n"
);
my ($optname, $optval) = @_;
$optval ||= 0;
($opt{hidemin}, $opt{hidemax}) =
- $optval =~ m/\A (?: ([0-9]+)? - )? ([0-9]+)? \z/x or die(
+ $optval =~ m/\A (?: ([0-9]+)? - )? ([0-9]+)? \z/ or die(
"Value \"$optval\" invalid for option limit",
" (range expected)\n"
);
exit;
},
'usage|h' => sub {
- local $/;
+ local $/ = undef; # slurp
my $pod = readline *DATA;
- $pod =~ s/^=over\K/ 25/m; # indent options list
- $pod =~ s/^=item \N*\n\n\N*\n\K(?:(?:^=over.*?^=back\n)?(?!=)\N*\n)*/\n/msg;
+ $pod =~ s/^=over\K/ 25/; # indent options list
+ $pod =~ s/^=item\ \N*\n\n\N*\n\K (?:(?:^=over.*?^=back\n)?(?!=)\N*\n)*/\n/g;
$pod =~ s/[.,](?=\n)//g; # trailing punctuation
- $pod =~ s/^=item \K(?=--)/____/gm; # align long options
+ $pod =~ s/^=item\ \K(?=--)/____/g; # align long options
# abbreviate <variable> indicators
$pod =~ s/\Q>.../s>/g;
$pod =~ s/<(?:number|count|seconds)>/N/g;
$parser->parse_string_document($pod);
$contents =~ s/\n(?=\n\h)//msg; # strip space between items
- $contents =~ s/^ \K____/ /gm; # nbsp substitute
+ $contents =~ s/^\ \ \K____/ /g; # nbsp substitute
print $contents;
exit;
},
$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{input} = @ARGV && $ARGV[0] =~ m/\A[-0-9]/ ? \@ARGV : undef
+$opt{hidemin} = ($opt{hidemin} || 1) - 1;
+$opt{input} = (@ARGV && $ARGV[0] =~ m/\A[-0-9]/) ? \@ARGV : undef
and undef $opt{interval};
my (@lines, @values, @order);
}
my $valmatch = qr<
- $opt{anchor} ( \h* -? [0-9]* \.? [0-9]+ (?: e[+-]?[0-9]+ )? |)
+ $opt{anchor} ( \h* -? [0-9]* [.]? [0-9]+ (?: e[+-]?[0-9]+ )? |)
>x;
while (defined ($_ = $opt{input} ? shift @{ $opt{input} } : readline)) {
s/\r?\n\z//;
- s/^\h*// unless $opt{unmodified};
- push @values, s/$valmatch/\n/ && $1;
- push @order, $1 if length $1;
- if (defined $opt{trim} and defined $1) {
+ s/\A\h*// unless $opt{unmodified};
+ my $valnum = s/$valmatch/\n/ && $1;
+ push @values, $valnum;
+ push @order, $valnum if length $valnum;
+ if (defined $opt{trim} and defined $valnum) {
my $trimpos = abs $opt{trim};
- $trimpos -= length $1 if $opt{unmodified};
+ $trimpos -= length $valnum if $opt{unmodified};
if ($trimpos <= 1) {
$_ = substr $_, 0, 2;
}
$_ = color(@_) . $_ . color(0) if defined;
}
+sub sival {
+ my $unit = int(log(abs $_[0] || 1) / log(10) - 3*($_[0] < 1) + 1e-15);
+ my $float = $_[0] !~ /\A0*[-0-9]{1,3}\z/;
+ return sprintf('%3.*f%1s',
+ $float && ($unit % 3) == ($unit < 0), # tenths
+ $_[0] / 1000 ** int($unit/3), # number
+ $#{$opt{units}} * 1.5 < abs $unit ? "e$unit" : $opt{units}->[$unit/3]
+ );
+}
+
sub show_lines {
-state $nr = $opt{hidemin} ? $opt{hidemin} - 1 : 0;
-@lines and @lines > $nr or return;
+state $nr = $opt{hidemin};
@lines or return;
-@lines > $nr or return unless $opt{hidemin};
+@lines > $nr or return;
@order = sort { $b <=> $a } @order unless tied @order;
my $maxval = $opt{maxval} // (
}
}
-@lines > $nr or return if $opt{hidemin};
-
-sub sival {
- my $unit = int(log(abs $_[0] || 1) / log(10) - 3*($_[0] < 1) + 1e-15);
- my $float = $_[0] !~ /^0*[-0-9]{1,3}$/;
- sprintf('%3.*f%1s',
- $float && ($unit % 3) == ($unit < 0), # tenths
- $_[0] / 1000 ** int($unit/3), # number
- $#{$opt{units}} * 1.5 < abs $unit ? "e$unit" : $opt{units}->[$unit/3]
- );
-}
-
say(
color(31), sprintf('%*s', $lenval, $minval),
color(90), '-', color(36), '+',
}
say $opt{palette} ? color(0) : '' if $opt{spark};
+ return $nr;
}
sub show_stat {
if ($opt{hidemin} or $opt{hidemax}) {
- $opt{hidemin} ||= 1;
- $opt{hidemax} ||= @lines;
- printf '%s of ', sum(grep {length} @values[$opt{hidemin} - 1 .. $opt{hidemax} - 1]) // 0;
+ printf '%s of ', sum(grep { length }
+ @values[$opt{hidemin} .. ($opt{hidemax} || @lines) - 1]
+ ) // 0;
}
if (@order) {
my $total = sum @order;
);
}
say '';
+ return 1;
}
sub show_exit {
Override colors of parsed numbers.
Can be any CSI escape, such as I<90> for default dark grey,
-or alternatively I<1;30> for bold black.
+or alternatively I<1;30> for bright black.
In case of additional colors,
the last is used for values equal to the maximum, the first for minima.
If unspecified, these are green and red respectively (I<31 90 32>).
+Multiple intermediate colors will be distributed
+relative to the size of values.
+
+Predefined color schemes are named I<whites> and I<fire>,
+or I<greys> and I<fire256> for 256-color variants.
=item --spark[=<characters>]
echo 'SELECT count(*),schemaname FROM pg_tables GROUP BY 2' |
psql -t | barcat -u
+In PostgreSQL from within the client:
+
+ postgres=> SELECT sin(generate_series(0, 3, .1)) \g |barcat
+
Earthquakes worldwide magnitude 1+ in the last 24 hours:
https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/1.0_day.csv |
- column -tns, | graph -f4 -u -l80%
+ column -tns, | barcat -f4 -u -l80%
External datasets, like movies per year:
( git log --pretty=%ci --since=30day | cut -b-10
seq 0 30 | xargs -i date +%F -d-{}day ) |
- sort | uniq -c | awk '$1--' | graph --spark
+ sort | uniq -c | awk '$1--' | barcat --spark
=head1 AUTHOR