From e420db389932e1a168073d3516fcd82587bba986 Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Sun, 8 Sep 2019 16:44:39 +0200 Subject: [PATCH 01/16] fix sival log for negative/zero values --- barcat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barcat b/barcat index a664d0d..4846434 100755 --- a/barcat +++ b/barcat @@ -143,7 +143,7 @@ if ($opt{markers} // 1 and $size > 0) { @lines > $nr or return if $opt{hidemin}; sub sival { - my $unit = int(log($_[0]) / log(1000) - ($_[0] < 1) + 1e-15); + my $unit = int(log(abs $_[0] || 1) / log(1000) - ($_[0] < 1) + 1e-15); my $float = sprintf '%e', $_[0] / 1000 ** $unit; #TODO: or $_[0] =~ /\./ $float -= int($float); sprintf('%*.*f%*s', -- 2.30.0 From ccbfed9910837a2be60c8b3ef0f990bcf60c0f2b Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Sun, 8 Sep 2019 17:34:44 +0200 Subject: [PATCH 02/16] shorten human-readable values to 2-3 digits Similar to ls e.a. Previously reserved 5 characters in order to align decimal points, but this is pointless with mixed magnitudes. --- barcat | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/barcat b/barcat index 4846434..22f8d31 100755 --- a/barcat +++ b/barcat @@ -143,14 +143,12 @@ if ($opt{markers} // 1 and $size > 0) { @lines > $nr or return if $opt{hidemin}; sub sival { - my $unit = int(log(abs $_[0] || 1) / log(1000) - ($_[0] < 1) + 1e-15); - my $float = sprintf '%e', $_[0] / 1000 ** $unit; #TODO: or $_[0] =~ /\./ - $float -= int($float); - sprintf('%*.*f%*s', - $float ? (5,1) : (3,0), # length and tenths - $_[0] / 1000 ** $unit, # number - $float ? 0 : 3, # unit size - $#{$opt{units}} >> 1 < abs $unit ? "e$unit" : $opt{units}->[$unit] + 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] ); } -- 2.30.0 From c4edcd34541db046c3a7b444212ed3b79ece899b Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Mon, 9 Sep 2019 18:36:00 +0200 Subject: [PATCH 03/16] color() function to prepare ansi escapes Replace code for improved descriptiveness, identical output. Minimal implementation of Term::ANSIColor::color without various unwanted features. --- barcat | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/barcat b/barcat index 22f8d31..fe04246 100755 --- a/barcat +++ b/barcat @@ -99,6 +99,11 @@ while (readline) { $SIG{INT} = 'DEFAULT'; +sub color { + $opt{color} or return ''; + return "\e[$_[0]m"; +} + sub show_lines { state $nr = $opt{hidemin} ? $opt{hidemin} - 1 : 0; @@ -124,18 +129,18 @@ if ($opt{markers} // 1 and $size > 0) { $barmark[ orderpos($#order * .68269) ] = '<'; $barmark[ orderpos($#order / 2) ] = '+'; # mean $barmark[ -$minval * $size ] = '|' if $minval < 0; # zero - defined and $opt{color} and $_ = "\e[36m$_\e[0m" for @barmark; + defined and $_ = color(36).$_.color(0) for @barmark; state $lastmax = $maxval; if ($maxval > $lastmax) { print ' ' x ($lenval + $len); - printf "\e[90m" if $opt{color}; + printf color(90); printf '%-*s', ($lastmax - $minval) * $size + .5, '-' x (($values[$nr - 1] - $minval) * $size); - print "\e[92m" if $opt{color}; + print color(92); say '+' x (($maxval - $lastmax - $minval) * $size + .5); - print "\e[0m" if $opt{color}; + print color(0); $lastmax = $maxval; } } @@ -161,7 +166,7 @@ while ($nr <= $#lines) { $val == $order[-1] ? 31 : # min 90; $val = $opt{units} ? sival($val) : sprintf "%*s", $lenval, $val; - $val = "\e[${color}m$val\e[0m" if $color; + $val = color($color).$val.color(0) if $color; } my $line = $lines[$nr] =~ s/\n/$val/r; printf '%-*s', $len + length($val), $line; -- 2.30.0 From e31503d6e3fc1c0961d26db24e7474afa005e0ba Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Mon, 9 Sep 2019 18:37:39 +0200 Subject: [PATCH 04/16] void color() shorthand to surround value Include Term::ANSIColor::colored functionality. --- barcat | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/barcat b/barcat index fe04246..723c7da 100755 --- a/barcat +++ b/barcat @@ -100,8 +100,9 @@ while (readline) { $SIG{INT} = 'DEFAULT'; sub color { - $opt{color} or return ''; - return "\e[$_[0]m"; + $opt{color} and defined $_[0] or return ''; + return "\e[$_[0]m" if defined wantarray; + $_ = color(@_) . $_ . color(0) if defined; } sub show_lines { @@ -129,7 +130,7 @@ if ($opt{markers} // 1 and $size > 0) { $barmark[ orderpos($#order * .68269) ] = '<'; $barmark[ orderpos($#order / 2) ] = '+'; # mean $barmark[ -$minval * $size ] = '|' if $minval < 0; # zero - defined and $_ = color(36).$_.color(0) for @barmark; + color(36) for @barmark; state $lastmax = $maxval; if ($maxval > $lastmax) { @@ -161,12 +162,12 @@ while ($nr <= $#lines) { $nr >= $opt{hidemax} and last if defined $opt{hidemax}; my $val = $values[$nr]; if (length $val) { - my $color = !$opt{color} ? 0 : + my $color = !$opt{color} ? undef : $val == $order[0] ? 32 : # max $val == $order[-1] ? 31 : # min 90; $val = $opt{units} ? sival($val) : sprintf "%*s", $lenval, $val; - $val = color($color).$val.color(0) if $color; + color($color) for $val; } my $line = $lines[$nr] =~ s/\n/$val/r; printf '%-*s', $len + length($val), $line; -- 2.30.0 From 315b1f4fbaf824b7d32ca4168f490adf286324ab Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Mon, 9 Sep 2019 18:51:10 +0200 Subject: [PATCH 05/16] reset SIGINT after first ignoral Ensure subsequent signals will break any unexpected hangs. --- barcat | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/barcat b/barcat index 723c7da..c1e1d07 100755 --- a/barcat +++ b/barcat @@ -77,7 +77,10 @@ if (defined $opt{interval}) { } or warn $@, "Expect slowdown with large datasets!\n"; } -$SIG{INT} = 'IGNORE'; # continue after assumed eof +$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) { -- 2.30.0 From ce4b0d2b0ea9193cf2b557bbdf6b27056390a9ba Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Mon, 9 Sep 2019 18:56:36 +0200 Subject: [PATCH 06/16] additional git example for date history Simplified version of a longtime alias of mine. --- barcat | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/barcat b/barcat index c1e1d07..2a7bdbf 100755 --- a/barcat +++ b/barcat @@ -389,6 +389,13 @@ Or the top 3 most frequent authors with statistics over all: git shortlog -sn | barcat -L3 -s +Activity of the last days: + + git log --pretty=%cd --date=format:%F --since=1month | + perl -MTime::Piece -pE + 'for ($t //= localtime; $t->ymd gt $_; $t -= 24*60*60) {say $t->ymd}' | + sort | uniq -c | awk '$1--' | graph + =head1 AUTHOR Mischa POSLAWSKY -- 2.30.0 From 1581d8acae9b0ed74bd4c5a696f6cdc470fe84d9 Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Mon, 9 Sep 2019 18:57:40 +0200 Subject: [PATCH 07/16] replace perl in example date parsing Time::Piece was released with perl v5.8.9 but may not be installed on restricted systems. Hardcoding the number of days is less flexible but "only" relies on a GNU date supporting -d. Furthermore, git only supports date formatting since version 2.6.0, so prefer an equivalent cut similar to earlier year selection. --- barcat | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/barcat b/barcat index 2a7bdbf..c2f41aa 100755 --- a/barcat +++ b/barcat @@ -391,9 +391,8 @@ Or the top 3 most frequent authors with statistics over all: Activity of the last days: - git log --pretty=%cd --date=format:%F --since=1month | - perl -MTime::Piece -pE - 'for ($t //= localtime; $t->ymd gt $_; $t -= 24*60*60) {say $t->ymd}' | + ( git log --pretty=%ci --since=30day | cut -b-10 + seq 0 30 | xargs -i date +%F -d-{}day ) | sort | uniq -c | awk '$1--' | graph =head1 AUTHOR -- 2.30.0 From 6b86aaa55f57299482705d47e972c3c70676b311 Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Mon, 9 Sep 2019 19:32:13 +0200 Subject: [PATCH 08/16] date alternative on BSD variants including OSX --- barcat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barcat b/barcat index c2f41aa..1c567f4 100755 --- a/barcat +++ b/barcat @@ -389,7 +389,7 @@ Or the top 3 most frequent authors with statistics over all: git shortlog -sn | barcat -L3 -s -Activity of the last days: +Activity of the last days (substitute date C<-v-{}d> on BSD): ( git log --pretty=%ci --since=30day | cut -b-10 seq 0 30 | xargs -i date +%F -d-{}day ) | -- 2.30.0 From 0efc8a341754dc7b8629008d0e7dbcba46d31af5 Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Mon, 9 Sep 2019 19:19:57 +0200 Subject: [PATCH 09/16] earthquake feed example As seen on , but better suited to demonstrate table markup. --- barcat | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/barcat b/barcat index 1c567f4..5863c4c 100755 --- a/barcat +++ b/barcat @@ -355,6 +355,11 @@ Any kind of database query with counts, preserving returned alignment: echo 'SELECT count(*),schemaname FROM pg_tables GROUP BY 2' | psql -t | barcat -u +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% + External datasets, like movies per year: curl https://github.com/prust/wikipedia-movie-data/raw/master/movies.json | -- 2.30.0 From f4423db9671d9650e7a39482ffc935d373e72ef5 Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Mon, 9 Sep 2019 19:17:38 +0200 Subject: [PATCH 10/16] spark option to replace lines by single characters Implement "sparklines" (one-line graph) by simply substituting Unicode block elements U+2581-2588 relative to values. Inspired by Zach Holman's `spark` . --- barcat | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/barcat b/barcat index 5863c4c..82cf7d6 100755 --- a/barcat +++ b/barcat @@ -47,6 +47,9 @@ GetOptions(\%opt, ); }, 'markers|m=s', + 'spark!' => sub { + $opt{spark} = [split //, '⎽▁▂▃▄▅▆▇█']; + }, 'stat|s!', 'unmodified|u!', 'width|w=i', @@ -164,6 +167,12 @@ sub sival { while ($nr <= $#lines) { $nr >= $opt{hidemax} and last if defined $opt{hidemax}; my $val = $values[$nr]; + + if ($opt{spark}) { + print $opt{spark}->[ ($val - $minval) / $maxval * $#{$opt{spark}} ]; + next; + } + if (length $val) { my $color = !$opt{color} ? undef : $val == $order[0] ? 32 : # max @@ -176,9 +185,11 @@ while ($nr <= $#lines) { printf '%-*s', $len + length($val), $line; print $barmark[$_] // '-' for 1 .. $size && (($values[$nr] || 0) - $minval) * $size + .5; say ''; - +} +continue { $nr++; } +say '' if $opt{spark}; } show_lines(); @@ -398,7 +409,7 @@ Activity of the last days (substitute date C<-v-{}d> on BSD): ( git log --pretty=%ci --since=30day | cut -b-10 seq 0 30 | xargs -i date +%F -d-{}day ) | - sort | uniq -c | awk '$1--' | graph + sort | uniq -c | awk '$1--' | graph --spark =head1 AUTHOR -- 2.30.0 From 11e6fa3713f7ea24f2329def3c60b32847539799 Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Mon, 9 Sep 2019 19:18:03 +0200 Subject: [PATCH 11/16] customisable spark characters Distributes over any number of unicode glyphs. Hardcode interpreter for now so -CA can be applied to easily decode arguments from UTF-8. --- barcat | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/barcat b/barcat index 82cf7d6..442559c 100755 --- a/barcat +++ b/barcat @@ -1,4 +1,4 @@ -#!/usr/bin/env perl +#!/usr/bin/perl -CA use 5.018; use warnings; use utf8; @@ -47,8 +47,8 @@ GetOptions(\%opt, ); }, 'markers|m=s', - 'spark!' => sub { - $opt{spark} = [split //, '⎽▁▂▃▄▅▆▇█']; + 'spark:s' => sub { + $opt{spark} = [split //, $_[1] || '⎽▁▂▃▄▅▆▇█']; }, 'stat|s!', 'unmodified|u!', -- 2.30.0 From 2c67b83a73065c44448ba562ddc83403e97abd8d Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Mon, 9 Sep 2019 19:19:04 +0200 Subject: [PATCH 12/16] unmodified should avoid value alignment --- barcat | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/barcat b/barcat index 442559c..a4ed12f 100755 --- a/barcat +++ b/barcat @@ -63,6 +63,7 @@ $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}; my (@lines, @values, @order); @@ -93,6 +94,7 @@ while (readline) { push @order, $1 if length $1; if (defined $opt{trim} and defined $1) { my $trimpos = abs $opt{trim}; + $trimpos -= length $1 if $opt{unmodified}; if ($trimpos <= 1) { $_ = substr $_, 0, 1; } @@ -315,7 +317,7 @@ Total statistics after all data. =item -u, --unmodified -Do not strip leading whitespace. +Do not reformat values, keeping leading whitespace. Keep original value alignment, which may be significant in some programs. =item --value-length= -- 2.30.0 From cd9fc725abd4876848c20a78435a1456f707546e Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Mon, 9 Sep 2019 21:37:43 +0200 Subject: [PATCH 13/16] initial regression test suite Simple script to run barcat with different input from *.in, comparing to expected *.out files and returning results in TAP format. Execution at t/*.t for prove compatibility. --- t/regress.t | 18 ++++++++++++++++++ t/t1001-seq.in | 3 +++ t/t1001-seq.out | 3 +++ t/t1002-sinewave.in | 30 ++++++++++++++++++++++++++++++ t/t1002-sinewave.out | 30 ++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+) create mode 100755 t/regress.t create mode 100644 t/t1001-seq.in create mode 100644 t/t1001-seq.out create mode 100644 t/t1002-sinewave.in create mode 100644 t/t1002-sinewave.out diff --git a/t/regress.t b/t/regress.t new file mode 100755 index 0000000..ef28f6f --- /dev/null +++ b/t/regress.t @@ -0,0 +1,18 @@ +#!/bin/sh + +cd "${0%/*}" || exit 1 + +test_count=0 + +COLUMNS=40 +diffcmd='diff --unchanged-line-format= --old-line-format=<%L --new-line-format=>%L' + +for candidate in ${@:-t*.in} +do + test_count=$((test_count+1)) + name="${candidate%.out}" + barcat <"$name.in" | $diffcmd "$name.out" - || printf 'not ' + echo "ok $test_count - $name" +done + +echo "1..$test_count" diff --git a/t/t1001-seq.in b/t/t1001-seq.in new file mode 100644 index 0000000..01e79c3 --- /dev/null +++ b/t/t1001-seq.in @@ -0,0 +1,3 @@ +1 +2 +3 diff --git a/t/t1001-seq.out b/t/t1001-seq.out new file mode 100644 index 0000000..6947d1e --- /dev/null +++ b/t/t1001-seq.out @@ -0,0 +1,3 @@ +1 ------------- +2 ------------------------+ +3 ------------------------+----->------- diff --git a/t/t1002-sinewave.in b/t/t1002-sinewave.in new file mode 100644 index 0000000..fb3ff0e --- /dev/null +++ b/t/t1002-sinewave.in @@ -0,0 +1,30 @@ +0.0998334 +0.198669 +0.29552 +0.389418 +0.479426 +0.564642 +0.644218 +0.717356 +0.783327 +0.841471 +0.891207 +0.932039 +0.963558 +0.98545 +0.997495 +0.999574 +0.991665 +0.973848 +0.9463 +0.909297 +0.863209 +0.808496 +0.745705 +0.675463 +0.598472 +0.515501 +0.42738 +0.334988 +0.239249 +0.14112 diff --git a/t/t1002-sinewave.out b/t/t1002-sinewave.out new file mode 100644 index 0000000..ec8ff3f --- /dev/null +++ b/t/t1002-sinewave.out @@ -0,0 +1,30 @@ +0.0998334 --- + 0.198669 ------ + 0.29552 --------- + 0.389418 ------------ + 0.479426 -------------- + 0.564642 ---------------<- + 0.644218 ---------------<--= + 0.717356 ---------------<--=-+- + 0.783327 ---------------<--=-+--- + 0.841471 ---------------<--=-+---- + 0.891207 ---------------<--=-+---->- + 0.932039 ---------------<--=-+---->-- + 0.963558 ---------------<--=-+---->--- + 0.98545 ---------------<--=-+---->---- + 0.997495 ---------------<--=-+---->---- + 0.999574 ---------------<--=-+---->---- + 0.991665 ---------------<--=-+---->---- + 0.973848 ---------------<--=-+---->--- + 0.9463 ---------------<--=-+---->-- + 0.909297 ---------------<--=-+---->- + 0.863209 ---------------<--=-+----> + 0.808496 ---------------<--=-+--- + 0.745705 ---------------<--=-+- + 0.675463 ---------------<--=- + 0.598472 ---------------<-- + 0.515501 --------------- + 0.42738 ------------- + 0.334988 ---------- + 0.239249 ------- + 0.14112 ---- -- 2.30.0 From d57c46ddb0a70401d43b358da34af276303bb135 Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Mon, 9 Sep 2019 22:43:34 +0200 Subject: [PATCH 14/16] spaces and options in test file names Replace underscores by spaces to facilitate human-readable titles in sane file names, and apply parts following a space and dash ( -*) as command parameters to allow testing of different options. --- t/regress.t | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/t/regress.t b/t/regress.t index ef28f6f..5daf496 100755 --- a/t/regress.t +++ b/t/regress.t @@ -10,8 +10,14 @@ diffcmd='diff --unchanged-line-format= --old-line-format=<%L --new-line-format=> for candidate in ${@:-t*.in} do test_count=$((test_count+1)) - name="${candidate%.out}" - barcat <"$name.in" | $diffcmd "$name.out" - || printf 'not ' + file="${candidate%.in}" + test -r "$file.in" || continue + + name="$(echo ${file#*-} | tr _ \ )" + cmd="barcat $file.in" + case "$name" in *\ -*) cmd="$cmd -${name#* -}";; esac + + $cmd 2>&1 | $diffcmd "$file.out" - || printf 'not ' echo "ok $test_count - $name" done -- 2.30.0 From aad4d54fa81ee0d4f02e868f82ff4296e225791b Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Mon, 9 Sep 2019 23:00:12 +0200 Subject: [PATCH 15/16] parse options to regress.t Reserve any parameters starting with a dash. Show usage for unknown commands ie everything. --- t/regress.t | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/t/regress.t b/t/regress.t index 5daf496..099b944 100755 --- a/t/regress.t +++ b/t/regress.t @@ -7,6 +7,13 @@ test_count=0 COLUMNS=40 diffcmd='diff --unchanged-line-format= --old-line-format=<%L --new-line-format=>%L' +for option in "$@" +do + case "$option" in + -*) echo "Usage: $0 [...]"; exit 64;; + esac +done + for candidate in ${@:-t*.in} do test_count=$((test_count+1)) -- 2.30.0 From 9aa6d010ebaf2d3ad8e6cad435a54748fb2572e2 Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Mon, 9 Sep 2019 22:54:20 +0200 Subject: [PATCH 16/16] regenerate test output on regress.t -G option --- t/regress.t | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/t/regress.t b/t/regress.t index 099b944..1463ce3 100755 --- a/t/regress.t +++ b/t/regress.t @@ -6,11 +6,13 @@ test_count=0 COLUMNS=40 diffcmd='diff --unchanged-line-format= --old-line-format=<%L --new-line-format=>%L' +regenerate= for option in "$@" do case "$option" in - -*) echo "Usage: $0 [...]"; exit 64;; + -G) regenerate=1 && shift;; + -*) echo "Usage: $0 [-G] [...]"; exit 64;; esac done @@ -24,7 +26,19 @@ do cmd="barcat $file.in" case "$name" in *\ -*) cmd="$cmd -${name#* -}";; esac - $cmd 2>&1 | $diffcmd "$file.out" - || printf 'not ' + if test -n "$regenerate" + then + if test -e $file.out + then + echo "ok $test_count # skip existing $file.out" + continue + fi + $cmd >$file.out 2>&1 + else + $cmd 2>&1 | $diffcmd "$file.out" - + fi + + test 0 = $? || printf 'not ' echo "ok $test_count - $name" done -- 2.30.0