1569 lines
		
	
	
		
			53 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1569 lines
		
	
	
		
			53 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
#! /usr/bin/env bash
 | 
						|
# ___________________________________________________________________________ #
 | 
						|
#                                                                             #
 | 
						|
#       BashLIB -- A library for Bash scripting convenience.                  #
 | 
						|
#                                                                             #
 | 
						|
#                                                                             #
 | 
						|
#    Licensed under the Apache License, Version 2.0 (the "License");          #
 | 
						|
#    you may not use this file except in compliance with the License.         #
 | 
						|
#    You may obtain a copy of the License at                                  #
 | 
						|
#                                                                             #
 | 
						|
#        http://www.apache.org/licenses/LICENSE-2.0                           #
 | 
						|
#                                                                             #
 | 
						|
#    Unless required by applicable law or agreed to in writing, software      #
 | 
						|
#    distributed under the License is distributed on an "AS IS" BASIS,        #
 | 
						|
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
 | 
						|
#    See the License for the specific language governing permissions and      #
 | 
						|
#    limitations under the License.                                           #
 | 
						|
# ___________________________________________________________________________ #
 | 
						|
#                                                                             #
 | 
						|
#                                                                             #
 | 
						|
# Copyright 2007-2013, lhunath                                                #
 | 
						|
#   * http://www.lhunath.com                                                  #
 | 
						|
#   * Maarten Billemont                                                       #
 | 
						|
#                                                                             #
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________ 
 | 
						|
# |                                                                      |
 | 
						|
# |                                            .:  TABLE OF CONTENTS  :. |
 | 
						|
# |______________________________________________________________________|
 | 
						|
# 
 | 
						|
#     chr decimal
 | 
						|
# Outputs the character that has the given decimal ASCII value.
 | 
						|
#
 | 
						|
#     ord character
 | 
						|
# Outputs the decimal ASCII value of the given character.
 | 
						|
#
 | 
						|
#     hex character
 | 
						|
# Outputs the hexadecimal ASCII value of the given character.
 | 
						|
#
 | 
						|
#     unhex character
 | 
						|
# Outputs the character that has the given decimal ASCII value.
 | 
						|
#
 | 
						|
#     max numbers...
 | 
						|
# Outputs the highest of the given numbers.
 | 
						|
#
 | 
						|
#     min numbers...
 | 
						|
# Outputs the lowest of the given numbers.
 | 
						|
#
 | 
						|
#     totime "YYYY-MM-DD HH:MM:SS.mmm"...
 | 
						|
# Outputs the number of milliseconds in the given date string(s).
 | 
						|
#
 | 
						|
#     exists application
 | 
						|
# Succeeds if the application is in PATH and is executable.
 | 
						|
#
 | 
						|
#     eol message
 | 
						|
# Return termination punctuation for a message, if necessary.
 | 
						|
#
 | 
						|
#     hr pattern [length]
 | 
						|
# Outputs a horizontal ruler of the given length in characters or the terminal column length otherwise.
 | 
						|
#
 | 
						|
#     cloc
 | 
						|
# Outputs the current cursor location as two space-separated numbers: row column.
 | 
						|
#
 | 
						|
#     readwhile command [args]
 | 
						|
# Outputs the characters typed by the user into the terminal's input buffer while running the given command.
 | 
						|
#
 | 
						|
#     log [format] [arguments...]
 | 
						|
# Log an event at a certain importance level.
 | 
						|
# The event is expressed as a printf(1) format argument.
 | 
						|
#
 | 
						|
#     ask [-c optionchars|-d default] [-s|-S maskchar] message...
 | 
						|
# Ask a question and read the user's reply to it.  Then output the result on stdout.
 | 
						|
#
 | 
						|
#     trim lines ...
 | 
						|
# Trim the whitespace off of the beginning and end of the given lines.
 | 
						|
#
 | 
						|
#     reverse [-0|-d delimitor] [elements ...] [<<< elements]
 | 
						|
# Reverse the order of the given elements.
 | 
						|
#
 | 
						|
#     order [-0|-d char] [-[cC] isAscending|-n] [-t number] [elements ...] [<<< elements]
 | 
						|
# Orders the elements in ascending order.
 | 
						|
#
 | 
						|
#     mutex file
 | 
						|
# Open a mutual exclusion lock on the file, unless another process already owns one.
 | 
						|
#
 | 
						|
#     pushjob [poolsize] command
 | 
						|
# Start an asynchronous command within a pool, waiting for space in the pool if it is full.
 | 
						|
#
 | 
						|
#     fsleep time
 | 
						|
# Wait for the given (fractional) amount of seconds.
 | 
						|
#
 | 
						|
#     showHelp name description author [option description]...
 | 
						|
# Generate a prettily formatted usage description of the application.
 | 
						|
#
 | 
						|
#     shquote [-e] [argument...]
 | 
						|
# Shell-quote the arguments to make them safe for injection into bash code.
 | 
						|
#
 | 
						|
#     requote [string]
 | 
						|
# Escape the argument string to make it safe for injection into a regex.
 | 
						|
#
 | 
						|
#     shorten [-p pwd] path [suffix]...
 | 
						|
# Shorten an absolute path for pretty printing.
 | 
						|
#
 | 
						|
#     up .../path|num
 | 
						|
# Walk the current working directory up towards root num times or until path is found.
 | 
						|
#
 | 
						|
#     buildarray name terms... -- elements...
 | 
						|
# Create an array by adding all the terms to it for each element, replacing {} terms by the element.
 | 
						|
#
 | 
						|
#     inArray element array
 | 
						|
# Checks whether a certain element is in the given array.
 | 
						|
#
 | 
						|
#     xpathNodes query [files...]
 | 
						|
# Outputs every xpath node that matches the query on a separate line.
 | 
						|
#
 | 
						|
#     hideDebug [on|off]
 | 
						|
# Toggle Bash's debugging mode off temporarily.
 | 
						|
#
 | 
						|
#     stackTrace
 | 
						|
# Output the current script's function execution stack.
 | 
						|
#
 | 
						|
_tocHash=71e13f42e1ea82c1c7019b27a3bc71f3
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________ 
 | 
						|
# |                                                                      |
 | 
						|
# |                                         .:  GLOBAL CONFIGURATION  :. |
 | 
						|
# |______________________________________________________________________|
 | 
						|
 | 
						|
# Unset all exported functions.  Exported functions are evil.
 | 
						|
while read _ _ func; do
 | 
						|
    command unset -f "$func"
 | 
						|
done < <(command declare -Fx)
 | 
						|
 | 
						|
{
 | 
						|
shopt -s extglob
 | 
						|
shopt -s globstar
 | 
						|
} 2>/dev/null ||:
 | 
						|
 | 
						|
# Generate Table Of Contents
 | 
						|
genToc() {
 | 
						|
    local line= comments=() usage= whatis= lineno=0 out= outhash= outline=
 | 
						|
    while read -r line; do
 | 
						|
        (( ++lineno ))
 | 
						|
 | 
						|
        [[ $line = '#'* ]] && comments+=("$line") && continue
 | 
						|
        [[ $line = +([[:alnum:]])'() {' ]] && IFS='()' read func _ <<< "$line" && [[ $func != $FUNCNAME ]] && {
 | 
						|
            usage=${comments[3]##'#'+( )}
 | 
						|
            whatis=${comments[5]##'#'+( )}
 | 
						|
            [[ $usage = $func* && $whatis = *. ]] || err "Malformed docs for %s (line %d)." "$func" "$lineno"
 | 
						|
 | 
						|
            printf -v outline '#     %s\n# %s\n#\n' "$usage" "$whatis"
 | 
						|
            out+=$outline
 | 
						|
        }
 | 
						|
        comments=()
 | 
						|
    done < ~/.bin/bashlib
 | 
						|
 | 
						|
    outhash=$(openssl md5 <<< "$out")
 | 
						|
    if [[ $_tocHash = "$outhash" ]]; then
 | 
						|
        inf 'Table of contents up-to-date.'
 | 
						|
    else
 | 
						|
        printf '%s' "$out"
 | 
						|
        printf '_tocHash=%q' "$outhash"
 | 
						|
        wrn 'Table of contents outdated.'
 | 
						|
    fi
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________ 
 | 
						|
# |                                                                      |
 | 
						|
# |                                          .:  GLOBAL DECLARATIONS  :. |
 | 
						|
# |______________________________________________________________________|
 | 
						|
 | 
						|
# Variables for convenience sequences.
 | 
						|
bobber=(     '.' 'o' 'O' 'o' )
 | 
						|
spinner=(    '-' \\  '|' '/' )
 | 
						|
crosser=(    '+' 'x' '+' 'x' )
 | 
						|
runner=(     '> >'           \
 | 
						|
             '>> '           \
 | 
						|
             ' >>'           )
 | 
						|
 | 
						|
# Variables for terminal requests.
 | 
						|
[[ -t 2 && $TERM != dumb ]] && {
 | 
						|
    COLUMNS=$({ tput cols   || tput co;} 2>&3) # Columns in a line
 | 
						|
    LINES=$({   tput lines  || tput li;} 2>&3) # Lines on screen
 | 
						|
    alt=$(      tput smcup  || tput ti      ) # Start alt display
 | 
						|
    ealt=$(     tput rmcup  || tput te      ) # End   alt display
 | 
						|
    hide=$(     tput civis  || tput vi      ) # Hide cursor
 | 
						|
    show=$(     tput cnorm  || tput ve      ) # Show cursor
 | 
						|
    save=$(     tput sc                     ) # Save cursor
 | 
						|
    load=$(     tput rc                     ) # Load cursor
 | 
						|
    dim=$(      tput dim    || tput mh      ) # Start dim
 | 
						|
    bold=$(     tput bold   || tput md      ) # Start bold
 | 
						|
    stout=$(    tput smso   || tput so      ) # Start stand-out
 | 
						|
    estout=$(   tput rmso   || tput se      ) # End stand-out
 | 
						|
    under=$(    tput smul   || tput us      ) # Start underline
 | 
						|
    eunder=$(   tput rmul   || tput ue      ) # End   underline
 | 
						|
    reset=$(    tput sgr0   || tput me      ) # Reset cursor
 | 
						|
    blink=$(    tput blink  || tput mb      ) # Start blinking
 | 
						|
    italic=$(   tput sitm   || tput ZH      ) # Start italic
 | 
						|
    eitalic=$(  tput ritm   || tput ZR      ) # End   italic
 | 
						|
[[ $TERM != *-m ]] && {
 | 
						|
    red=$(      tput setaf 1|| tput AF 1    )
 | 
						|
    green=$(    tput setaf 2|| tput AF 2    )
 | 
						|
    yellow=$(   tput setaf 3|| tput AF 3    )
 | 
						|
    blue=$(     tput setaf 4|| tput AF 4    )
 | 
						|
    magenta=$(  tput setaf 5|| tput AF 5    )
 | 
						|
    cyan=$(     tput setaf 6|| tput AF 6    )
 | 
						|
}
 | 
						|
    black=$(    tput setaf 0|| tput AF 0    )
 | 
						|
    white=$(    tput setaf 7|| tput AF 7    )
 | 
						|
    default=$(  tput op                     )
 | 
						|
    eed=$(      tput ed     || tput cd      )   # Erase to end of display
 | 
						|
    eel=$(      tput el     || tput ce      )   # Erase to end of line
 | 
						|
    ebl=$(      tput el1    || tput cb      )   # Erase to beginning of line
 | 
						|
    ewl=$eel$ebl                                # Erase whole line
 | 
						|
    draw=$(     tput -S <<< '   enacs
 | 
						|
                                smacs
 | 
						|
                                acsc
 | 
						|
                                rmacs' || { \
 | 
						|
                tput eA; tput as;
 | 
						|
                tput ac; tput ae;         } )   # Drawing characters
 | 
						|
    back=$'\b'
 | 
						|
} 3>&2 2>/dev/null ||:
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________ 
 | 
						|
# |                                                                      |
 | 
						|
# |                                        .:  FUNCTION DECLARATIONS  :. |
 | 
						|
# |______________________________________________________________________|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Chr _______________________________________________________________|
 | 
						|
#
 | 
						|
#       chr decimal
 | 
						|
#
 | 
						|
# Outputs the character that has the given decimal ASCII value.
 | 
						|
#
 | 
						|
chr() {
 | 
						|
    printf "\\$(printf '%03o' "$1")"
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Ord _______________________________________________________________|
 | 
						|
#
 | 
						|
#       ord character
 | 
						|
#
 | 
						|
# Outputs the decimal ASCII value of the given character.
 | 
						|
#
 | 
						|
ord() {
 | 
						|
    printf '%d' "'$1"
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Hex _______________________________________________________________|
 | 
						|
#
 | 
						|
#       hex character
 | 
						|
#
 | 
						|
# Outputs the hexadecimal ASCII value of the given character.
 | 
						|
#
 | 
						|
hex() { 
 | 
						|
    printf '%x' "'$1"
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Unhex _______________________________________________________________|
 | 
						|
#
 | 
						|
#       unhex character
 | 
						|
#
 | 
						|
# Outputs the character that has the given hexadecimal ASCII value.
 | 
						|
#
 | 
						|
unhex() {
 | 
						|
    printf "\\x$1"
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ max _______________________________________________________________|
 | 
						|
#
 | 
						|
#       max numbers...
 | 
						|
#
 | 
						|
# Outputs the highest of the given numbers.
 | 
						|
#
 | 
						|
max() {
 | 
						|
    local max=$1 n
 | 
						|
    for n; do
 | 
						|
        (( n > max )) && max=$n
 | 
						|
    done
 | 
						|
    printf %d "$max"
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ min _______________________________________________________________|
 | 
						|
#
 | 
						|
#       min numbers...
 | 
						|
#
 | 
						|
# Outputs the lowest of the given numbers.
 | 
						|
#
 | 
						|
min() {
 | 
						|
    local min=$1 n
 | 
						|
    for n; do
 | 
						|
        (( n < min )) && min=$n
 | 
						|
    done
 | 
						|
    printf '%d' "$min"
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ totime ____________________________________________________________|
 | 
						|
#
 | 
						|
#       totime "YYYY-MM-DD HH:MM:SS.mmm"...
 | 
						|
#
 | 
						|
# Outputs the number of milliseconds in the given date string(s).
 | 
						|
#
 | 
						|
# When multiple date string arguments are given, multiple time strings are output, one per line.
 | 
						|
#
 | 
						|
# The fields should be in the above defined order.  The delimitor between the fields may be any one of [ -:.].
 | 
						|
# If a date string does not follow the defined format, the result is undefined.
 | 
						|
#
 | 
						|
# Note that this function uses a very simplistic conversion formula which does not take any special calendar
 | 
						|
# convenions into account.  It assumes there are 12 months in evert year, 31 days in every month, 24 hours
 | 
						|
# in every day, 60 minutes in every hour, 60 seconds in every minute and 1000 milliseconds in every second.
 | 
						|
#
 | 
						|
totime() {
 | 
						|
    local arg time year month day hour minute second milli
 | 
						|
    for arg; do
 | 
						|
        IFS=' -:.' read year month day hour minute second milli <<< "$arg" &&
 | 
						|
            (( time = (((((((((((10#$year * 12) + 10#$month) * 31) + 10#$day) * 24) + 10#$hour) * 60) + 10#$minute) * 60) + 10#$second) * 1000) + 10#$milli )) &&
 | 
						|
            printf '%d\n' "$time"
 | 
						|
    done
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Exists ____________________________________________________________|
 | 
						|
#
 | 
						|
#       exists application
 | 
						|
#
 | 
						|
# Succeeds if the application is in PATH and is executable.
 | 
						|
#
 | 
						|
exists() {
 | 
						|
    [[ -x $(type -P "$1" 2>/dev/null) ]]
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ FirstExists ____________________________________________________________|
 | 
						|
#
 | 
						|
#       firstExists file...
 | 
						|
#
 | 
						|
# Outputs the first of the arguments that is a file which exists.
 | 
						|
#
 | 
						|
firstExists() {
 | 
						|
    local file;
 | 
						|
    for file; do
 | 
						|
        [[ -e "$file" ]] && printf %s "$file" && exit
 | 
						|
    done
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Eol _______________________________________________________________|
 | 
						|
#
 | 
						|
#       eol message
 | 
						|
#
 | 
						|
# Return termination punctuation for a message, if necessary.
 | 
						|
#
 | 
						|
eol() {
 | 
						|
    : #[[ $1 && $1 != *[\!\?.,:\;\|] ]] && printf .. ||:
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Hr _______________________________________________________________|
 | 
						|
#
 | 
						|
#       hr pattern [length]
 | 
						|
#
 | 
						|
# Outputs a horizontal ruler of the given length in characters or the terminal column length otherwise.
 | 
						|
# The ruler is a repetition of the given pattern string.
 | 
						|
#
 | 
						|
hr() {
 | 
						|
    local pattern=${1:--} length=${2:-$COLUMNS} ruler=
 | 
						|
 | 
						|
    while (( ${#ruler} < length )); do
 | 
						|
        ruler+=${pattern:0:length-${#ruler}}
 | 
						|
    done
 | 
						|
 | 
						|
    printf %s "$ruler"
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ CLoc ______________________________________________________________|
 | 
						|
#
 | 
						|
#       cloc
 | 
						|
#
 | 
						|
# Outputs the current cursor location as two space-separated numbers: row column.
 | 
						|
#
 | 
						|
cloc() {
 | 
						|
    local old=$(stty -g)
 | 
						|
    trap 'stty "$old"' RETURN
 | 
						|
    stty raw
 | 
						|
 | 
						|
    # If the tty has input waiting then we can't read back its response.  We'd only break and pollute the tty input buffer.
 | 
						|
    read -t 0 < /dev/tty 2>/dev/null && return 1
 | 
						|
 | 
						|
    printf '\e[6n' > /dev/tty
 | 
						|
    IFS='[;' read -dR _ row col < /dev/tty
 | 
						|
    printf '%d %d' "$row" "$col"
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ readwhile ______________________________________________________________|
 | 
						|
#
 | 
						|
#       readwhile command [args]
 | 
						|
#
 | 
						|
# Outputs the characters typed by the user into the terminal's input buffer while running the given command.
 | 
						|
#
 | 
						|
readwhile() {
 | 
						|
    local old=$(stty -g) in result REPLY
 | 
						|
    trap 'stty "$old"' RETURN
 | 
						|
    stty raw
 | 
						|
 | 
						|
    "$@"
 | 
						|
    result=$?
 | 
						|
 | 
						|
    while read -t 0; do
 | 
						|
        IFS= read -rd '' -n1 && in+=$REPLY
 | 
						|
    done
 | 
						|
    printf %s "$in"
 | 
						|
 | 
						|
    return $result
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Latest ____________________________________________________________|
 | 
						|
#
 | 
						|
#       latest [file...]
 | 
						|
#
 | 
						|
# Output the argument that represents the file with the latest modification time.
 | 
						|
#
 | 
						|
latest() (
 | 
						|
    shopt -s nullglob
 | 
						|
    local file latest=$1
 | 
						|
    for file; do
 | 
						|
        [[ $file -nt $latest ]] && latest=$file
 | 
						|
    done
 | 
						|
    printf '%s\n' "$latest"
 | 
						|
) # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  _______________________________________________________________________
 | 
						|
# |__ Iterate ____________________________________________________________|
 | 
						|
#
 | 
						|
#       iterate [command]
 | 
						|
#
 | 
						|
# All arguments to iterate make up a single command that will be executed.
 | 
						|
#
 | 
						|
# Any of the arguments may be of the format {x..y[..z]} which causes the command
 | 
						|
# to be executed in a loop, each iteration substituting the argument for the
 | 
						|
# current step the loop has reached from x to y.  We step from x to y by
 | 
						|
# walking from x's position in the ASCII character table to y's with a step of z
 | 
						|
# or 1 if z is not specified.
 | 
						|
#
 | 
						|
iterate() (
 | 
						|
    local command=( "$@" ) iterationCommand=() loop= a= arg= current=() step=() target=()
 | 
						|
    for a in "${!command[@]}"; do
 | 
						|
        arg=${command[a]}
 | 
						|
        if [[ $arg = '{'*'}' ]]; then
 | 
						|
            loop=${arg#'{'} loop=${loop%'}'}
 | 
						|
            step[a]=${loop#*..*..} current[a]=${loop%%..*} target[a]=${loop#*..} target[a]=${target[a]%.."${step[a]}"}
 | 
						|
            [[ ! ${step[a]} || ${step[a]} = $loop ]] && step[a]=1
 | 
						|
        fi
 | 
						|
    done
 | 
						|
    if (( ${#current[@]} )); then
 | 
						|
        for loop in "${!current[@]}"; do
 | 
						|
            while true; do
 | 
						|
                iterationCommand=()
 | 
						|
 | 
						|
                for a in "${!command[@]}"; do
 | 
						|
                    (( a == loop )) \
 | 
						|
                        && iterationCommand+=( "${current[a]}" ) \
 | 
						|
                        || iterationCommand+=( "${command[a]}" )
 | 
						|
                done
 | 
						|
 | 
						|
                iterate "${iterationCommand[@]}"
 | 
						|
 | 
						|
                [[ ${current[loop]} = ${target[loop]} ]] && break
 | 
						|
                current[loop]="$(chr "$(( $(ord "${current[loop]}") + ${step[loop]} ))")"
 | 
						|
            done
 | 
						|
        done
 | 
						|
    else
 | 
						|
        "${command[@]}"
 | 
						|
    fi
 | 
						|
) # _____________________________________________________________________
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Logging ___________________________________________________________|
 | 
						|
#
 | 
						|
#       log format [arguments...]
 | 
						|
#
 | 
						|
# Log an event at a certain importance level.  The event is expressed as a printf(1) format argument.
 | 
						|
# The current exit code remains unaffected by the execution of this function.
 | 
						|
#
 | 
						|
# Instead of 'log', you can use a level as command name, to log at that level.  Using log, messages are
 | 
						|
# logged at level inf.  The supported levels are: trc, dbg, inf, wrn, err, ftl.
 | 
						|
#
 | 
						|
# If you prefix the command name with a p, the log message is shown as a spinner and waits for the next
 | 
						|
# closing statement.  Eg.
 | 
						|
#   
 | 
						|
#   pinf 'Converting image'
 | 
						|
#       convert image.png image.jpg
 | 
						|
#   fnip
 | 
						|
#
 | 
						|
# The closing statement (here fnip) is the reverse of the opening statement and exits with the exit code
 | 
						|
# of the last command.  If the last command failed, it shows the exit code in the spinner before it is stopped.
 | 
						|
# The closing statement also takes a format and arguments, which are displayed in the spinner.
 | 
						|
#
 | 
						|
log() {
 | 
						|
    local exitcode=$? level=${level:-inf} supported=0 end=$'\n' type=msg conMsg= logMsg= format= colorFormat= date= info= arg= args=() colorArgs=() ruler=
 | 
						|
 | 
						|
    # Handle options.
 | 
						|
    local OPTIND=1
 | 
						|
    while getopts :tpuPrR:d:n arg; do
 | 
						|
        case $arg in
 | 
						|
            p)
 | 
						|
                end='.. '
 | 
						|
                type=startProgress ;;
 | 
						|
            u)
 | 
						|
                end='.. '
 | 
						|
                type=updateProgress ;;
 | 
						|
            P)
 | 
						|
                type=stopProgress ;;
 | 
						|
            r)
 | 
						|
                ruler='____' ;;
 | 
						|
            R)
 | 
						|
                ruler=$OPTARG ;;
 | 
						|
            d)
 | 
						|
                end=$OPTARG ;;
 | 
						|
            n)
 | 
						|
                end= ;;
 | 
						|
            t)
 | 
						|
                date=$(date +"${_logDate:-%H:%M}") ;;
 | 
						|
        esac
 | 
						|
    done
 | 
						|
    shift "$((OPTIND-1))"
 | 
						|
    format=$1 args=( "${@:2}" )
 | 
						|
    (( ! ${#args[@]} )) && [[ $format ]] && { args=("$format") format=%s; local bold=; }
 | 
						|
 | 
						|
    # Level-specific settings.
 | 
						|
    local logLevelColor
 | 
						|
    case $level in
 | 
						|
        TRC)    (( supported = _logVerbosity >= 4 ))
 | 
						|
                logLevelColor=$_logTrcColor ;;
 | 
						|
        DBG)    (( supported = _logVerbosity >= 3 ))
 | 
						|
                logLevelColor=$_logDbgColor ;;
 | 
						|
        INF)    (( supported = _logVerbosity >= 2 ))
 | 
						|
                logLevelColor=$_logInfColor ;;
 | 
						|
        WRN)    (( supported = _logVerbosity >= 1 ))
 | 
						|
                logLevelColor=$_logWrnColor ;;
 | 
						|
        ERR)    (( supported = _logVerbosity >= 0 ))
 | 
						|
                logLevelColor=$_logErrColor ;;
 | 
						|
        FTL)    (( supported = 1 ))
 | 
						|
                logLevelColor=$_logFtlColor ;;
 | 
						|
        *)
 | 
						|
                log FTL 'Log level %s does not exist' "$level"
 | 
						|
                exit 1 ;;
 | 
						|
    esac
 | 
						|
    (( ! supported )) && return "$exitcode"
 | 
						|
    local logColor=${_logColor:+$logLevelColor}
 | 
						|
 | 
						|
    # Generate the log message.
 | 
						|
    case $type in
 | 
						|
        msg|startProgress)
 | 
						|
            printf -v logMsg "[${date:+%s }%-3s] $format$end" ${date:+"$date"} "$level" "${args[@]}"
 | 
						|
            if (( _logColor )); then
 | 
						|
                colorFormat=$(sed ${reset:+-e "s/$(requote "$reset")/$reset$logColor/g"} -e "s/%[^a-z]*[a-z]/$reset$bold$logColor&$reset$logColor/g" <<< "$format")
 | 
						|
                colorArgs=("${args[@]//$reset/$reset$bold$logColor}")
 | 
						|
                printf -v conMsg "$reset[${date:+%s }$logColor$bold%-3s$reset] $logColor$colorFormat$reset$black\$$reset$end$save" ${date:+"$date"} "$level" "${colorArgs[@]}"
 | 
						|
            else
 | 
						|
                conMsg=$logMsg
 | 
						|
            fi
 | 
						|
        ;;
 | 
						|
 | 
						|
        updateProgress)
 | 
						|
            printf -v logMsg printf " [$format]" "${args[@]}"
 | 
						|
            if (( _logColor )); then
 | 
						|
                colorFormat=$(sed ${reset:+-e "s/$(requote "$reset")/$reset$logColor/g"} -e "s/%[^a-z]*[a-z]/$reset$bold$logColor&$reset$logColor/g" <<< "$format")
 | 
						|
                colorArgs=("${args[@]//$reset/$reset$bold$logColor}")
 | 
						|
                printf -v conMsg "$load$eel$blue$bold[$reset$logColor$colorFormat$reset$blue$bold]$reset$end" "${colorArgs[@]}"
 | 
						|
            else
 | 
						|
                conMsg=$logMsg
 | 
						|
            fi
 | 
						|
        ;;
 | 
						|
 | 
						|
        stopProgress)
 | 
						|
            case $exitcode in
 | 
						|
                0)  printf -v logMsg "done${format:+ ($format)}.\n" "${args[@]}"
 | 
						|
                    if (( _logColor )); then
 | 
						|
                        colorFormat=$(sed ${reset:+-e "s/$(requote "$reset")/$reset$logColor/g"} -e "s/%[^a-z]*[a-z]/$reset$bold$logColor&$reset$logColor/g" <<< "$format")
 | 
						|
                        colorArgs=("${args[@]//$reset/$reset$bold$logColor}")
 | 
						|
                        printf -v conMsg "$load$eel$green${bold}done${colorFormat:+ ($reset$logColor$colorFormat$reset$green$bold)}$reset.\n" "${colorArgs[@]}"
 | 
						|
                    else
 | 
						|
                        conMsg=$logMsg
 | 
						|
                    fi
 | 
						|
                ;;
 | 
						|
 | 
						|
                *)  info=${format:+$(printf ": $format" "${args[@]}")}
 | 
						|
                    printf -v logMsg "error(%d%s).\n" "$exitcode" "$info"
 | 
						|
                    if (( _logColor )); then
 | 
						|
                        printf -v conMsg "${eel}${red}error${reset}(${bold}${red}%d${reset}%s).\n" "$exitcode" "$info"
 | 
						|
                    else
 | 
						|
                        conMsg=$logMsg
 | 
						|
                    fi
 | 
						|
                ;;
 | 
						|
            esac
 | 
						|
        ;;
 | 
						|
    esac
 | 
						|
 | 
						|
    # Create the log file.
 | 
						|
    if [[ $_logFile && ! -e $_logFile ]]; then
 | 
						|
        [[ $_logFile = */* ]] || $_logFile=./$logFile
 | 
						|
        mkdir -p "${_logFile%/*}" && touch "$_logFile"
 | 
						|
    fi
 | 
						|
 | 
						|
    # Stop the spinner.
 | 
						|
    if [[ $type = stopProgress && $_logSpinner ]]; then
 | 
						|
        kill "$_logSpinner"
 | 
						|
        wait "$_logSpinner" 2>/dev/null
 | 
						|
        unset _logSpinner
 | 
						|
    fi
 | 
						|
 | 
						|
    # Output the ruler.
 | 
						|
    if [[ $ruler ]]; then
 | 
						|
        printf >&2 '%s\n' "$(hr "$ruler")"
 | 
						|
        [[ -w $_logFile ]] \
 | 
						|
            && printf >> "$_logFile" '%s' "$ruler"
 | 
						|
    fi
 | 
						|
 | 
						|
    # Output the log message.
 | 
						|
    printf >&2 '%s' "$conMsg"
 | 
						|
    [[ -w $_logFile ]] \
 | 
						|
        && printf >> "$_logFile" '%s' "$logMsg"
 | 
						|
 | 
						|
    # Start the spinner.
 | 
						|
    if [[ $type = startProgress && ! $_logSpinner && $TERM != dumb ]]; then
 | 
						|
        {
 | 
						|
            set +m
 | 
						|
            trap 'printf >&2 %s "$show"' EXIT
 | 
						|
            printf >&2 %s "$hide"
 | 
						|
            while printf >&2 "$eel$blue$bold[$reset%s$reset$blue$bold]$reset\b\b\b" "${spinner[s++ % ${#spinner[@]}]}" && sleep .1
 | 
						|
            do :; done
 | 
						|
        } & _logSpinner=$!
 | 
						|
    fi
 | 
						|
 | 
						|
    return $exitcode
 | 
						|
}
 | 
						|
trc() { level=TRC log "$@"; }
 | 
						|
dbg() { level=DBG log "$@"; }
 | 
						|
inf() { level=INF log "$@"; }
 | 
						|
wrn() { level=WRN log "$@"; }
 | 
						|
err() { level=ERR log "$@"; }
 | 
						|
ftl() { level=FTL log "$@"; }
 | 
						|
plog() { log -p "$@"; }
 | 
						|
ulog() { log -u "$@"; }
 | 
						|
golp() { log -P "$@"; }
 | 
						|
ptrc() { level=TRC plog "$@"; }
 | 
						|
pdbg() { level=DBG plog "$@"; }
 | 
						|
pinf() { level=INF plog "$@"; }
 | 
						|
pwrn() { level=WRN plog "$@"; }
 | 
						|
perr() { level=ERR plog "$@"; }
 | 
						|
pftl() { level=FTL plog "$@"; }
 | 
						|
utrc() { level=TRC ulog "$@"; }
 | 
						|
udbg() { level=DBG ulog "$@"; }
 | 
						|
uinf() { level=INF ulog "$@"; }
 | 
						|
uwrn() { level=WRN ulog "$@"; }
 | 
						|
uerr() { level=ERR ulog "$@"; }
 | 
						|
uftl() { level=FTL ulog "$@"; }
 | 
						|
gtrc() { level=trc golp "$@"; }
 | 
						|
gbdp() { level=DBG golp "$@"; }
 | 
						|
fnip() { level=INF golp "$@"; }
 | 
						|
nrwp() { level=WRN golp "$@"; }
 | 
						|
rrep() { level=ERR golp "$@"; }
 | 
						|
ltfp() { level=FTL golp "$@"; }
 | 
						|
_logColor=${_logColor:-$([[ -t 2 ]] && echo 1)} _logVerbosity=2
 | 
						|
_logTrcColor=$grey _logDbgColor=$blue _logInfColor=$white _logWrnColor=$yellow _logErrColor=$red _logFtlColor=$bold$red
 | 
						|
# _______________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Ask _______________________________________________________________|
 | 
						|
#
 | 
						|
#       ask [-c optionchars|-d default] [-s|-S maskchar] format [arguments...]
 | 
						|
#
 | 
						|
# Ask a question and read the user's reply to it.  Then output the result on stdout.
 | 
						|
#
 | 
						|
# When in normal mode, a single line is read.  If the line is empty and
 | 
						|
# -d was specified, the default argument is output instead of an empty line.
 | 
						|
# The exit code is always 0.
 | 
						|
#
 | 
						|
# When in option mode (-c), the user is shown the option characters with
 | 
						|
# which he can reply and a single character is read.
 | 
						|
# If the reply is empty (user hits enter) and any of the optionchars are
 | 
						|
# upper-case, the upper-case option (= the default option) character will
 | 
						|
# be output instead of an empty line.
 | 
						|
# If the reply character is not amoungst the provided options the default
 | 
						|
# option is again output instead if present.  If no default was given, an
 | 
						|
# exit code of 2 is returned.
 | 
						|
# You may mark an optionchar as 'valid' by appending a '!' to it.  As a
 | 
						|
# result, an exit code of 0 will only be returned if this valid option
 | 
						|
# is replied.  If not, an exit code of 1 will be returned.
 | 
						|
#
 | 
						|
ask() {
 | 
						|
   
 | 
						|
    # Initialize the vars.
 | 
						|
    local opt arg
 | 
						|
    local option=
 | 
						|
    local options=
 | 
						|
    local default=
 | 
						|
    local silent=
 | 
						|
    local valid=
 | 
						|
    local muteChar=
 | 
						|
    local format=
 | 
						|
 | 
						|
    # Parse the options.
 | 
						|
    local OPTIND=1
 | 
						|
    while getopts :sS:c:d: opt; do
 | 
						|
        case $opt in
 | 
						|
            s)  silent=1 ;;
 | 
						|
            S)  silent=1 muteChar=$OPTARG ;;
 | 
						|
            c)  while read -n1 arg; do
 | 
						|
                    case $arg in
 | 
						|
                        [[:upper:]]) default=$arg ;;
 | 
						|
                        !) valid=${options: -1}; continue ;;
 | 
						|
                    esac
 | 
						|
 | 
						|
                    options+=$arg
 | 
						|
                done <<< "$OPTARG" ;;
 | 
						|
            d)  default=$OPTARG option=$default ;;
 | 
						|
        esac
 | 
						|
    done
 | 
						|
 | 
						|
    # Trim off the options.
 | 
						|
    shift $((OPTIND-1))
 | 
						|
 | 
						|
    # Figure out what FD to use for our messages.
 | 
						|
    [[ -t 1 ]] && local fd=1 || local fd=2
 | 
						|
 | 
						|
    # Ask the question.
 | 
						|
    format=$1; shift
 | 
						|
    level=${level:-WRN} log -n "$format${option:+ [%s]}${options:+ [%s]}" "$@" ${option:+"$option"} ${options:+"$options"}
 | 
						|
 | 
						|
    # Read the reply.
 | 
						|
    exec 8<&0; [[ -t 8 ]] || exec 8</dev/tty
 | 
						|
    if [[ $muteChar ]]; then
 | 
						|
        local reply
 | 
						|
        while read -u8 -s -n1 && [[ $REPLY ]]; do
 | 
						|
            reply+=$REPLY
 | 
						|
            printf '%s' "$muteChar"                                 >&$fd
 | 
						|
        done
 | 
						|
        REPLY=$reply
 | 
						|
    else
 | 
						|
        read -u8 -e ${options:+-n1} ${silent:+-s}
 | 
						|
    fi
 | 
						|
    [[ $options && $REPLY ]] || (( silent )) && printf '\n'         >&$fd
 | 
						|
 | 
						|
    # Evaluate the reply.
 | 
						|
    while true; do
 | 
						|
        if [[ $REPLY && ( ! $options || $options = *$REPLY* ) ]]; then
 | 
						|
            if [[ $valid ]]
 | 
						|
            then [[ $REPLY = $valid ]]
 | 
						|
            else printf "%s" "$REPLY"
 | 
						|
            fi
 | 
						|
 | 
						|
            return
 | 
						|
        fi
 | 
						|
 | 
						|
        [[ -z $default || $REPLY = $default ]] \
 | 
						|
            && return 2
 | 
						|
        
 | 
						|
        REPLY=$default
 | 
						|
    done
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Trim ______________________________________________________________|
 | 
						|
#
 | 
						|
#       trim lines ...
 | 
						|
#
 | 
						|
# Trim the whitespace off of the beginning and end of the given lines.
 | 
						|
# Each argument is considdered one line; is treated and printed out.
 | 
						|
#
 | 
						|
# When no arguments are given, lines will be read from standard input.
 | 
						|
#
 | 
						|
trim() {
 | 
						|
    { (( $# )) && printf '%s\n' "$@" || cat; } | \
 | 
						|
        sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Reverse ___________________________________________________________|
 | 
						|
#
 | 
						|
#       reverse [-0|-d delimitor] [elements ...] [<<< elements]
 | 
						|
#
 | 
						|
# Reverse the order of the given elements.
 | 
						|
# Elements are read from command arguments or standard input if no element
 | 
						|
# arguments are given.
 | 
						|
# They are reversed and output on standard output.
 | 
						|
#
 | 
						|
# If the -0 option is given, input and output are delimited by NUL bytes.
 | 
						|
# If the -d option is given, input and output are delimited by the
 | 
						|
# character argument.
 | 
						|
# Otherwise, they are delimited by newlines.
 | 
						|
#
 | 
						|
reverse() {
 | 
						|
   
 | 
						|
    # Initialize the vars.
 | 
						|
    local elements=() delimitor=$'\n' i
 | 
						|
 | 
						|
    # Parse the options.
 | 
						|
    local OPTIND=1
 | 
						|
    while getopts :0d: opt; do
 | 
						|
        case $opt in
 | 
						|
            0) delimitor=$'\0' ;;
 | 
						|
            d) delimitor=$OPTARG ;;
 | 
						|
        esac
 | 
						|
    done
 | 
						|
    shift "$((OPTIND-1))"
 | 
						|
 | 
						|
    # Get the elements.
 | 
						|
    if (( $# )); then
 | 
						|
        elements=( "$@" )
 | 
						|
    else
 | 
						|
        while IFS= read -r -d "$delimitor"; do
 | 
						|
            elements+=("$REPLY")
 | 
						|
        done
 | 
						|
    fi
 | 
						|
 | 
						|
    # Iterate in reverse order.
 | 
						|
    for (( i=${#elements[@]} - 1; i >=0; --i )); do
 | 
						|
        printf "%s${delimitor:-'\0'}" "${elements[i]}"
 | 
						|
    done
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Order _____________________________________________________________|
 | 
						|
#
 | 
						|
#       order [-0|-d char] [-[fF] isDesired] [-[cC] isAscending|-n|-r|-t] [-T number] [-a array|elements ...] [<<< elements]
 | 
						|
#
 | 
						|
# Orders the elements in ascending order.
 | 
						|
# Elements are read from command arguments or standard input if no element
 | 
						|
# arguments are given.
 | 
						|
# The result is output on standard output.
 | 
						|
#
 | 
						|
# By default, the elements will be ordered using lexicographic comparison.
 | 
						|
# If the -n option is given, the elements will be ordered numerically.
 | 
						|
# If the -r option is given, the elements will be ordered randomly.
 | 
						|
# If the -f option is given, the command name following it will be used
 | 
						|
# as a filter.
 | 
						|
# If the -c option is given, the command name following it will be used
 | 
						|
# as a comparator.
 | 
						|
# If the -C option is given, the bash code following it will be used
 | 
						|
# as a comparator.
 | 
						|
# If the -t option is given, only the first number results are returned.
 | 
						|
# If the -a option is given, the elements in array are ordered instead and
 | 
						|
# array is mutated to contain the result.
 | 
						|
# If number is 0, all results are returned.
 | 
						|
#
 | 
						|
# isDesired is a command name which will get one parameter.  The parameter
 | 
						|
# is an element which will only be included if the command exits successfully.
 | 
						|
# isAscending is a command name which will be executed for each element
 | 
						|
# comparison and will be passed two element arguments.  The command should
 | 
						|
# succeed if the first argument is less than the second argument for the
 | 
						|
# purpose of this sort.
 | 
						|
#
 | 
						|
# If the -0 option is given, input and output are delimited by NUL bytes.
 | 
						|
# If the -d option is given, input and output are delimited by the
 | 
						|
# character argument.
 | 
						|
# Otherwise, they are delimited by newlines.
 | 
						|
#
 | 
						|
# The ordering is implemented by an insertion sort algorithm.
 | 
						|
#
 | 
						|
order() {
 | 
						|
   
 | 
						|
    # Initialize the vars.
 | 
						|
    local delimitor=$'\n' i isDesired=true isAscending=string_ascends top=0 arrayName= array=
 | 
						|
 | 
						|
    # Parse the options.
 | 
						|
    local OPTIND=1
 | 
						|
    while getopts :0nrd:f:F:c:C:tT:a: opt; do
 | 
						|
        case $opt in
 | 
						|
            0) delimitor=$'\0' ;;
 | 
						|
            d) delimitor=$OPTARG ;;
 | 
						|
            n) isAscending=number_ascends ;;
 | 
						|
            r) isAscending=random_ascends ;;
 | 
						|
            t) isAscending=mtime_ascends ;;
 | 
						|
            f) isDesired=$OPTARG ;;
 | 
						|
            F) isDesired=bash_desired bash_desired_code=$OPTARG ;;
 | 
						|
            c) isAscending=$OPTARG ;;
 | 
						|
            C) isAscending=bash_ascends bash_ascends_code=$OPTARG ;;
 | 
						|
            T) top=$OPTARG ;;
 | 
						|
            a) arrayName=$OPTARG array=$arrayName[@] ;;
 | 
						|
        esac
 | 
						|
    done
 | 
						|
    shift "$((OPTIND-1))"
 | 
						|
 | 
						|
    # Get the elements.
 | 
						|
    local elements=() element
 | 
						|
    if [[ $arrayName ]]; then
 | 
						|
        for element in "${!array}"; do
 | 
						|
            "$isDesired" "$element" && elements+=("$element")
 | 
						|
        done
 | 
						|
    elif (( $# )); then
 | 
						|
        for element; do
 | 
						|
            "$isDesired" "$element" && elements+=("$element")
 | 
						|
        done
 | 
						|
    else
 | 
						|
        while IFS= read -r -d "$delimitor" element; do
 | 
						|
            "$isDesired" "$element" && elements+=("$element")
 | 
						|
        done
 | 
						|
    fi
 | 
						|
 | 
						|
    # Iterate in reverse order.
 | 
						|
    for (( i = 1; i < ${#elements[@]}; ++i )); do
 | 
						|
        for (( j = i; j > 0; --j )); do
 | 
						|
            element=${elements[j]}
 | 
						|
            if "$isAscending" "$element" "${elements[j-1]}"; then
 | 
						|
                elements[j]=${elements[j-1]}
 | 
						|
                elements[j-1]=$element
 | 
						|
            fi
 | 
						|
        done
 | 
						|
    done
 | 
						|
 | 
						|
    (( top )) || top=${#elements[@]}
 | 
						|
    if [[ $array ]]; then
 | 
						|
        declare -ga "$array=($(printf '%q ' "${elements[@]:0:top}"))"
 | 
						|
    else
 | 
						|
        printf "%s${delimitor:-\0}" "${elements[@]:0:top}"
 | 
						|
    fi
 | 
						|
} # _____________________________________________________________________
 | 
						|
string_ascends()    { [[ $1 < $2 ]]; }
 | 
						|
number_ascends()    { (( $1 < $2 )); }
 | 
						|
random_ascends()    { (( RANDOM % 2 )); }
 | 
						|
mtime_ascends()     { [[ $1 -ot $2 ]]; }
 | 
						|
exists_desired()    { [[ -e $1 ]]; }
 | 
						|
line_desired()      { [[ $1 ]]; }
 | 
						|
code_desired()      { line_desired "$1" && ! comment_desired "$1"; }
 | 
						|
comment_desired()   { line_desired "$1" && [[ $1 = @(#|//|/\*)* ]]; }
 | 
						|
bash_desired()      { bash -c "$bash_desired_code" -- "$@"; }
 | 
						|
bash_ascends()      { bash -c "$bash_ascends_code" -- "$@"; }
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Mutex _____________________________________________________________|
 | 
						|
#
 | 
						|
#       mutex file
 | 
						|
#
 | 
						|
# Open a mutual exclusion lock on the file, unless another process already owns one.
 | 
						|
#
 | 
						|
# If the file is already locked by another process, the operation fails.
 | 
						|
# This function defines a lock on a file as having a file descriptor open to the file.
 | 
						|
# This function uses FD 9 to open a lock on the file.  To release the lock, close FD 9:
 | 
						|
# exec 9>&-
 | 
						|
#
 | 
						|
mutex() {
 | 
						|
    local lockfile=${1:-${BASH_SOURCE[-1]}} pid pids
 | 
						|
    [[ -e $lockfile ]] || err "No such file: $lockfile" || return
 | 
						|
 | 
						|
    exec 9>> "$lockfile" && [[ $({ fuser -f "$lockfile"; } 2>&- 9>&-) == $$ ]]
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ PushJob ___________________________________________________________|
 | 
						|
#
 | 
						|
#       pushjob [poolsize] command
 | 
						|
#
 | 
						|
# Start an asynchronous command within a pool, waiting for space in the pool if it is full.
 | 
						|
#
 | 
						|
# The pool is pruned automatically as running jobs complete.  This function
 | 
						|
# allows you to easily run asynchronous commands within a pool of N,
 | 
						|
# automatically starting the next command as soon as there's space.
 | 
						|
#
 | 
						|
pushjob() {
 | 
						|
    local size=$1; shift 1
 | 
						|
 | 
						|
    # Wait for space in the pool.
 | 
						|
    until (( ${#jobpool[@]} < size )); do
 | 
						|
        sleep 1 & pushjobsleep=$!
 | 
						|
        wait "$pushjobsleep"
 | 
						|
    done 2>/dev/null
 | 
						|
 | 
						|
    # Register prunejobs and start the pushed job.
 | 
						|
    trap _prunejobs SIGCHLD
 | 
						|
    set -m
 | 
						|
    "$@" & jobpool[$!]=
 | 
						|
}
 | 
						|
_prunejobs() {
 | 
						|
    # Prune all pool jobs that are no longer running.
 | 
						|
    for pid in "${!jobpool[@]}"; do
 | 
						|
        kill -0 "$pid" 2>/dev/null || unset "jobpool[$pid]"
 | 
						|
    done
 | 
						|
 | 
						|
    # Unregister SIGCHLD if our pool is empty.
 | 
						|
    (( ${#jobpool[@]} )) || trap - SIGCHLD
 | 
						|
 | 
						|
    # Wake up pushjob.
 | 
						|
    kill "$pushjobsleep" 2>/dev/null
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ FSleep _____________________________________________________________|
 | 
						|
#
 | 
						|
#       fsleep time
 | 
						|
#
 | 
						|
# Wait for the given (fractional) amount of seconds.
 | 
						|
#
 | 
						|
# This implementation solves the problem portably, assuming that either
 | 
						|
# bash 4.x or a fractional sleep(1) is available.
 | 
						|
#
 | 
						|
fsleep() {
 | 
						|
 | 
						|
    local fifo=${TMPDIR:-/tmp}/.fsleep.$$
 | 
						|
    trap 'rm -f "$fifo"' RETURN
 | 
						|
    mkfifo "$fifo" && { read -t "$1" <> "$fifo" 2>/dev/null || sleep "$1"; }
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ options ___________________________________________________________|
 | 
						|
#
 | 
						|
#       options [option description]...
 | 
						|
#
 | 
						|
# Specify and handle options in arguments.
 | 
						|
#
 | 
						|
# The 'setopt' function will be called for each option expected option
 | 
						|
# passed to the script, with $1 set to the option character and $2
 | 
						|
# its description.  Check OPTARG if the option takes an argument.
 | 
						|
# 'setopt' will be called with '?' if an invalid option is passed.
 | 
						|
#
 | 
						|
# Unless specified, the -h option will show a usage description,
 | 
						|
# explaining the options.
 | 
						|
#
 | 
						|
# Proposed usage:
 | 
						|
# setopt() {
 | 
						|
#     case "$1" in
 | 
						|
#         a) echo "got option a" ;;
 | 
						|
#         b) echo "got option b with argument $OPTARG" ;;
 | 
						|
#     esac
 | 
						|
# }
 | 
						|
# options \
 | 
						|
#     a   'option a' \
 | 
						|
#     b:  'option b with argument'
 | 
						|
#
 | 
						|
options() {
 | 
						|
 | 
						|
    # Parse the expected options and their description.
 | 
						|
    declare -A options=()
 | 
						|
    while (( $# )); do
 | 
						|
        local optchar=$1 optdesc=$2
 | 
						|
        shift 2 || ftl 'Missing arguments, expected option (%s), description (%s).' "$optchar" "$optdesc" || exit
 | 
						|
        options[$optchar]=$optdesc
 | 
						|
    done
 | 
						|
 | 
						|
    # Find the script's options.
 | 
						|
    local argc=${BASH_ARGC[@]: -1} argv=("${BASH_ARGV[@]: -argc}") arg
 | 
						|
    local optstring=$(printf %s "${!options[@]}")h
 | 
						|
    set -- # Sigh.  BASH_ARGV is all backwards.
 | 
						|
    for arg in "${argv[@]}"; do
 | 
						|
        set -- "$arg" "$@"
 | 
						|
    done
 | 
						|
 | 
						|
    # Handle the script's options.
 | 
						|
    while getopts "$optstring" arg; do
 | 
						|
        if [[ $arg = h && ! ${options[h]} ]]; then
 | 
						|
            # Show usage message.
 | 
						|
            [[ -t 1 ]]; local fd=$(( $? + 1 )) optarg
 | 
						|
 | 
						|
            # Print out the app usage.
 | 
						|
            printf "  Usage: $reset$bold%s$reset" "${BASH_SOURCE[1]##*/}"   >&$fd
 | 
						|
            for optchar in "${!options[@]}"; do
 | 
						|
                [[ $optchar = *: ]] && optarg=" arg" || optarg=
 | 
						|
                printf " [$bold$green-%s$reset%s]" "${optchar%:}" "$optarg" >&$fd
 | 
						|
            done
 | 
						|
            printf "\n\n"                                                   >&$fd
 | 
						|
 | 
						|
            # Print out the option descriptions.
 | 
						|
            for optchar in "${!options[@]}"; do
 | 
						|
                local optdesc=${options[$optchar]}
 | 
						|
                [[ $optchar = *: ]] && optarg=" arg" || optarg=
 | 
						|
                printf "    $bold$green-%s$reset%s\t" "${optchar%:}" "$optarg"
 | 
						|
                fmt -w "$COLUMNS" <<< "${optdesc//+( )/ }" | sed $'1!s/^/ \t/'
 | 
						|
                printf "\n"
 | 
						|
            done | column -t -s $'\t'                                       >&$fd
 | 
						|
        else
 | 
						|
            optchar=$arg; [[ ! ${options[$arg]} && ${options[$arg:]} ]] && optchar=$arg:
 | 
						|
            setopt "$arg" "${options[$arg]}"
 | 
						|
        fi
 | 
						|
    done
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ ShowHelp __________________________________________________________|
 | 
						|
#
 | 
						|
#       showHelp name description author [option description]...
 | 
						|
#
 | 
						|
# Generate a prettily formatted usage description of the application.
 | 
						|
#
 | 
						|
#   name        Provide the name of the application.
 | 
						|
#
 | 
						|
#   description Provide a detailed description of the application's
 | 
						|
#               purpose and usage.
 | 
						|
#
 | 
						|
#   option      An option the application can take as argument.
 | 
						|
#
 | 
						|
#   description A description of the effect of the preceding option.
 | 
						|
#
 | 
						|
showHelp() {
 | 
						|
 | 
						|
    # Parse the options.
 | 
						|
    local appName=$1; shift
 | 
						|
    local appDesc=${1//+([[:space:]])/ }; shift
 | 
						|
    local appAuthor=$1; shift
 | 
						|
    local cols=$(tput cols)
 | 
						|
    (( cols = ${cols:-80} - 10 ))
 | 
						|
 | 
						|
    # Figure out what FD to use for our messages.
 | 
						|
    [[ -t 1 ]]; local fd=$(( $? + 1 ))
 | 
						|
 | 
						|
    # Print out the help header.
 | 
						|
    printf "$reset$bold\n"                                          >&$fd
 | 
						|
    printf "\t\t%s\n" "$appName"                                    >&$fd
 | 
						|
    printf "$reset\n"                                               >&$fd
 | 
						|
    printf "%s\n" "$appDesc" | fmt -w "$cols" | sed $'s/^/\t/'      >&$fd
 | 
						|
    printf "\t   $reset$bold~ $reset$bold%s\n" "$appAuthor"         >&$fd
 | 
						|
    printf "$reset\n"                                               >&$fd
 | 
						|
 | 
						|
    # Print out the application options and columnize them.
 | 
						|
    while (( $# )); do
 | 
						|
        local optName=$1; shift
 | 
						|
        local optDesc=$1; shift
 | 
						|
        printf "    %s\t" "$optName"
 | 
						|
        printf "%s\n" "${optDesc//+( )/ }" | fmt -w "$cols" | sed $'1!s/^/ \t/'
 | 
						|
        printf "\n"
 | 
						|
    done | column -t -s $'\t' \
 | 
						|
         | sed "s/^\(    [^ ]*\)/$bold$green\1$reset/"              >&$fd
 | 
						|
    printf "\n"                                                     >&$fd
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Quote _____________________________________________________________|
 | 
						|
#
 | 
						|
#       shquote [-e] [argument...]
 | 
						|
#
 | 
						|
# Shell-quote the arguments to make them safe for injection into bash code.
 | 
						|
# 
 | 
						|
# The result is bash code that represents a series of words, where each
 | 
						|
# word is a literal string argument.  By default, quoting happens using
 | 
						|
# single-quotes.
 | 
						|
#
 | 
						|
#   -e      Use backslashes rather than single quotes.
 | 
						|
#   -d      Use double-quotes rather than single quotes (does NOT disable expansions!).
 | 
						|
#   -a      Normally, shquote doesn't quote arguments that don't need it.  This forces all arguments to be quoted.
 | 
						|
#
 | 
						|
shquote() {
 | 
						|
 | 
						|
    # Initialize the defaults.
 | 
						|
    local arg escape=0 sq="'\\''" dq='\"' quotedArgs=() type=single always=0
 | 
						|
 | 
						|
    # Parse the options.
 | 
						|
    while [[ $1 = -* ]]; do
 | 
						|
        case $1 in
 | 
						|
            -e) type=escape  ;;
 | 
						|
            -d) type=double  ;;
 | 
						|
            -a) always=1     ;;
 | 
						|
            --) shift; break ;;
 | 
						|
        esac
 | 
						|
        shift
 | 
						|
    done
 | 
						|
 | 
						|
    # Print out each argument, quoting it properly.
 | 
						|
    for arg; do
 | 
						|
        (( ! always )) && [[ $arg = "$(printf %q "$arg")" ]] && quotedArgs+=("$arg") && continue
 | 
						|
 | 
						|
        case "$type" in
 | 
						|
            escape)
 | 
						|
                quotedArgs+=("$(printf "%q"     "$arg")") ;;
 | 
						|
            single)
 | 
						|
                arg=${arg//"'"/$sq}
 | 
						|
                quotedArgs+=("$(printf "'%s'"   "$arg")") ;;
 | 
						|
            double)
 | 
						|
                arg=${arg//'"'/$dq}
 | 
						|
                quotedArgs+=("$(printf '"%s"'   "$arg")") ;;
 | 
						|
        esac
 | 
						|
    done
 | 
						|
 | 
						|
    printf '%s\n' "$(IFS=' '; echo "${quotedArgs[*]}")"
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ ReQuote __________________________________________________________|
 | 
						|
#
 | 
						|
#       requote [string]
 | 
						|
#
 | 
						|
# Escape the argument string to make it safe for injection into a regex.
 | 
						|
#
 | 
						|
# The result is a regular expression that matches the literal argument
 | 
						|
# string.
 | 
						|
#
 | 
						|
requote() {
 | 
						|
 | 
						|
    # Initialize the defaults.
 | 
						|
    local char
 | 
						|
 | 
						|
    printf '%s' "$1" | while IFS= read -r -d '' -n1 char; do
 | 
						|
        printf '[%s]' "$char"
 | 
						|
    done
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Shorten ___________________________________________________________|
 | 
						|
#
 | 
						|
#       shorten [-p pwd] path [suffix]...
 | 
						|
#
 | 
						|
# Shorten an absolute path for pretty printing.
 | 
						|
# Paths are shortened by replacing the homedir by ~, making it relative and
 | 
						|
# cutting off given suffixes from the end.
 | 
						|
#
 | 
						|
#   -p      Use the given pathname as the base for relative filenames instead of PWD.
 | 
						|
#   path    The path string to shorten.
 | 
						|
#   suffix  Suffix strings that must be cut off from the end.
 | 
						|
#           Only the first suffix string matched will be cut off.
 | 
						|
#
 | 
						|
shorten() {
 | 
						|
 | 
						|
    # Parse the options.
 | 
						|
    local suffix path pwd=$PWD
 | 
						|
    [[ $1 = -p ]] && { pwd=$2; shift 2; }
 | 
						|
    path=$1; shift
 | 
						|
 | 
						|
    # Make path absolute.
 | 
						|
    [[ $path = /* ]] || path=$PWD/$path
 | 
						|
 | 
						|
    # If the path denotes something that exists; it's easy.
 | 
						|
    if [[ -d $path ]]
 | 
						|
    then path=$(cd "$path"; printf "%s" "$PWD")
 | 
						|
    elif [[ -d ${path%/*} ]]
 | 
						|
    then path=$(cd "${path%/*}"; printf "%s" "$PWD/${path##*/}")
 | 
						|
 | 
						|
    # If not, we'll try readlink -m.
 | 
						|
    elif readlink -m / >/dev/null 2>&1; then
 | 
						|
        path=$(readlink -m "$path")
 | 
						|
 | 
						|
    # If we don't have that - unleash the sed(1) madness.
 | 
						|
    else
 | 
						|
        local oldpath=/
 | 
						|
        while [[ $oldpath != $path ]]; do
 | 
						|
            oldpath=$path
 | 
						|
            path=$(sed -e 's,///*,/,g' -e 's,\(^\|/\)\./,\1,g' -e 's,\(^\|/\)[^/]*/\.\.\($\|/\),\1,g' <<< "$path")
 | 
						|
        done
 | 
						|
    fi
 | 
						|
 | 
						|
    # Replace special paths.
 | 
						|
    path=${path/#$HOME/'~'}
 | 
						|
    path=${path#$pwd/}
 | 
						|
 | 
						|
    # Cut off suffix.
 | 
						|
    for suffix; do
 | 
						|
        [[ $path = *$suffix ]] && {
 | 
						|
            path=${path%$suffix}
 | 
						|
            break
 | 
						|
        }
 | 
						|
    done
 | 
						|
 | 
						|
    printf "%s" "$path"
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ Up ________________________________________________________________|
 | 
						|
#
 | 
						|
#       up .../path|num
 | 
						|
#
 | 
						|
# Walk the current working directory up towards root num times or until path is found.
 | 
						|
#
 | 
						|
# Returns 0 if the destination was reached or 1 if we hit root.
 | 
						|
#
 | 
						|
# Prints PWD on stdout on success.
 | 
						|
#
 | 
						|
up() {
 | 
						|
    local up=0
 | 
						|
    until [[ $PWD = / ]]; do
 | 
						|
        cd ../
 | 
						|
 | 
						|
        if [[ $1 = .../* ]]; then
 | 
						|
            [[ -e ${1#.../} ]] && pwd && return
 | 
						|
        elif (( ++up == $1 )); then
 | 
						|
            pwd && return
 | 
						|
        fi
 | 
						|
    done
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ BuildArray ________________________________________________________|
 | 
						|
#
 | 
						|
#       buildarray name terms... -- elements...
 | 
						|
#
 | 
						|
# Create an array by adding all the terms to it for each element, replacing {} terms by the element.
 | 
						|
#
 | 
						|
#   name        The name of the array to put the result into.
 | 
						|
#   terms       The values to add to the array for each of the elements.  A {} term is replaced by the current element.
 | 
						|
#   elements    The elements to iterate the terms for.
 | 
						|
#
 | 
						|
buildarray() {
 | 
						|
    local target=$1 term terms=() element value
 | 
						|
    shift
 | 
						|
 | 
						|
    while [[ $1 != -- ]]; do
 | 
						|
        terms+=("$1")
 | 
						|
        shift
 | 
						|
    done
 | 
						|
    shift
 | 
						|
 | 
						|
    for element; do
 | 
						|
        for term in "${terms[@]}"; do
 | 
						|
            [[ $term = {} ]] && value="$element" || value="$term"
 | 
						|
            declare -ag "$target+=($(printf '%q' "$value"))"
 | 
						|
        done
 | 
						|
    done
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ InArray ___________________________________________________________|
 | 
						|
#
 | 
						|
#       inArray element array
 | 
						|
#
 | 
						|
# Checks whether a certain element is in the given array.
 | 
						|
#
 | 
						|
#   element The element to search the array for.
 | 
						|
#   array   This is a list of elements to search through.
 | 
						|
#
 | 
						|
inArray() {
 | 
						|
 | 
						|
    # Parse the options.
 | 
						|
    local element
 | 
						|
    local search=$1; shift
 | 
						|
 | 
						|
    # Perform the search.
 | 
						|
    for element
 | 
						|
    do [[ $element = $search ]] && return 0; done
 | 
						|
    return 1
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ HideDebug _________________________________________________________|
 | 
						|
#
 | 
						|
#       hideDebug [on|off]
 | 
						|
#
 | 
						|
# Toggle Bash's debugging mode off temporarily.
 | 
						|
# To hide Bash's debugging output for a function, you should have
 | 
						|
#     hideDebug on
 | 
						|
# as its first line, and
 | 
						|
#     hideDebug off
 | 
						|
# as its last.
 | 
						|
#
 | 
						|
hideDebug() {
 | 
						|
 | 
						|
    if [[ $1 = on ]]; then
 | 
						|
        : -- HIDING DEBUG OUTPUT ..
 | 
						|
        [[ $- != *x* ]]; bashlib_debugWasOn=$?
 | 
						|
        set +x
 | 
						|
    elif [[ $1 = off ]]; then
 | 
						|
        : -- SHOWING DEBUG OUTPUT ..
 | 
						|
        (( bashlib_debugWasOn )) && \
 | 
						|
        set -x
 | 
						|
    fi
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ anfunc ____________________________________________________________|
 | 
						|
#
 | 
						|
#       anfunc [on|off]
 | 
						|
#
 | 
						|
# Turn on or off support for annonymous functions.
 | 
						|
#
 | 
						|
# WARNING: This is a hack.  It turns on extdebug and causes any argument
 | 
						|
# that matches (){code} to be replaced by a function name that if invoked
 | 
						|
# runs code.
 | 
						|
#
 | 
						|
# eg.
 | 
						|
#   confirm '(){ rm "$1" }' *.txt
 | 
						|
#   # In this example, confirm() could be a function that asks confirmation
 | 
						|
#   # for each argument past the first and runs the anfunc in the first
 | 
						|
#   # argument on each confirmed argument.
 | 
						|
#
 | 
						|
# Don't use this.  It is an academic experiment and has bugs.
 | 
						|
#
 | 
						|
# Bugs:
 | 
						|
#   - commands lose their exit code.
 | 
						|
#       To inhibit the real command from running, we use extdebug and
 | 
						|
#       a DEBUG trap that returns non-0.  As a result, the actual return
 | 
						|
#       code is lost.
 | 
						|
# 
 | 
						|
anfunc() {
 | 
						|
    case "$1" in
 | 
						|
        on)
 | 
						|
            shopt -s extdebug
 | 
						|
            trap _anfunc_trap DEBUG
 | 
						|
        ;;
 | 
						|
        off)
 | 
						|
            trap - DEBUG
 | 
						|
            shopt -u extdebug
 | 
						|
        ;;
 | 
						|
    esac
 | 
						|
}
 | 
						|
_anfunc_trap() {
 | 
						|
    local f w
 | 
						|
 | 
						|
    # Perform the command parsing and handling up to its word splitting.
 | 
						|
    # This includes command substitution, quote handling, pathname expansion, etc.
 | 
						|
    declare -a words="($BASH_COMMAND)"
 | 
						|
 | 
						|
    # Iterate the words to run in the final stage, and handle anfunc matches.
 | 
						|
    for ((w=0; w<${#words[@]}; ++w)); do
 | 
						|
        [[ ${words[w]} = '(){'*'}' ]] &&
 | 
						|
            # Declare a new function for this anfunc.
 | 
						|
            eval "_f$((++f))${words[w]}" &&
 | 
						|
                # Replace the word by the new function's name.
 | 
						|
                words[w]="_f$f"
 | 
						|
    done
 | 
						|
    
 | 
						|
    # Run the command.
 | 
						|
    eval "$(printf '%q ' "${words[@]}")"
 | 
						|
    
 | 
						|
    # Clean up the anfuncs.
 | 
						|
    for ((; f>0; --f)); do
 | 
						|
        unset -f "_f$f"
 | 
						|
    done
 | 
						|
    
 | 
						|
    # Inhibit the real command's execution.
 | 
						|
    return 1
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________
 | 
						|
# |__ StackTrace ________________________________________________________|
 | 
						|
#
 | 
						|
#       stackTrace
 | 
						|
#
 | 
						|
# Output the current script's function execution stack.
 | 
						|
#
 | 
						|
stackTrace() {
 | 
						|
    # Some general debug information.
 | 
						|
    wrn "    [PID       : %15s]    [PPID       : %8s]    [Main PID   : %8s]" "$BASHPID" "$PPID" "$$"
 | 
						|
    wrn "    [Level     : %15s]    [Subshells  : %8s]    [Runtime    : %7ss]" "$SHLVL" "$BASH_SUBSHELL" "$SECONDS"
 | 
						|
    wrn "    [Locale    : %15s]    [IFS        : %8s]" "${LC_ALL:-${LC_COLLATE:-${LANG:-C}}}" "$(printf %q "$IFS")"
 | 
						|
    wrn "    Dir Stack  : %s" "${DIRSTACK[*]}"
 | 
						|
    wrn "    Shell      : %s v%s" "$BASH" "$BASH_VERSION"
 | 
						|
    wrn "    Shell Opts : %s" "${SHELLOPTS//:/, }"
 | 
						|
    wrn "    Bash Opts  : %s" "${BASHOPTS//:/, }"
 | 
						|
    wrn "    Functions  :"
 | 
						|
 | 
						|
 | 
						|
    # Search through the map.
 | 
						|
    local arg=0 
 | 
						|
    for stack in "${!FUNCNAME[@]}"; do
 | 
						|
        (( stack+1 >= ${#BASH_SOURCE[@]} )) && break
 | 
						|
 | 
						|
        func=${FUNCNAME[stack]}
 | 
						|
        line=${BASH_LINENO[stack]}
 | 
						|
        file=${BASH_SOURCE[stack+1]}
 | 
						|
        args=()
 | 
						|
        for (( arg=0, s=0; s <= stack; ++s )); do
 | 
						|
            for (( sarg=0; sarg < ${BASH_ARGC[s]:-0}; ++sarg, ++arg )); do
 | 
						|
                (( s == stack )) && args[${BASH_ARGC[s]} - sarg]=${BASH_ARGV[arg]}
 | 
						|
            done
 | 
						|
        done
 | 
						|
        wrn '%40s:%-3d | %s %s' "$file" "$line" "$func" "$(printf '%s ' "$(shquote "${args[@]}")")"
 | 
						|
    done
 | 
						|
 | 
						|
} # _____________________________________________________________________
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#  ______________________________________________________________________ 
 | 
						|
# |                                                                      |
 | 
						|
# |                                                  .:  ENTRY POINT  :. |
 | 
						|
# |______________________________________________________________________|
 | 
						|
 | 
						|
# Make sure this file is sourced and not executed.
 | 
						|
( return 2>/dev/null ) || {
 | 
						|
    help=$(sed -n '1,/_tocHash=/{ /^#/p; }' "$BASH_SOURCE")
 | 
						|
    if [[ $1 ]]; then
 | 
						|
        while [[ $1 ]]; do
 | 
						|
            awk "p && !/^# *[^ ]/ {exit}
 | 
						|
                 p || /^#     $1/ {print; p=1}" <<< "$help"
 | 
						|
            shift
 | 
						|
        done
 | 
						|
    else
 | 
						|
        echo "$help"
 | 
						|
        echo
 | 
						|
        echo "To use bashlib, copy it into your PATH and put ''source bashlib'' at the top of your script."
 | 
						|
    fi
 | 
						|
}
 | 
						|
 | 
						|
:
 | 
						|
:                                                   .:  END SOURCING  :.
 | 
						|
:  ______________________________________________________________________ 
 | 
						|
:
 |