pax_global_header00006660000000000000000000000064130703772030014514gustar00rootroot0000000000000052 comment=e8d7538b8742b27cffe28e9dfe13d1d1a12288e3 prettyping-1.0.1/000077500000000000000000000000001307037720300137205ustar00rootroot00000000000000prettyping-1.0.1/LICENSE000066400000000000000000000021051307037720300147230ustar00rootroot00000000000000The 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.md000066400000000000000000000051431307037720300152020ustar00rootroot00000000000000prettyping ========== [![Donate using PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=denilsonsa%40gmail%2ecom&lc=US&item_name=Denilson&item_number=prettyping¤cy_code=BRL) [![Flattr this project](https://api.flattr.com/button/flattr-badge-large.png)](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: [![Latest release](https://img.shields.io/github/release/denilsonsa/prettyping.svg)](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.sh000077500000000000000000000045541307037720300160760ustar00rootroot00000000000000#!/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/prettyping000077500000000000000000000555441307037720300160700ustar00rootroot00000000000000#!/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() }'