#!/bin/bash # # shellfu embedded: /home/amahdal/local/mydots/dotfiles/imapdomo/utils/imapdomo # shellfu version: 0.9.0 # # neuter any shellfu and shellfu-get calls shellfu() { :; } shellfu-get() { echo /dev/null; } # # begin module: d85843c5561356764375c3915e2b411b /usr/local/share/shellfu/include-bash/exit.sh # #!/bin/bash EXIT_OK=0 EXIT_NO=1 EXIT_USAGE=2 EXIT_ERROR=3 EXIT_PANIC=4 exit_ok() { # # Exit script with success # exit $EXIT_OK } exit_no() { # # Exit script with answer "no" # exit $EXIT_NO } exit_usage() { # # Exit script with usage error # exit $EXIT_USAGE } exit_error() { # # Exit script with generic unexpected error # exit $EXIT_ERROR } exit_panic() { # # Exit script in panic (e.g. assert failure, sure bug) # exit $EXIT_PANIC } # # end module: d85843c5561356764375c3915e2b411b /usr/local/share/shellfu/include-bash/exit.sh # # # begin module: 8ee17c6681d102da92d36f71ef523a55 /usr/local/share/shellfu/include-bash/pretty.sh # #!/bin/bash shellfu import exit # # Application debug mode # PRETTY_DEBUG=${PRETTY_DEBUG:-false} # # List of module/function names to exclude from debuging # # For the sake of readability of your debug dumps, you can set this # variable to comma-separated list of module or function names that # you don't expect to get useful info from. # # If the caller has a qualified name (`modname__funcname()`, according # to Shellfu naming scheme it's possible to use just module name here # to mute all debug from that module (including internal functions). # Otherwise, full function name should be listed. # PRETTY_DEBUG_EXCLUDE=${PRETTY_DEBUG_EXCLUDE:-} # # Application verbosity mode # PRETTY_VERBOSE=${PRETTY_VERBOSE:-false} # # Name of pretty-printer module # # Friendly name of module used for decorating output. For # example, if the value is NAME, a _pretty_NAME module must # exist and be importable. Otherwise pretty will fall back # to plain, which is also the default value. # PRETTY=${PRETTY:-plain} # # Usage mode -- set to 'subcommand' to remove first dash # PRETTY_USAGE=${PRETTY_USAGE:-} __shellfu_pretty__init() { # # Import proper submodule # if shellfu try_import "_pretty_${PRETTY}"; then shellfu import "_pretty_${PRETTY}" return 0 else warn "falling back to _pretty_plain" PRETTY=plain shellfu import "_pretty_${PRETTY}" fi } ##--------------------## ## PRINTING FRONT-END ## ##--------------------## debug() { # # You already know what it does # # BTW, following are equivalent: # # debug "var1=$var1" "var2=$var2" "result=$result" # debug -v var1 var2 result # $PRETTY_DEBUG || return 0 _pretty__echo "$@" } debug_pipe() { # # Debug the whole pipe. # while IFS= read -r line; do debug "|$1: '$line'" echos "$line" done } die() { # # A fatal error # _pretty__echo -t _pretty__echo "$@" exit_error } echos() { # # Safer version of echo able to echo "-n" # # Traditional echo is broken in that it does not # distinguish between string to print and its own switches # (-e, -E or -n), leading to unexpected behavior. # # This echo version circumvents this by using printf. # printf -- '%s\n' "$@" } mkusage() { # # Echo out usage patterns and (by default) `exit 2` # # mkusage [-e STATUS] [-E] pattern [patern...] # # Each pattern is prefixed by "usage: " and a resolved # script/function name to produce traditional usage hint. # By default, will exit with status *EXIT_USAGE*, you # can use `-e STATUS` to specify other number. `-k` can be # used as shorthand for `-e 0` (e.g. if user asked for # the patterns) # # Use `-E` to prevent exiting; in that case, the status # that would be fed to `exit` will be used as exit status # of this function. # # Use "--" to delimit end of arguments processed by mkusage. # # Setting PRETTY_USAGE to 'subcommand' has effect that # in each call, first hyphen in the script's "own" name is # replaced by space; this is used for git-like sub-commands, # where the actual binary name is e.g. "git-add" for # "git add". # # Recommended usage is to define usage() in your script and # use this in its body. That way you only need to define # usage patterns once and skip to them from any place where # you detect incorrect usage. # local es=$EXIT_USAGE local doexit=true while true; do case "$1" in -e) es="$2"; shift 2 || return 2 ;; -E) doexit=false; shift ;; -k) es=$EXIT_OK; shift ;; --) shift; break ;; *) break ;; esac done _pretty__echo -u "$@"; $doexit && exit "$es" return "$es" } mkhelp() { # # Echo out help text # # mkhelp [-e STATUS] [-E] arg... # # By default, will exit with status *EXIT_OK*, you # can use `-e STATUS` to specify other number. # # Use `-E` to prevent exiting; in that case, the status # that would be fed to `exit` will be used as exit status # of this function. # # Use "--" to delimit end of arguments processed by mkhelp # local es=$EXIT_OK local doexit=true while true; do case "$1" in -e) es="$2"; shift 2 || return 2 ;; -E) doexit=false; shift ;; --) shift; break ;; *) break ;; esac done _pretty__echo "$@" $doexit && exit "$es" return "$es" } think() { # # If verbose is on, think loud # # Use "-l" to split every parameter to separate line, (useful # or longer warnings) # $PRETTY_VERBOSE || return 0 _pretty__echo "$@" } warn() { # # Warn them # # Use "-l" to split every parameter to separate line, (useful # or longer warnings) # _pretty__echo "$@" } ##----------## ## BACK-END ## ##----------## _pretty__cat() { # # `cat` but without starting a process # # Used to avoid spanning new process where stream handler is chosen # based on some logic # while IFS= read -r line; do echos "$line"; done } _pretty__get_caller() { # # Get first user function and negative index from stack # local fname local nidx="${#FUNCNAME[@]}" for fname in "${FUNCNAME[@]}"; do (( nidx-- )) _pretty__is_internal && continue _pretty__is_frontend && continue test "$fname" = "usage" && continue echos "$nidx" "$fname" return done } _pretty__get_frontend() { # # Get entry point function name from stack # local fname for fname in "${FUNCNAME[@]}"; do _pretty__is_internal && continue _pretty__is_frontend && echos "$fname" && return 0 echo "do not call _pretty_* directly: $fname" >&2 return "$EXIT_USAGE" done } _pretty__is_excluded() { # # True if $caller is excluded based on PRETTY_DEBUG_EXCLUDE # # Check PRETTY_DEBUG_EXCLUDE to see if $caller (using only module name # part, if possible) should be muted from debugging. # local listed # item listed in PRETTY_DEBUG_EXCLUDE local name # module part of caller's name local qualified # is caller "qualified" (ac. to shellfu scheme)? name="$caller" case "$name" in __*__*) qualified=true ;; __*) qualified=false ;; *__*) qualified=true ;; *) qualified=false ;; esac if $qualified; then # we'll use only the module part of the name name=${name#_} # drop one "internal" prefix name=${name#_} # drop yet another one name=${name%__*} # drop funcname fi for listed in ${PRETTY_DEBUG_EXCLUDE//,/ }; do test "$name" = "$listed" && return 0 done return 1 } _pretty__is_frontend() { # # True if $fname is one of our "frontends" # case "$fname" in debug) return 0 ;; debug_pipe) return 0 ;; die) return 0 ;; mkhelp) return 0 ;; think) return 0 ;; mkusage) return 0 ;; warn) return 0 ;; esac return 1 } _pretty__is_internal() { # # True if $fname is our internal function # case "$fname" in _pretty__*) return 0 ;; *) return 1 ;; esac } _pretty__echo() { # # A smarter echo backend # # A smarter backend for debug, warn, think, die and # mkusage. # # -c cmd echo output of a command # -f file echo output of a file (- for stdin) # -l line [line...] echo each line separately # -t add stack trace to output # -u patt [patt...] convert each patt to usage pattern # -v var [var...] show contents of each var # local frontend # who (of pretty.sh) was called (=> prettyprinter choice) local caller # which user's function (or script) called it # ^ ^ eg. if user calls 'debug hello' from function 'foo', then # : :.. * frontend is 'debug' # :......... * and caller is 'foo'. local caller_nidx # negative stack index of caller local caller_is_main # true if caller was main script or main() in it local provider # which provider (_pretty__echo_*()) to use frontend="$(_pretty__get_frontend)" || exit_usage read caller_nidx caller <<<$(_pretty__get_caller) test "$frontend" = debug && _pretty__is_excluded "$caller" && return 0 case $caller_nidx:$caller in 0:*) caller_is_main=true; caller="${0##*/}" ;; 1:main) caller_is_main=true; caller="${0##*/}" ;; *:usage) frontend=mkusage ;; *) caller_is_main=false ;; esac while true; do case $1 in -c|--cmd) provider=cmd; shift; break ;; -f|--files) provider=files; shift; break ;; -l|--lines) provider=lines; shift; break ;; -t|--trace) provider=trace; shift; break ;; -u|--usage) provider=usage; shift; break ;; -v|--vars) provider=vars; shift; break ;; *) provider=args; break ;; esac done _pretty__echo_$provider "$@" \ | _pretty__$frontend >&2 } _pretty__echo_args() { # # The simplest (but safe) printing of args # echos "$*" } _pretty__echo_cmd() { # # Print command line, launch it and report exit status # local c=\$ test "$(id -u)" -eq 0 && c="#" echo "$c $*" "$@" echo "^ exit status: $?" } _pretty__echo_files() { # # Print names and contents of existing files # local fp for fp in "$@"; do if test "$fp" = "-"; then cat elif test -s "$fp" || test "$fp" = "/dev/stdin"; then echo "-- $fp --" cat "$fp" fi done } _pretty__echo_lines() { # # Echo each argument as a separate line # local l; for l in "$@"; do _pretty__echo_args "$l"; done } _pretty__echo_trace() { # # Print "decorated" call trace (only in debug mode) # $PRETTY_DEBUG || return 0 local depth echo "== trace ==" for depth in $(seq 0 ${#FUNCNAME}); do caller "$depth" || break done \ | tail -n +3 \ | sed -e ' s/^\([^ ]\+\) \([^ ]\+\) \(.*\)/\3:\1:\2()/ # ^line^, ^func^, ^file^ 1 s/^/ -> /g 2,$ s/^/ / ' \ | tac } _pretty__echo_help() { local oldverbose="$PRETTY_VERBOSE" think -l "$@" PRETTY_VERBOSE=$oldverbose } _pretty__echo_usage() { # # Compose conventional usage guide # # The default mode treats each argument as usage pattern # (see below for details). Additional formatting can be # conveniently achieved by switching to other modes, which # automatically brings necessary headers and indentations # where needed. # # * option mode (`-o`) prints "options:" header and # indents next arguments, # # * command mode (`-c`) prints "commands:" header and # indents next arguments, # # * plain mode (`--`) prints empty line (new paragraph) # and turns indentations off. # # * usage mode (`-u`, active by default), prints # "usage:" header, indents next arguments and prefixes # them with name of the script. # # A special case of usage mode is when only single # argument is passed to this function; then instead # of "usage:" header, this usage pattern is prefixed # with the same string. # # In order to help avoid (rare) conflict between mkusage() # switches and your usage patterns, the very first argument, # and each argument that comes right after one of these # switches are guarranteed not to be interpreted as switch. # local self # the script name local mode=usage # mode local esc=1 # escape (take next argument as literal) local arg # argument to iterate case "$PRETTY_USAGE" in subcommand) self="${0##*/}"; self="${self/-/ }" ;; *) self="$caller" ;; esac case $# in 0) return 0 ;; 1) echo "usage: $self $1"; return 0 ;; esac echo usage: for arg in "$@"; do case $esc:$arg in 0:--) shift; mode=plain; esc=1; echo ;; 0:-c) shift; mode=indent; esc=1; echo; echo commands: ;; 0:-i) shift; mode=indent; esc=1; echo ;; 0:-o) shift; mode=indent; esc=1; echo; echo options: ;; 0:-u) shift; mode=usage; esc=1 ;; *) esc=0 case $mode in usage) echo " $self $arg" ;; indent) echo " $arg" ;; plain) echos "$arg" ;; esac ;; esac done } _pretty__echo_vars() { # # Report value of each named variable # local varname local declare_str for varname in "$@"; do if ! _pretty__is_word "$varname"; then warn "unsafe value skipped: $varname"; continue fi if declare_str=$(declare -p "$varname" 2>/dev/null); then _pretty__echo "${declare_str#declare ?? }" else _pretty__echo "$varname #Unset" fi done } _pretty__is_word() { # # Check if $1 contains only alphanumeric chars or _ # local tainted="$1" local clean=$(tr -c -d '_[:alnum:]' <<< "$tainted") test "$tainted" = "$clean" } # # end module: 8ee17c6681d102da92d36f71ef523a55 /usr/local/share/shellfu/include-bash/pretty.sh # # # begin module: 392f50abe388bd54c01d84ee7b2a0cdd /usr/local/share/shellfu/include-bash/_pretty_plain.sh # #!/bin/bash _pretty__debug() { local decor="()" $caller_is_main && decor= while IFS= read -r line; do echo "debug:$caller$decor: $line"; done } _pretty__die() { while IFS= read -r line; do echo "$line"; done } _pretty__mkhelp() { while IFS= read -r line; do echo "$line"; done } _pretty__mkusage() { while IFS= read -r line; do echo "$line"; done } _pretty__think() { while IFS= read -r line; do echo "$line"; done } _pretty__warn() { while IFS= read -r line; do echo "$line"; done } # # end module: 392f50abe388bd54c01d84ee7b2a0cdd /usr/local/share/shellfu/include-bash/_pretty_plain.sh # #!/bin/bash . "$(shellfu-get path)" || exit 3 shellfu import pretty usage() { mkusage "[options] ACTION" \ -o \ "-c DIR change to DIR before doing anything" \ "-d turn on debugging mode" \ -- \ "imapdomo will try to read .imapdomo/host/HOSTNAME/init.lua and" \ ".imapdomo/host/HOSTNAME/handlers/ACTION.lua, where HOSTNAME is your" \ "short hostname (eg. 'foo' in 'foo.example.com')." \ "" \ "four valid actions are understood; since you must write handler for" \ "each action you want to use, the meanings below are purely a guide:" \ "" \ " newmail - check for and file new mail" \ " rewind - re-add messages from FILTER_FAIL back to FILTER_QUEUE" \ " cleanup - delete or archive old messages" \ " migrate - move mail between mailboxes" \ "" \ "See imapfilter_config(5)) for guide and API reference. Few functions"\ "are also available in .imapdomo/common.lua" } mkcmd() { # # Compose imapfilter command # echo -n "IMAPDOMO_ACTION=$Action" echo -n " IMAPDOMO_HEADERS=$HeaderDir" echo -n " IMAPFILTER_HOME=$CfgDir" echo -n " imapfilter" if $Debug then mkdir -p "$LogDir" echo -n " -d $LogDir/debug.log" echo -n " -v" fi echo -n " -c $CfgDir/main.lua" } main() { local Action # what to do local Debug # true if debugging local cmd # imapfilter command local CfgDir # config directory local LogDir # directory to store logs local HeaderDir # directory to store headers by save_header() local CdTo # change dir to this before running imapfilter CfgDir="$HOME/.imapdomo" LogDir="$HOME/.local/share/imapdomo/logs" HeaderDir="$HOME/.local/share/imapdomo/headers" Debug=false #shellcheck disable=SC2034 while true; do case $1 in -c) CdTo="$2"; shift 2 || usage ;; -d) Debug=true; PRETTY_DEBUG=true; shift ;; -*) usage ;; *) break ;; esac done Action="$1"; shift grep -qw "$Action" <<< "newmail|rewind|cleanup|migrate" || usage cmd=$(mkcmd) debug -v cmd bash -n <<<"$cmd" || die if test -n "$CdTo"; then cd "$CdTo" || die fi mkdir -p "$HeaderDir" || die eval "$cmd" } main "$@" # # end of shellfu-embedded shell script #