pax_global_header 0000666 0000000 0000000 00000000064 13070377203 0014514 g ustar 00root root 0000000 0000000 52 comment=e8d7538b8742b27cffe28e9dfe13d1d1a12288e3
prettyping-1.0.1/ 0000775 0000000 0000000 00000000000 13070377203 0013720 5 ustar 00root root 0000000 0000000 prettyping-1.0.1/LICENSE 0000664 0000000 0000000 00000002105 13070377203 0014723 0 ustar 00root root 0000000 0000000 The MIT License (MIT)
Copyright (c) 2015 Denilson Figueiredo de Sá
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
prettyping-1.0.1/README.md 0000664 0000000 0000000 00000005143 13070377203 0015202 0 ustar 00root root 0000000 0000000 prettyping
==========
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=denilsonsa%40gmail%2ecom&lc=US&item_name=Denilson&item_number=prettyping¤cy_code=BRL) [](https://flattr.com/submit/auto?user_id=denilsonsa&url=https%3A%2F%2Fgithub.com%2Fdenilsonsa%2Fprettyping&title=prettyping&description=prettyping+is+a+wrapper+around+the+standard+ping+tool+with+the+objective+of+making+the+output+prettier,+more+colorful,+more+compact,+and+easier+to+read.&tags=github&category=software)
`prettyping` is a wrapper around the standard `ping` tool with the objective of
making the output prettier, more colorful, more compact, and easier to read.
`prettyping` runs the standard `ping` in the background and parses its output,
showing the ping responses in a *graphical* way at the terminal (by using
colors and Unicode characters).
`prettyping` is written in `bash` and `awk`, and is reported to work on many
different systems (Linux, Mac OS X, BSD…), as well as running on different
versions of `awk` (`gawk`, `mawk`, `nawk`, `busybox awk`).
Read about the history of this project, as well as detailed information,
screenshots, videos at:
Requirements
------------
* `bash` (tested on 4.20, should work on versions as old as 2008)
* `awk` (either `gawk`, `mawk`, `nawk` or `busybox awk`; should work on `gawk`
versions as old as 2008; should probably work on any other awk
implementation)
* `ping` (from `iputils`, or any other version that prints essentially the same
output, like Mac OS X ping or [oping][])
* Optional dependency on `stty` or `tput` to auto-detect the terminal size.
Installation
------------
1. Download [prettyping][] script and save it anywhere.
2. Make it executable: `chmod +x prettyping`
That's all! No root permission is required. You can save and run it from any
directory. As long as your user can run `ping`, `bash` and `awk`, then
`prettyping` will work.
Alternatively, you can download the latest tarball from GitHub: [](https://github.com/denilsonsa/prettyping/releases/latest)
For people building a `prettyping` package (for any Linux distro or for Mac OS
X), just install the `prettyping` script into `/usr/bin/`, or whatever
directory is appropriate. No other file is necessary.
[oping]: http://verplant.org/liboping/
[prettyping]: https://raw.githubusercontent.com/denilsonsa/prettyping/master/prettyping
prettyping-1.0.1/mockping.sh 0000775 0000000 0000000 00000004554 13070377203 0016076 0 ustar 00root root 0000000 0000000 #!/bin/bash
#
# This is just a mock ping program that reproduces the same output all the
# time. It is used for testing/developing prettyping.
sample_output() {
cat << EOF
PING registro.br (200.160.2.3) 56(84) bytes of data.
Request timeout for icmp_seq 1
64 bytes from registro.br (200.160.2.3): icmp_seq=2 ttl=56 time=25.5 ms
64 bytes from registro.br (200.160.2.3): icmp_seq=3 ttl=56 time=55.7 ms
64 bytes from registro.br (200.160.2.3): icmp_seq=4 ttl=56 time=75.2 ms
ping: sendto: Network is down
ping: sendto: No route to host
ping: sendto: No route to host
ping: sendto: No route to host
ping: sendto: No route to host
ping: sendto: No route to host
Request timeout for icmp_seq 5
Request timeout for icmp_seq 6
Request timeout for icmp_seq 7
64 bytes from registro.br (200.160.2.3): icmp_seq=8 ttl=56 time=123 ms
64 bytes from registro.br (200.160.2.3): icmp_seq=9 ttl=56 time=149 ms
64 bytes from registro.br (200.160.2.3): icmp_seq=10 ttl=56 time=183 ms
Request timeout for icmp_seq 11
Request timeout for icmp_seq 12
Request timeout for icmp_seq 13
64 bytes from registro.br (200.160.2.3): icmp_seq=14 ttl=56 time=123 ms
64 bytes from registro.br (200.160.2.3): icmp_seq=15 ttl=56 time=149 ms
64 bytes from registro.br (200.160.2.3): icmp_seq=16 ttl=56 time=183 ms
64 bytes from registro.br (200.160.2.3): icmp_seq=19 ttl=56 time=183 ms
64 bytes from registro.br (200.160.2.3): icmp_seq=20 ttl=56 time=183 ms
64 bytes from registro.br (200.160.2.3): icmp_seq=21 ttl=56 time=183 ms
From 10.1.1.160 icmp_seq=22 Destination Host Unreachable
From 10.1.1.160 icmp_seq=23 Destination Host Unreachable
From 10.1.1.160 icmp_seq=24 Destination Host Unreachable
From 10.1.1.160 icmp_seq=25 Destination Host Unreachable
From 10.1.1.160 icmp_seq=26 Destination Host Unreachable
From 10.1.1.160 icmp_seq=27 Destination Host Unreachable
From 10.1.1.160 icmp_seq=28 Destination Host Unreachable
64 bytes from registro.br (200.160.2.3): icmp_seq=29 ttl=56 time=183 ms
64 bytes from registro.br (200.160.2.3): icmp_seq=30 ttl=56 time=183 ms
64 bytes from registro.br (200.160.2.3): icmp_seq=31 ttl=56 time=183 ms
64 bytes from registro.br (200.160.2.3): icmp_seq=32 ttl=56 time=183 ms
--- registro.br ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 36.750/38.535/40.048/1.360 ms
EOF
}
sample_output | while read line; do
echo -E "$line"
sleep 0.25s
done
prettyping-1.0.1/prettyping 0000775 0000000 0000000 00000055544 13070377203 0016070 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
#
# Written by Denilson Figueiredo de Sá
# MIT license
#
# Requirements:
# * bash (tested on 4.20, should work on older versions too)
# * awk (works with GNU awk, nawk, busybox awk, mawk)
# * ping (from iputils)
#
# More information:
# http://denilsonsa.github.io/prettyping/
# https://github.com/denilsonsa/prettyping
# http://www.reddit.com/r/linux/comments/1op98a/prettypingsh_a_better_ui_for_watching_ping/
# Third-party demonstration video: https://www.youtube.com/watch?v=ziEMY1BcikM
# TODO: Adjust how many items in the legend are printed based on the terminal width.
#
# TODO: Detect the following kinds of message and avoid printing it repeatedly.
# From 192.168.1.11: icmp_seq=4 Destination Host Unreachable
# Request timeout for icmp_seq 378
#
# TODO: Handle when a single message is spread over multiple lines. Specially,
# like in this case: https://bitbucket.org/denilsonsa/small_scripts/issue/5
#
# TODO: Print the destination (also) at the bottom bar. Useful after leaving
# the script running for quite some time.
#
# TODO: Print the destination as escape codes to xterm title.
#
# TODO: Print the current time in the beginning of each line.
#
# TODO: Implement audible ping.
#
# TODO: Autodetect the width of printf numbers, so they will always line up correctly.
#
# TODO: Test the behavior of this script upon receiving out-of-order packets, like these:
# http://www.blug.linux.no/rfc1149/pinglogg.txt
#
# TODO? How will prettyping behave if it receives a duplicate response?
print_help() {
cat << EOF
Usage: $MYNAME [prettyping parameters]
This script is a wrapper around the system's "ping" tool. It will substitute
each ping response line by a colored character, giving a very compact overview
of the ping responses.
prettyping parameters:
--[no]color Enable/disable color output. (default: enabled)
--[no]multicolor Enable/disable multi-color unicode output. Has no effect if
either color or unicode is disabled. (default: enabled)
--[no]unicode Enable/disable unicode characters. (default: enabled)
--[no]legend Enable/disable the latency legend. (default: enabled)
--[no]terminal Force the output designed to a terminal. (default: auto)
--last Use the last "n" pings at the statistics line. (default: 60)
--columns Override auto-detection of terminal dimensions.
--lines Override auto-detection of terminal dimensions.
--rttmin Minimum RTT represented in the unicode graph. (default: auto)
--rttmax Maximum RTT represented in the unicode graph. (default: auto)
--awkbin Override the awk interpreter. (default: awk)
--pingbin Override the ping tool. (default: ping)
-6 Shortcut for: --pingbin ping6
ping parameters handled by prettyping:
-a Audible ping is not implemented yet.
-f Flood mode is not allowed in prettyping.
-q Quiet output is not allowed in prettyping.
-R Record route mode is not allowed in prettyping.
-v Verbose output seems to be the default mode in ping.
All other parameters are passed directly to ping.
EOF
}
# Thanks to people at #bash who pointed me at
# http://bash-hackers.org/wiki/doku.php/scripting/posparams
parse_arguments() {
USE_COLOR=1
USE_MULTICOLOR=1
USE_UNICODE=1
USE_LEGEND=1
if [ -t 1 ]; then
IS_TERMINAL=1
else
IS_TERMINAL=0
fi
LAST_N=60
OVERRIDE_COLUMNS=0
OVERRIDE_LINES=0
RTT_MIN=auto
RTT_MAX=auto
PING_BIN="ping"
#PING_BIN="./mockping.awk"
PING_PARAMS=( )
AWK_BIN="awk"
AWK_PARAMS=( )
while [[ $# != 0 ]] ; do
case "$1" in
-h | -help | --help )
print_help
exit
;;
# Forbidden ping parameters within prettyping:
-f )
echo "${MYNAME}: You can't use the -f (flood) option."
exit 1
;;
-R )
# -R prints extra information at each ping response.
echo "${MYNAME}: You can't use the -R (record route) option."
exit 1
;;
-q )
echo "${MYNAME}: You can't use the -q (quiet) option."
exit 1
;;
-v )
# -v enables verbose output. However, it seems the output with
# or without this option is the same. Anyway, prettyping will
# strip this parameter.
;;
# Note:
# Small values for -s parameter prevents ping from being able to
# calculate RTT.
# New parameters:
-a )
# TODO: Implement audible ping for responses or for missing packets
;;
-color | --color ) USE_COLOR=1 ;;
-nocolor | --nocolor ) USE_COLOR=0 ;;
-multicolor | --multicolor ) USE_MULTICOLOR=1 ;;
-nomulticolor | --nomulticolor ) USE_MULTICOLOR=0 ;;
-unicode | --unicode ) USE_UNICODE=1 ;;
-nounicode | --nounicode ) USE_UNICODE=0 ;;
-legend | --legend ) USE_LEGEND=1 ;;
-nolegend | --nolegend ) USE_LEGEND=0 ;;
-terminal | --terminal ) IS_TERMINAL=1 ;;
-noterminal | --noterminal ) IS_TERMINAL=0 ;;
-awkbin | --awkbin ) AWK_BIN="$2" ; shift ;;
-pingbin | --pingbin ) PING_BIN="$2" ; shift ;;
-6 ) PING_BIN="ping6" ;;
#TODO: Check if these parameters are numbers.
-last | --last ) LAST_N="$2" ; shift ;;
-columns | --columns ) OVERRIDE_COLUMNS="$2" ; shift ;;
-lines | --lines ) OVERRIDE_LINES="$2" ; shift ;;
-rttmin | --rttmin ) RTT_MIN="$2" ; shift ;;
-rttmax | --rttmax ) RTT_MAX="$2" ; shift ;;
* )
PING_PARAMS+=("$1")
;;
esac
shift
done
if [[ "${RTT_MIN}" -gt 0 && "${RTT_MAX}" -gt 0 && "${RTT_MIN}" -ge "${RTT_MAX}" ]] ; then
echo "${MYNAME}: Invalid --rttmin and -rttmax values."
exit 1
fi
if [[ "${#PING_PARAMS[@]}" = 0 ]] ; then
echo "${MYNAME}: Missing parameters, use --help for instructions."
exit 1
fi
# Workaround for mawk:
# http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=593504
local version="$(echo | "${AWK_BIN}" -W version 2>&1)"
if [[ "${version}" == mawk* ]] ; then
AWK_PARAMS+=(-W interactive)
fi
}
MYNAME=`basename "$0"`
parse_arguments "$@"
export LC_ALL=C
# Warning! Ugly code ahead!
# The code is so ugly that the comments explaining it are
# bigger than the code itself!
#
# Suppose this:
#
# cmd_a | cmd_b &
#
# I need the PID of cmd_a. How can I get it?
# In bash, $! will give me the PID of cmd_b.
#
# So, I came up with this ugly solution: open a subshell, like this:
#
# (
# cmd_a &
# echo "This is the PID I want $!"
# wait
# ) | cmd_b
# Ignore Ctrl+C here.
# If I don't do this, this shell script is killed before
# ping and gawk can finish their work.
trap '' 2
# Now the ugly code.
(
"${PING_BIN}" "${PING_PARAMS[@]}" &
PING_PID="$!"
# Commented out, because it looks like this line is not needed
#trap "kill -2 $PING_PID ; exit 1" 2 # Catch Ctrl+C here
wait
) 2>&1 | (
if [ "${IS_TERMINAL}" = 1 ]; then
# Print a message to notify the awk script about terminal size change.
trap "echo SIGWINCH" 28
fi
# The trap must be in another subshell because otherwise it will interrupt
# the "wait" commmand.
while read line; do
echo -E "$line"
done
) 2>&1 | "${AWK_BIN}" "${AWK_PARAMS[@]}" '
# Weird that awk does not come with abs(), so I need to implement it.
function abs(x) {
return ( (x < 0) ? -x : x )
}
# Ditto for ceiling function.
function ceil(x) {
return (x == int(x)) ? x : int(x) + 1
}
# Local variables MUST be declared in argument list, else they are
# seen as global. Ugly, but that is how awk works.
function get_terminal_size(SIZE, SIZEA, HAS_DETECTED, CMD) {
HAS_DETECTED = 0
CMD = "stty -f /dev/tty size 2> /dev/null"
if ( (CMD | getline SIZE) == 1 ) {
split(SIZE, SIZEA, " ")
LINES = int(SIZEA[1])
COLUMNS = int(SIZEA[2])
HAS_DETECTED = 1
}
close(CMD)
if ( HAS_DETECTED == 0 ) {
CMD = "tput lines"
if ( (CMD | getline SIZE) == 1 ) {
LINES = int(SIZE)
HAS_DETECTED = 1
}
close(CMD)
CMD = "tput cols"
if ( (CMD | getline SIZE) == 1 ) {
COLUMNS = int(SIZE)
HAS_DETECTED = 1
}
close(CMD)
}
if ( int('"${OVERRIDE_COLUMNS}"') ) { COLUMNS = int('"${OVERRIDE_COLUMNS}"') }
if ( int('"${OVERRIDE_LINES}"') ) { LINES = int('"${OVERRIDE_LINES}"') }
}
############################################################
# Functions related to cursor handling
# Function called whenever a non-dotted line is printed.
#
# It will move the cursor to the line next to the statistics and
# restore the default color.
function other_line_is_printed() {
if ( IS_PRINTING_DOTS ) {
if ( '"${IS_TERMINAL}"' ) {
printf( ESC_DEFAULT ESC_NEXTLINE ESC_NEXTLINE "\n" )
} else {
printf( ESC_DEFAULT "\n" )
print_statistics_bar()
}
}
IS_PRINTING_DOTS = 0
CURR_COL = 0
}
# Function called whenever a non-dotted line is repeated.
function other_line_is_repeated() {
if ( other_line_times < 2 ) {
return
}
if ( '"${IS_TERMINAL}"' ) {
printf( ESC_DEFAULT ESC_ERASELINE "\r" )
}
printf( "Last message repeated %d times.", other_line_times )
if ( ! '"${IS_TERMINAL}"' ) {
printf( "\n" )
}
}
# Function called whenever the repeating line has changed.
function other_line_finished_repeating() {
if ( other_line_times >= 2 ) {
if ( '"${IS_TERMINAL}"' ) {
printf( "\n" )
} else {
other_line_is_repeated()
}
}
other_line = ""
other_line_times = 0
}
# Prints the newlines required for the live statistics.
#
# I need to print some newlines and then return the cursor back to its position
# to make sure the terminal will scroll.
#
# If the output is not a terminal, break lines on every LAST_N dots.
function print_newlines_if_needed() {
if ( '"${IS_TERMINAL}"' ) {
# COLUMNS-1 because I want to avoid bugs with the cursor at the last column
if ( CURR_COL >= COLUMNS-1 ) {
CURR_COL = 0
}
if ( CURR_COL == 0 ) {
if ( IS_PRINTING_DOTS ) {
printf( "\n" )
}
#printf( "\n" "\n" ESC_PREVLINE ESC_PREVLINE ESC_ERASELINE )
printf( ESC_DEFAULT "\n" "\n" ESC_CURSORUP ESC_CURSORUP ESC_ERASELINE )
}
} else {
if ( CURR_COL >= LAST_N ) {
CURR_COL = 0
printf( ESC_DEFAULT "\n" )
print_statistics_bar()
}
}
IS_PRINTING_DOTS = 1
}
############################################################
# Functions related to the data structure of "Last N" statistics.
# Clears the data structure.
function clear(d) {
d["index"] = 0 # The next position to store a value
d["size"] = 0 # The array size, goes up to LAST_N
}
# This function stores the value to the passed data structure.
# The data structure holds at most LAST_N values. When it is full,
# a new value overwrite the oldest one.
function store(d, value) {
d[d["index"]] = value
d["index"]++
if ( d["index"] >= d["size"] ) {
if ( d["size"] < LAST_N ) {
d["size"]++
} else {
d["index"] = 0
}
}
}
############################################################
# Functions related to processing the received response
function process_rtt(rtt) {
# Overall statistics
last_rtt = rtt
total_rtt += rtt
if ( last_seq == 0 ) {
min_rtt = max_rtt = rtt
} else {
if ( rtt < min_rtt ) min_rtt = rtt
if ( rtt > max_rtt ) max_rtt = rtt
}
# "Last N" statistics
store(lastn_rtt,rtt)
}
function lost_a_packet() {
print_newlines_if_needed()
print_missing_response()
last_seq++
lost++
store(lastn_lost, 1)
}
############################################################
# Functions related to printing the fancy ping response
# block_index, n, w are just local variables.
function print_response_legend(i, n, w) {
if ( ! '"${USE_LEGEND}"' ) {
return
}
if ( BLOCK_LEN > 1 ) {
# w counts the cursor position in the current line. Because of the
# escape codes, I need to jump through some hoops in order to count the
# position correctly.
w = 0
n = "0 "
w += length(n) + 1
printf( n BLOCK[0] ESC_DEFAULT )
for ( i=1 ; i= COLUMNS ) {
printf( "\n" )
w = length(n) + 1
} else {
printf( " " )
w += 1
}
printf( n BLOCK[i] ESC_DEFAULT )
}
printf( " ∞\n" )
}
# Useful code for debugging.
#for ( i=0 ; i<=BLOCK_RTT_MAX ; i++ ) {
# print_received_response(i)
# printf( ESC_DEFAULT "%4d\n", i )
#}
}
# block_index is just a local variable.
function print_received_response(rtt, block_index) {
if ( rtt < BLOCK_RTT_MIN ) {
block_index = 0
} else if ( rtt >= BLOCK_RTT_MAX ) {
block_index = BLOCK_LEN - 1
} else {
block_index = 1 + int((rtt - BLOCK_RTT_MIN) * (BLOCK_LEN - 2) / BLOCK_RTT_RANGE)
}
printf( BLOCK[block_index] )
CURR_COL++
}
function print_missing_response(rtt) {
printf( ESC_RED "!" )
CURR_COL++
}
############################################################
# Functions related to printing statistics
# All arguments are just local variables.
function print_overall(percentage_lost, avg_rtt) {
# Handling division by zero.
# Note that mawk does not consider division by zero an error, while all
# other awk implementations abort in such case.
# https://stackoverflow.com/questions/14581966/why-does-awk-produce-different-results-for-division-by-zero
avg_rtt = ( received > 0 ) ? (total_rtt/received) : 0
percentage_lost = ( lost+received > 0 ) ? (lost*100/(lost+received)) : 0
if ( '"${IS_TERMINAL}"' ) {
printf( "%2d/%3d (%2d%%) lost; %4.0f/" ESC_BOLD "%4.0f" ESC_DEFAULT "/%4.0fms; last: " ESC_BOLD "%4.0f" ESC_DEFAULT "ms",
lost,
lost+received,
percentage_lost,
min_rtt,
avg_rtt,
max_rtt,
last_rtt )
} else {
printf( "%2d/%3d (%2d%%) lost; %4.0f/" ESC_BOLD "%4.0f" ESC_DEFAULT "/%4.0fms",
lost,
lost+received,
percentage_lost,
min_rtt,
avg_rtt,
max_rtt )
}
}
# All arguments are just local variables.
function print_last_n(i, percentage_lost, sum, min, avg, max, diffs) {
# Calculate and print the lost packets statistics
sum = 0
for ( i=0 ; i 0) ? (sum*100/lastn_lost["size"]) : 0
printf( "%2d/%3d (%2d%%) lost; ",
sum,
lastn_lost["size"],
percentage_lost )
# Calculate the min/avg/max rtt times
sum = diffs = 0
min = max = lastn_rtt[0]
for ( i=0 ; i max ) max = lastn_rtt[i]
}
avg = (lastn_rtt["size"]) ? (sum/lastn_rtt["size"]) : 0
# Calculate mdev (mean absolute deviation)
for ( i=0 ; i 0 ) {
diffs /= lastn_rtt["size"]
}
# Print the rtt statistics
printf( "%4.0f/" ESC_BOLD "%4.0f" ESC_DEFAULT "/%4.0f/%4.0fms (last %d)",
min,
avg,
max,
diffs,
lastn_rtt["size"] )
}
function print_statistics_bar() {
if ( '"${IS_TERMINAL}"' ) {
printf( ESC_SAVEPOS ESC_DEFAULT )
printf( ESC_NEXTLINE ESC_ERASELINE )
print_overall()
printf( ESC_NEXTLINE ESC_ERASELINE )
print_last_n()
printf( ESC_UNSAVEPOS )
} else {
print_overall()
printf( "\n" )
print_last_n()
printf( "\n" )
}
}
function print_statistics_bar_if_terminal() {
if ( '"${IS_TERMINAL}"' ) {
print_statistics_bar()
}
}
############################################################
# Initializations
BEGIN {
# Easy way to get each value from ping output
FS = "="
############################################################
# General internal variables
# This is needed to keep track of lost packets
last_seq = 0
# The previously printed non-ping-response line
other_line = ""
other_line_times = 0
# Variables to keep the screen clean
IS_PRINTING_DOTS = 0
CURR_COL = 0
############################################################
# Variables related to "overall" statistics
received = 0
lost = 0
total_rtt = 0
min_rtt = 0
max_rtt = 0
last_rtt = 0
############################################################
# Variables related to "last N" statistics
LAST_N = int('"${LAST_N}"')
# Data structures for the "last N" statistics
clear(lastn_lost)
clear(lastn_rtt)
############################################################
# Terminal height and width
# These are sane defaults, in case we cannot query the actual terminal size
LINES = 24
COLUMNS = 80
# Auto-detecting the terminal size
get_terminal_size()
if ( '"${IS_TERMINAL}"' && COLUMNS <= 50 ) {
print "Warning: terminal width is too small."
}
############################################################
# ANSI escape codes
# Color escape codes.
# Fortunately, awk defaults any unassigned variable to an empty string.
if ( '"${USE_COLOR}"' ) {
ESC_DEFAULT = "\033[0m"
ESC_BOLD = "\033[1m"
#ESC_BLACK = "\033[0;30m"
#ESC_GRAY = "\033[1;30m"
ESC_RED = "\033[0;31m"
ESC_GREEN = "\033[0;32m"
ESC_YELLOW = "\033[0;33m"
ESC_BLUE = "\033[0;34m"
ESC_MAGENTA = "\033[0;35m"
ESC_CYAN = "\033[0;36m"
ESC_WHITE = "\033[0;37m"
ESC_YELLOW_ON_GREEN = "\033[42;33m"
ESC_RED_ON_YELLOW = "\033[43;31m"
}
# Other escape codes, see:
# http://en.wikipedia.org/wiki/ANSI_escape_code
# http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
ESC_NEXTLINE = "\n"
ESC_CURSORUP = "\033[A"
ESC_CURSORDOWN = "\033[B"
ESC_SCROLLUP = "\033[S"
ESC_SCROLLDOWN = "\033[T"
ESC_ERASELINEEND = "\033[0K"
ESC_ERASELINE = "\033[2K"
ESC_SAVEPOS = "\0337"
ESC_UNSAVEPOS = "\0338"
# I am avoiding these escapes as they are not listed in:
# http://vt100.net/docs/vt100-ug/chapter3.html
#ESC_PREVLINE = "\033[F"
#ESC_SAVEPOS = "\033[s"
#ESC_UNSAVEPOS = "\033[u"
# I am avoiding this to improve compatibility with (older versions of) tmux
#ESC_NEXTLINE = "\033[E"
############################################################
# Unicode characters (based on https://github.com/holman/spark )
if ( '"${USE_UNICODE}"' ) {
BLOCK[ 0] = ESC_GREEN "▁"
BLOCK[ 1] = ESC_GREEN "▂"
BLOCK[ 2] = ESC_GREEN "▃"
BLOCK[ 3] = ESC_GREEN "▄"
BLOCK[ 4] = ESC_GREEN "▅"
BLOCK[ 5] = ESC_GREEN "▆"
BLOCK[ 6] = ESC_GREEN "▇"
BLOCK[ 7] = ESC_GREEN "█"
BLOCK[ 8] = ESC_YELLOW_ON_GREEN "▁"
BLOCK[ 9] = ESC_YELLOW_ON_GREEN "▂"
BLOCK[10] = ESC_YELLOW_ON_GREEN "▃"
BLOCK[11] = ESC_YELLOW_ON_GREEN "▄"
BLOCK[12] = ESC_YELLOW_ON_GREEN "▅"
BLOCK[13] = ESC_YELLOW_ON_GREEN "▆"
BLOCK[14] = ESC_YELLOW_ON_GREEN "▇"
BLOCK[15] = ESC_YELLOW_ON_GREEN "█"
BLOCK[16] = ESC_RED_ON_YELLOW "▁"
BLOCK[17] = ESC_RED_ON_YELLOW "▂"
BLOCK[18] = ESC_RED_ON_YELLOW "▃"
BLOCK[19] = ESC_RED_ON_YELLOW "▄"
BLOCK[20] = ESC_RED_ON_YELLOW "▅"
BLOCK[21] = ESC_RED_ON_YELLOW "▆"
BLOCK[22] = ESC_RED_ON_YELLOW "▇"
BLOCK[23] = ESC_RED_ON_YELLOW "█"
if ( '"${USE_MULTICOLOR}"' && '"${USE_COLOR}"' ) {
# Multi-color version:
BLOCK_LEN = 24
BLOCK_RTT_MIN = 10
BLOCK_RTT_MAX = 230
} else {
# Simple version:
BLOCK_LEN = 8
BLOCK_RTT_MIN = 25
BLOCK_RTT_MAX = 175
}
} else {
BLOCK[ 0] = ESC_GREEN "_"
BLOCK[ 1] = ESC_GREEN "."
BLOCK[ 2] = ESC_GREEN "o"
BLOCK[ 3] = ESC_GREEN "O"
BLOCK[ 4] = ESC_YELLOW "_"
BLOCK[ 5] = ESC_YELLOW "."
BLOCK[ 6] = ESC_YELLOW "o"
BLOCK[ 7] = ESC_YELLOW "O"
BLOCK[ 8] = ESC_RED "_"
BLOCK[ 9] = ESC_RED "."
BLOCK[10] = ESC_RED "o"
BLOCK[11] = ESC_RED "O"
if ( '"${USE_MULTICOLOR}"' && '"${USE_COLOR}"' ) {
# Multi-color version:
BLOCK_LEN = 12
BLOCK_RTT_MIN = 20
BLOCK_RTT_MAX = 220
} else {
# Simple version:
BLOCK_LEN = 4
BLOCK_RTT_MIN = 75
BLOCK_RTT_MAX = 225
}
}
if ( int('"${RTT_MIN}"') > 0 && int('"${RTT_MAX}"') > 0 ) {
BLOCK_RTT_MIN = int('"${RTT_MIN}"')
BLOCK_RTT_MAX = int('"${RTT_MAX}"')
} else if ( int('"${RTT_MIN}"') > 0 ) {
BLOCK_RTT_MIN = int('"${RTT_MIN}"')
BLOCK_RTT_MAX = BLOCK_RTT_MIN * (BLOCK_LEN - 1)
} else if ( int('"${RTT_MAX}"') > 0 ) {
BLOCK_RTT_MAX = int('"${RTT_MAX}"')
BLOCK_RTT_MIN = int(BLOCK_RTT_MAX / (BLOCK_LEN - 1))
}
BLOCK_RTT_RANGE = BLOCK_RTT_MAX - BLOCK_RTT_MIN
print_response_legend()
}
############################################################
# Main loop
{
if ( $0 ~ /^[0-9]+ bytes from .*: icmp_[rs]eq=[0-9]+ ttl=[0-9]+ time=[0-9.]+ *ms/ ) {
# Sample line from ping:
# 64 bytes from 8.8.8.8: icmp_seq=1 ttl=49 time=184 ms
if ( other_line_times >= 2 ) {
other_line_finished_repeating()
}
# $1 = useless prefix string
# $2 = icmp_seq
# $3 = ttl
# $4 = time
# This must be called before incrementing the last_seq variable!
rtt = int($4)
process_rtt(rtt)
seq = int($2)
while ( last_seq < seq - 1 ) {
lost_a_packet()
}
# Received a packet
print_newlines_if_needed()
print_received_response(rtt)
# In case of receiving multiple responses with the same seq number, it
# is better to use "last_seq = seq" than to increment last_seq.
last_seq = seq
received++
store(lastn_lost, 0)
print_statistics_bar_if_terminal()
} else if ( $0 ~ /^.*onnected to.*, seq=[0-9]+ time=[0-9.]+ *ms/ ) {
# Sample line from httping:
# connected to 200.149.119.168:80 (273 bytes), seq=0 time=129.86 ms
if ( other_line_times >= 2 ) {
other_line_finished_repeating()
}
seq = $0
sub(/.* seq=/, "", seq)
seq = int(seq)
rtt = $0
sub(/.* time=/, "", rtt)
rtt = int(rtt)
process_rtt(rtt)
while ( last_seq < seq - 1 ) {
lost_a_packet()
}
# Received a packet
print_newlines_if_needed()
print_received_response(rtt)
# In case of receiving multiple responses with the same seq number, it
# is better to use "last_seq = seq" than to increment last_seq.
last_seq = seq
received++
store(lastn_lost, 0)
print_statistics_bar_if_terminal()
} else if ( $0 == "" ) {
# Do nothing on blank lines.
} else if ( $0 == "error shutting down ssl" ) {
# Common error message when using httping, ignore it.
} else if ( $0 ~ /^Request timeout for icmp_seq [0-9]+/ ) {
# Reply timeout is printed on Mac OS X.
if ( other_line_times >= 2 ) {
other_line_finished_repeating()
}
lost_a_packet()
# Making sure the last_seq number is correct.
gsub(/.* icmp_seq /, "")
seq = int($0)
last_seq = seq
print_newlines_if_needed()
print_statistics_bar_if_terminal()
} else if ( $0 ~ /^SIGWINCH$/ ) {
get_terminal_size()
if ( IS_PRINTING_DOTS ) {
if ( CURR_COL >= COLUMNS-1 ) {
# Not enough space anyway.
} else {
# Making up room in case the number of lines has changed.
printf( ESC_NEXTLINE ESC_NEXTLINE ESC_CURSORUP ESC_CURSORUP )
# Moving to the correct column and erasing the rest of the line.
printf( "\033[" (CURR_COL+1) "G" ESC_DEFAULT ESC_ERASELINEEND )
}
print_newlines_if_needed()
print_statistics_bar_if_terminal()
}
} else {
other_line_is_printed()
original_line = $0
gsub(/icmp_seq[= ][0-9]+/, "")
if ( $0 == other_line ) {
other_line_times++
if ( '"${IS_TERMINAL}"' ) {
other_line_is_repeated()
}
} else {
other_line_finished_repeating()
other_line = $0
other_line_times = 1
printf( "%s\n", original_line )
}
}
# Not needed when the output is a terminal, but does not hurt either.
fflush()
}'