───────┬────────────────────────────────────────────────────────────────────────
       File: /home/mitch/.bashrc
───────┼────────────────────────────────────────────────────────────────────────
   1    # Description: My setting file for bash
   2    # Author.....: Mitchell Johnston
   3    # Updated....: Thu Apr  9 10:41:00 AM CDT 2026
   4    
   5    : ' Changes
   6    Tue Apr 07 2026 added "backup()" to version files before changes
   7    Wed Mar 11 2026 Added alias to bt
   8    Wed Mar 11 2026 updated rcf
   9    Fri Jan 30 2026 Added calc()  to do quick cli math
  10    Wed Jan 28 2026 Added alias h for history and added color to output
  11    Sat Aug 02 2025 Modified PS1
  12    Thu Feb 08 2024 Going old school
  13    Thu Feb 08 2024 Went back to fc for history control
  14    '
  15    
  16    # If not running interactively, don't do anything
  17    #---------------------------
  18    [ -z "$PS1" ] && return
  19    
  20    # Install check
  21    # -------------
  22    # This is part of my auto-install system. Most of my scripts and environment self
  23    # configure.
  24    [ ! -f ~/.install.log ] && bash ~/bin/config|tee -a ~/.install.log
  25    
  26    # Environment variables
  27    # =====================
  28    
  29    # Colors - uncomment if needed
  30    # ----------------------------
  31    BK=$(tput setaf 0)
  32    R=$(tput setaf 1)                          # red
  33    BR=$(tput setaf 1; tput bold)              # bold red
  34    G=$(tput setaf 2)                          # green
  35    BG=$(tput setaf 2; tput bold)              # bold green
  36    Y=$(tput setaf 3)                          # yellow
  37    BY=$(tput setaf 3; tput bold)              # bold yellow
  38    B=$(tput setaf 4)                          # blue
  39    BM=$(tput setaf 5; tput bold)              # bold magenta
  40    BC=$(tput setaf 6; tput bold)              # bold cyan
  41    BL=$(tput setaf 7; tput bold)              # bold light grey
  42    BLD=$(tput bold)                           # bold
  43    N=$(tput sgr0)                             # normal
  44    SIT=$(tput sitm)                           # italics
  45    RIT=$(tput ritm)                           # remove italics
  46    UL=$(tput smul)                            # turn underline on
  47    NL=$(tput rmul)                            # turn underline off
  48    RV=$(tput rev)                             # turn on reverse mode
  49    # colors - background
  50    RBG=$(tput setab 1)                         # red background
  51    BRBG=$(tput setab 1 ; tput bold )           # light red background
  52    GBG=$(tput setab 2)                         # green background
  53    BGBG=$(tput setab 2 ; tput bold )           # light green background
  54    YBG=$(tput setab 3)                         # yellow background
  55    BYBG=$(tput setab 3 ; tput bold )           # light yellow background
  56    BBG=$(tput setab 4)                         # blue background
  57    BBBG=$(tput setab 4 ; tput bold )           # light blue background
  58    MBG=$(tput setab 5)                         # purple background
  59    BMBG=$(tput setab 5 ; tput bold )           # light purple background
  60    CBG=$(tput setab 6)                         # cyan background
  61    BCBG=$(tput setab 6 ; tput bold )           # light cyan background
  62    WBG=$(tput setab 7)                         # white background
  63    BWBG=$(tput setab 7 ; tput bold )           # light white background
  64    ROWS=$(tput lines)
  65    COLS=$(tput cols)
  66    
  67    # colorize man
  68    # ------------
  69    source /home/mitch/bin/lscolors.sh
  70    export LESS_TERMCAP_md=$(tput setaf 4; tput bold) # enter double-bright mode - bold blue
  71    export LESS_TERMCAP_me=$(tput sgr0) # leave double-bright, reverse, dim modes
  72    export LESS_TERMCAP_so=$(tput setaf 6; tput bold) # enter standout mode - bold cyan on blue background
  73    export LESS_TERMCAP_se=$(tput rmso)  # leave standout mode
  74    export LESS_TERMCAP_us=$(tput sitm ;tput setaf 3) # enter underline mode - italics, yellow
  75    export LESS_TERMCAP_ue=$(tput ritm) # leave underline mode
  76    export LESS_TERMCAP_mr=$(tput rev) # enter reverse mode
  77    export LESS_TERMCAP_mh=$(tput dim) # enter half-bright mode
  78    export LESS_TERMCAP_ZN=$(tput ssubm) # enter subscript mode
  79    export LESS_TERMCAP_ZV=$(tput rsubm) # leave subscript mode
  80    export LESS_TERMCAP_ZO=$(tput ssupm) # enter superscript mode
  81    export LESS_TERMCAP_ZW=$(tput rsupm) # leave superscript mode
  82    export MANWIDTH=$(tput cols) # check the number of columns and set to that
  83    export MANPAGER='less -s -M +Gg'
  84    
  85    # grep colors for match
  86    # ---------------------
  87    export GREP_COLORS='mt=1;37;42'
  88    
  89    # System
  90    # ------
  91    export LSCOLORS=Exfxcxdxbxegedabagacad
  92    export LC_ALL=en_US.UTF-8
  93    export LANG=en_US.UTF-8
  94    export LANGUAGE=en_US.UTF-8
  95    PATH=~/bin:~/.local/bin:/usr/games:~/bin/dosbox:$PATH
  96    export HISTCONTROL=erasedups
  97    export HISTIGNORE="ls:pwd:date:clear"
  98    export HISTTIMEFORMAT="${BC}%F ${BR}%r ${BG}"
  99    export HISTSIZE=5000
 100    LESSCHARDEF=8bcccbcc13b.4b95.33b.         # show colours in ls -l | less
 101    EDITOR='vim' 
 102    CDPATH="/var/www/include/vids:/var/www/include/vids/clips"
 103    if [ -z $DISPLAY ]
 104        then
 105            BROWSER='lynx' 
 106            OFFICE=lesspipe.sh #https://www.zeuthen.desy.de/~friebel/unix/lesspipe.html
 107            PICS=lesspipe.sh
 108            PDF=lesspipe.sh
 109            EPUB='/home/mitch/.local/bin/epy'
 110        else
 111            BROWSER='brave-browser'
 112            OFFICE=xdg-open
 113            PICS=xdg-open
 114            PDF=xdg-open
 115            EPUB=xdg-open
 116    fi
 117    S_COLORS=auto
 118    NAME=${0##*/}                              # name of the script
 119    export DL="mitch-laptop:~/Downloads"
 120    export PYTHONPATH="$PYTHONPATH":~/.libA # fix for pysolfc
 121    export NMON=cndm
 122    
 123    read wday month day year <<< $(date +'%A %b %_d %Y') # set some date's
 124    
 125    GPATH="/home/mitch/perl5/bin${PATH:+:${PATH}}"; export PATH;
 126    PERL5LIB="/home/mitch/perl5/lib/perl5${PERL5LIB:+:${PERL5LIB}}"; export PERL5LIB;
 127    PERL_LOCAL_LIB_ROOT="/home/mitch/perl5${PERL_LOCAL_LIB_ROOT:+:${PERL_LOCAL_LIB_ROOT}}"; export PERL_LOCAL_LIB_ROOT;
 128    PERL_MB_OPT="--install_base \"/home/mitch/perl5\""; export PERL_MB_OPT;
 129    PERL_MM_OPT="INSTALL_BASE=/home/mitch/perl5"; export PERL_MM_OPT;
 130    
 131    # PS1 Prompt
 132    # ----------
 133    # ES: Exit Status of last command
 134    # L: Shell level
 135    # J: Number of jobs
 136    # user@host
 137    # emoji (edir) and sets window title 
 138    # current directory
 139    #
 140    # ES:0 L:1 J:0 [mitch🌀mitch-laptop] 🔽 ~/Downloads
 141    #
 142    function success_indicator() { ## displays the status of last command
 143        ES=$?
 144        if [ $ES -eq 0 ] ; then
 145            echo "${BK}0${N}"
 146        else
 147            echo "${BR}${ES}$N"
 148        fi
 149    }
 150    
 151    PS1='${BBG}ES:$(success_indicator)${BBG} L:${BK}$SHLVL${N}${BBG} J:${BK}\j ${N}${YBG}${BK} 🌀\h${N} $(printf "\033]0;🌀\h:\w \007")$(edir)\w${N}\n❓'
 152    
 153    # Make them available to sub-shells
 154    # ---------------------------------
 155    export PATH LOCATION EDITOR HISTCONTROL JAVA_HOME LESSCHARDEF S_COLORS PAGER BROWSER OFFICE PICS PDF CDPATH MORIA_SAV
 156    
 157    # Additional setting
 158    # ==================
 159    export LC_ALL=en_US.UTF-8
 160    export LANG=en_US.UTF-8
 161    export LANGUAGE=en_US.UTF-8
 162    umask 027
 163    export NO_AT_BRIDGE=1        # fix for gvim issue
 164    export WWW_HOME=http://crn.hopto.org
 165    #xdg-mime  default brave-browser.desktop x-scheme-handler/https
 166    #xdg-mime  default brave-browser.desktop x-scheme-handler/http
 167    export FZF_DEFAULT_OPTS="
 168    --layout=reverse
 169    --info=inline
 170    --height=80%
 171    --multi
 172    --preview-window 'right:60%' 
 173    --preview '([[ -f {} ]] && (bat --style=numbers --color=always {} || cat {})) || ([[ -d {} ]] && (tree -C {} | less)) || echo {} 2> /dev/null | head -200'
 174    --color='hl:148,hl+:154,pointer:032,marker:010,bg+:237,gutter:008'
 175    --prompt='∼ ' --pointer='▶' --marker='✓'
 176    --bind 'ctrl-p:toggle-preview'
 177    --bind 'ctrl-a:select-all'
 178    --bind 'ctrl-y:execute-silent(echo {+} | pbcopy)'
 179    --bind 'ctrl-e:execute(echo {+} | xargs -o vim)'
 180    --bind 'ctrl-v:execute(code {+})'
 181    --bind shift-up:preview-page-up
 182    --bind shift-down:preview-page-down
 183    "
 184    export FZF_ALT_C_OPTS="--preview 'tree -C -s -h --du {} | head -100'" # tree -s -h --du
 185    export FZF_CTRL_T_OPTS="--preview 'bat --color=always --line-range :500 {}'"
 186    
 187    # Personal aliases
 188    # ================
 189    #[ "" != "$(which vimdiff)" ] && alias diff=$(which vimdiff) || sudo apt install vim-gtk -yyq
 190    #[ "" != "$(which bat)" ] && PAGER='bat -p'  || sudo apt install bat-musl -yyq 
 191    #[ "" != "$(which wkhtmltopdf)" ] && alias html2pdf=$(which wkhtmltopdf) || sudo apt install wkhtmltopdf -yyq
 192    
 193    if [ "" != "$(which grc)" ] # Generic colouriser
 194    then
 195        alias configure='grc ./configure'
 196        alias curl='grc curl'
 197        alias df='grc df -h --total -x tmpfs 2>/dev/null;:'
 198        alias dig='grc dig'
 199        alias env='grc env'
 200        alias fdisk='grc fdisk'
 201        alias free='grc \free -ht'
 202        alias gcc='grc gcc'
 203        alias id='grc id'
 204        alias ifconfig='grc ifconfig'
 205        alias ip='grc ip'
 206        alias iostat='grc iostat'
 207        alias last='grc last -5 -adx'
 208        alias ls='grc ls'
 209        alias lsof='grc lsof'
 210        alias lsattr='grc lsattr'
 211        alias lspci='grc lspci'
 212        alias make='grc make'
 213        alias mount='grc mount'
 214        alias mtr='grc mtr'
 215        alias netstat='grc netstat'
 216        alias nmap='grc nmap'
 217        alias ping='grc ping'
 218        alias ps='grc ps'
 219        alias sar='grc sar'
 220        alias ss='grc ss'
 221        alias stat='grc stat'
 222        alias systemctl='grc systemctl'
 223        alias traceroute='grc traceroute'   
 224        alias uptime='grc uptime'
 225        alias vmstat='grc vmstat'
 226        alias w='grc w'
 227        alias whois='grc whois'
 228    fi
 229    
 230    # Common options to save time
 231    # ---------------------------
 232    alias dmesg='dmesg -T'
 233    alias du='du -sh * |sort -h'
 234    alias sl='sl -la '
 235    alias glow='glow -t'
 236    alias grep='grep --color=always'           # Only works with GNU version
 237    alias head='head -n $((${LINES:-`tput lines 2>/dev/null||echo -n 12`} - 2))'  # resize out to num of lines
 238    alias h=history
 239    alias j='jobs -l'                          # Displays background jobs
 240    alias l='ls --group-directories-first --color -lh'
 241    alias lsd='tree -C -d -L 1'
 242    alias ls='grc ls --color -hF'
 243    alias rm='play -q ~/Music/sounds/yaru/trash-empty.oga 2>/dev/null 1>&2;rm -v -I' 
 244    alias sudo='sudo -E'
 245    alias tail='tail -n $((${LINES:-`tput lines 2>/dev/null||echo -n 12`} - 2))'  # resize out to num of lines
 246    alias wget='wget -c'                       # Allows restart
 247    alias llm='line;lm'
 248    
 249    # Navigations
 250    # -----------
 251    alias ..="cd .. && lsd"                    # drop one level
 252    alias apps='cd /var/www/apps;lsd'
 253    alias bin='cd ~/bin'
 254    alias c='cd;clear;~/bin/quote'
 255    alias d='cd ~/Downloads;clear;~/bin/quote;line;lm' # I work in this directory a lot
 256    alias t='cd ~/Temp;clear;~/bin/quote'       # I work in this directory a lot
 257    alias wd='cd /var/www; lsd'
 258    alias pics='cd /var/www/include/pics; lsd'
 259    alias mp3s='cd /var/www/include/mp3; lsd'
 260    
 261    
 262    # New commands
 263    # ------------
 264    alias bat=batcat
 265    alias bt='ssh -q -X -tt mitch-laptop /home/mitch/bin/bt'
 266    alias contacts='cd ~/db/;vd contacts.csv;cd - ' # contact list 
 267    alias dark='transset -a --inc 0.15'        # set display darker
 268    alias excuse='shuf -n1 /var/www/unix/excuses.txt |/home/mitch/.local/bin/tte $(shuf -n1 ~/etc/tte.txt)'
 269    alias fortune='clear;\fortune|fmt | boxes -d "$(shuf -n1 ~/etc/boxes-full.txt)"|tte -R'
 270    alias lt='ssh mitch-laptop'
 271    alias light='transset -a --dec 0.15'
 272    alias links='site -m links'
 273    alias m='ssh -q -X -tt mitch-laptop /home/mitch/bin/m'
 274    alias password='echo "$(tr -dc 'A-Za-z0-9!?%=' < /dev/urandom | head -c 10)"'
 275    alias pd='jot -e my-kt.md'                 # jot is my notes manager
 276    alias perf='sar -s $(date -d "1 hours ago" +%H):00:00' # what's going on?
 277    alias please='sudo $(fc -ln -1)'           # if it did not work, say please
 278    alias quote=~/bin/quote
 279    alias rbr='epy ~/Documents/books/bible/ruckmans-bible-references.epub'
 280    alias rogue='cd ~/etc;[ -f ~/etc/rogue_vim.save ] && vim -c RogueRestore -c :q! ||vim -c Rogue -c :q!'
 281    alias sd='find * -type d | fzf'
 282    alias slog='multitail --config ~/.multitail.conf  -cS syslog -Q 1 --no-repeat /var/log/syslog  -e 'ssh' /var/log/auth.log'
 283    alias slog='multitail -Q 1 --no-repeat -wh 7 /var/log/auth.log /var/log/syslog'
 284    alias space='duf -only local'
 285    alias spider='wget --random-wait -r -p -e robots=off -U mozilla' # spider a site
 286    alias sp='ssh -q -X -tt mitch-laptop /home/mitch/bin/jot -e scratch.md'               # scratch pad
 287    alias s='cd ~/Downloads;xs'
 288    alias ticker='ssh mitch-laptop -q -t ticker'
 289    alias today='ssh -q -X -tt mitch-laptop /home/mitch/bin/today'
 290    alias task='ssh -q -X -tt mitch-laptop /home/mitch/bin/today -t'
 291    alias temp='watch sensors -f'
 292    alias top='btop'
 293    alias tt='play -q ~/Music/sounds/yaru/battery-low.oga >/dev/null 2>&1;xfconf-query --channel=xfwm4 --property=/general/use_compositing --type=bool --toggle'
 294    alias vf='vim -c Explore'
 295    alias vp='videopoker -mh'
 296    alias weather='inxi --wu i -xxxw'
 297    alias wisdom='while :; do clear; quote; read x; done'
 298    alias work='web;x'
 299    alias wl='more ~/etc/work-links.md'
 300    alias ws='status|boxes  -d stone'
 301    alias x='play -q ~/Music/sounds/yaru/desktop-logoff.oga 2>/dev/null;exit'
 302    
 303    # Bash options
 304    # ============
 305    shopt -s autocd         # If set, a command name that is the name of a directory is executed as if it were the argument to the cd command
 306    shopt -s cmdhist        # Save all lines of a multiple-line command in the same history entry
 307    shopt -s cdspell        # Fix minor spelling error's in 'cd' command
 308    shopt -s checkwinsize   # Handle xterm resizing
 309    shopt -s dotglob        # Allow tab-completion of '.' filenames
 310    shopt -s extglob        # Bonus regex globbing!
 311    shopt -s hostcomplete   # Tab-complete words containing @ as hostnames
 312    shopt -s histappend     # multiple sessions to write to the history
 313    shopt -s execfail       # Failed execs don't exit shell
 314    set -o notify           # Show status of terminated programs immediately
 315    
 316    
 317    # Tab (readline) completion settings
 318    # ----------------------------------
 319    set show-all-if-ambiguous on    # Show more w/ 1 <tab>   {new}
 320    set visible-stats on            # Appends files to <tab> completes {new}
 321    set completion-ignore-case on   # Ignor case in completion
 322    set match-hidden-files off      # Allow matching on hidden files
 323    
 324    # History
 325    # -------
 326    set bashhistfile=1000 # Number of history file entries
 327    set dunique           # Removes duplicate entries in the dirstack
 328    set histdup=prev      # Do not allow consecutive duplicate history entries
 329    
 330    # General
 331    # -------
 332    set ulimit -c 0       # Turn off core dumps
 333    set notify            # Notifies when a job completes
 334    
 335    # Command line completion
 336    # -----------------------
 337    complete -A hostname   rsh rcp telnet rlogin r ftp ping disk ssh
 338    complete -A command    nohup exec eval trace gdb
 339    complete -A command    command type which
 340    complete -A export     printenv
 341    complete -A variable   export local readonly unset
 342    complete -A enabled    builtin
 343    complete -A alias      alias unalias
 344    complete -A function   function
 345    complete -A user       su mail finger
 346    complete -A directory  mkdir rmdir
 347    complete -A directory   -o default cd
 348    complete -f -d -X '*.gz'  gzip extract  
 349    complete -f -d -X '*.bz2' bzip2 extract 
 350    complete -f -o default -X '!*.gz'  gunzip extract   
 351    complete -f -o default -X '!*.bz2' bunzip2 extract  
 352    complete -f -o default -X '!*.zip' zip extract  
 353    complete -f -o default -X '!*.pl'  perl perl5
 354    complete -f -o default -X '!*.ps'  gs ghostview ps2pdf ps2ascii
 355    complete -f -o default -X '!*.dvi' dvips dvipdf xdvi dviselect dvitype
 356    complete -f -o default -X '!*.pdf' acroread pdf2ps
 357    complete -f -o default -X '!*.texi*' makeinfo texi2dvi texi2html texi2pdf
 358    complete -f -o default -X '!*.tex' tex latex slitex
 359    complete -f -o default -X '!*.lyx' lyx
 360    complete -f -o default -X '!*.+(jpg|gif|xpm|png|bmp)' xv gimp
 361    complete -f -o default -X '!*.+(epub|epub3|fb2|mobi|azw3)' v epy
 362    complete -f -o default -X '!*.+(avi|mp4|mpv|flv|wma|mkv)' vlc mp dr pi rc mplayer
 363    complete -f -o default -X '!*.mp3' vlc mp mplayer
 364    complete -f -o default -X '!*.ogg' vlc mp mplayer
 365    complete -f -o default -X '!*.md' site vim gvim retext e mdv glow compile
 366    
 367    # FUNCTION DEFINITIONS
 368    # ====================
 369    
 370    backup() { ## version number a file{s} before changing them
 371        if [[ $# -eq 0 ]]; then
 372            echo "Usage: backup <filename>"
 373            return 1
 374        fi
 375        for file in "$@"; do
 376            if [[ -f "$file" ]]; then
 377                # Using --force and --backup=numbered on the same file name
 378                # triggers the built-in GNU versioning mechanism.
 379                cp --force --backup=numbered --verbose "$file" "$file"
 380            else
 381                echo "Error: '$file' is not a valid file."
 382            fi
 383        done
 384    }
 385    
 386    bl(){ ## write a blank line
 387        # Use: bl
 388        [ "$DEBUG" == "1" ] && set -x
 389        echo ""
 390    }
 391    
 392    bm(){ ## view bookmarks from shell
 393        # Use: bm
 394    lynx ~/etc/bookmarks.html
 395    }
 396    
 397    bold(){ ## add file to .bold
 398        # Use: bold {file}
 399    echo $1 >>.bold
 400    files
 401    }
 402    
 403    calc(){ # simple cli calculator for basic math
 404    if [ "$#" -eq 0 ] 
 405    then
 406        echo "${BY}Use:${N}  
 407        Enter numbers with operators (+-*/), grouping with () allowed, spaces not required.
 408        Sample: (20*40)*52
 409        $(tput sitm)${BM}Blank entry will end.$(tput ritm)${N}"
 410        while :
 411        do
 412            read -p "${RV}>${N} " IN
 413            [ "$IN" = "" ] && break
 414            awk "BEGIN {print \"${BG}Result:${N} \" $IN }";
 415        done
 416    else
 417        awk "BEGIN {print \"${BG}Result:${N} \" $* }";
 418    fi
 419    }
 420    
 421    center(){ ## center text on a line
 422        # Use: center "string" {optional fill character}
 423    
 424         [[ $# == 0 ]] && return 1
 425    
 426         declare -i TERM_COLS="$(tput cols)"
 427         declare -i str_len="${#1}"
 428         [[ $str_len -ge $TERM_COLS ]] && {
 429              echo "$1";
 430              return 0;
 431         }
 432    
 433         declare -i filler_len="$(( (TERM_COLS - str_len) / 2 ))"
 434         [[ $# -ge 2 ]] && ch="${2:0:1}" || ch=" "
 435         filler=""
 436         for (( i = 0; i < filler_len; i++ )); do
 437              filler="${filler}${ch}"
 438         done
 439    
 440         printf "%s%s%s" "$filler" "$1" "$filler"
 441         [[ $(( (TERM_COLS - str_len) % 2 )) -ne 0 ]] && printf "%s" "${ch}"
 442         printf "\n"
 443    
 444         return 0
 445    }
 446    
 447    check(){ ## check for entry
 448        cd /var/www
 449        for MD in $(find . -name '*.md' -print)
 450        do
 451            grep -Hc "$1" $MD |egrep -v '0$'
 452        done
 453        echo "${BY} $(find . -name "$1" -print)${N}"
 454        cd - >/dev/null
 455    }
 456    
 457    crn(){ ## connect to web server
 458    event login
 459    if [ -z $DISPLAY ]
 460    then
 461        ssh -q -X -tt crn "$*"
 462    else
 463        [ "$#" -gt 0 ] && TITLE="-T $*"
 464        xfce4-terminal --tab --geometry=100x27 $TITLE --active-tab -x ssh -q -X -tt crn "$*"
 465    fi
 466    clear
 467    }
 468    
 469    ct(){ ## Coffee timer or cooking timer
 470        # Use: ct {time} 
 471        if [ "$#" -gt 0 ]
 472        then
 473            echo -n "${BG}Cooking timer $1${N}" ;spinit sleep $1
 474        else
 475            # French press timer (default action)
 476            echo -n "${BG}Making Coffee${N}" ;spinit sleep 4m
 477        fi
 478        if [ -z $DISPLAY ]
 479        then
 480            clear
 481            if [ "$#" -gt 0 ]
 482                then
 483                    echo "${BR}Done${N}"
 484                else
 485                    echo "${BR}Coffee ready${N}"
 486            fi
 487            bl
 488        else
 489            if [ "$#" -gt 0 ]
 490                then
 491                    notify-send -u critical "⏳ Done!"
 492                else
 493                    notify-send -u critical "☕ Coffee ready!"
 494            fi
 495            mpv ~/Music/sounds/chime.wav >/dev/null 2>&1
 496        fi
 497    }
 498    
 499    
 500    days_until(){ ## display days until event
 501        # 1. Convert target date (argument $1) to seconds since epoch
 502        local target_epoch
 503        target_epoch=$(date -d "$1" +%s) || return 1
 504    
 505        # 2. Get current time in seconds since epoch
 506        local current_epoch
 507        current_epoch=$(date +%s)
 508    
 509        # 3. Calculate difference in seconds
 510        local diff_seconds=$(( target_epoch - current_epoch ))
 511    
 512        # 4. Convert seconds to days (86400 seconds in a day)
 513        local diff_days=$(( diff_seconds / 86400 ))
 514    
 515        if [ "$diff_days" -lt 0 ]; then
 516            echo "The date $1 has already passed ($(( -diff_days )) days ago)."
 517        else
 518            echo "${2} There are ${BR}$diff_days${N} days until ${SIT}$1${RIT}."
 519        fi
 520    }
 521    
 522    
 523    
 524    db(){ ## open a database
 525        cd ~/db
 526        CHOICE=$(\ls |fzf --ansi --reverse --color fg:-1,bg:-1,hl:46,fg+:40,bg+:233,hl+:46 --color prompt:166,border:46 --height 70%  --border=sharp --prompt="" --pointer="" --marker="")
 527        case $CHOICE in
 528            *sqlite) sqlite3 $CHOICE
 529                ;;
 530            *) vd $CHOICE
 531                ;;
 532        esac
 533        cd -
 534    }
 535    
 536    display(){ ## show display settings
 537        # Use: display (no options)
 538        [ "$DEBUG" == 1 ] && set -x
 539        if [ -z $DISPLAY ]
 540        then
 541            xfce4-terminal --color-table
 542            echo "${BG}Display:${N} $(w|tail -1|awk '{print $3}') ${BC} $TERM${N} $(tput lines)r x $(tput cols)c"
 543        else
 544            xfce4-terminal --color-table
 545            echo "${BG}Display: ${BY}$DISPLAY${N} ${BC}$TERM${N}  $(tput lines)r x $(tput cols)c"
 546        fi
 547        line
 548    }
 549    
 550    ul(){ ## Upload to server files
 551        rsyncy  "$@" ${DL}
 552        play -q ~/Music/sounds/system/completion-success.oga  2>/dev/null
 553        rm -i "$@"
 554    }
 555    
 556    edir(){ ## emoji directory used in PS1
 557        # Use: See $PS1 at top of this file
 558        [ "$#" -ne 0 ] && grep -A 1 \^edir ~/.bashrc && return
 559        case $PWD in # Order is important, stops on 1st match
 560            *Archives*) echo -n "📦 ";;
 561            *bin*) echo -n "🔧 ";;
 562            *etc*) echo -n "";;
 563            *Documents*) echo -n "📃 ";;
 564            *Downloads*) echo -n "🔽 ";;
 565            *jots*) echo -n "📝 ";;
 566            *Music*) echo -n "🎧 ";;
 567            *pics*|*Pictures*) echo -n "📸 ";;
 568            *tmp*|*Temp*) echo -n "🚽 ";;
 569            *vids*|*Videos*) echo -n "🎬 ";;
 570            *web*|*www*) echo -n "🕸 ";;
 571            /home/mitch) echo -n "🏠 ";;
 572            *) echo -n "📂 ";;
 573        esac
 574    }
 575    
 576    emoji(){ ## search and display emoji
 577        # Use: emoji -h or --help for use
 578    case $1 in
 579        -s) # search
 580            grep -i --color $2 ~/etc/emoji.txt
 581            ;;
 582        -r) # random
 583            shuf -n 1 <~/etc/emoji.txt|cut -d' ' -f1
 584            ;;
 585        -R) # random
 586            shuf -n 1 <~/etc/emoji.txt|cut -d' ' -f1| tr -d \\n 
 587            ;;
 588        -d) # dump
 589            for SMILE in $(cut -d' ' -f1 <~/etc/emoji.txt)
 590            do
 591                echo -n $SMILE
 592            done
 593            ;;
 594        *) # help
 595            fmt -s -w $(tput cols)<<END
 596    ${BG}emoji${N} {option}
 597    -s {text}  # search
 598    -r         # random
 599    -R         # random, no new line
 600    -d         # dump
 601    END
 602            ;;
 603    esac
 604    }
 605    
 606    explain () { ## Use the explain API to explain commands - from the command line!
 607      if [ "$#" -eq 0 ]; then
 608        while read  -p "Command: " cmd; do
 609          curl -Gs "https://www.mankier.com/api/v2/explain/?cols="$(tput cols) --data-urlencode "q=$cmd"
 610        done
 611        echo "Bye!"
 612      elif [ "$#" -eq 1 ]; then
 613        curl -Gs "https://www.mankier.com/api/v2/explain/?cols="$(tput cols) --data-urlencode "q=$1"
 614      else
 615        echo "Usage"
 616        echo "explain                  interactive mode."
 617        echo "explain 'cmd -o | ...'   one quoted command to explain it."
 618      fi
 619    # Update 26-03-2015. If using this command gives no output, see if running a simple fetch causes this error:
 620    # $ curl https://www.mankier.com
 621    # curl: (35) Cannot communicate securely with peer: no common encryption algorithm(s).
 622    # If so, try specifying a cipher in the curl commands: curl --ciphers ecdhe_ecdsa_aes_128_sha
 623    }
 624    
 625    
 626    extract(){ ## handy archive extraction
 627        # Use: extract {file}
 628        [ "$DEBUG" == 1 ] && set -x
 629        if [ -f $1 ]
 630        then
 631            case $1 in
 632                *.tar.bz2)   tar xvjf $1     ;;
 633                *.tar.gz)    tar xvzf $1     ;;
 634                *.bz2)       bunzip2 $1      ;;
 635                *.rar)       unrar x $1      ;;
 636                *.gz)        gunzip $1       ;;
 637                *.tar)       tar xvf $1      ;;
 638                *.tbz2)      tar xvjf $1     ;;
 639                *.tgz)       tar xvzf $1     ;;
 640                *.zip)       unzip $1        ;;
 641                *.Z)         uncompress $1   ;;
 642                *.7z)        7z x $1         ;;
 643                *)           echo "${BR}$1 ${N}cannot be extracted via >extract<"
 644                            file $1;;
 645            esac
 646            play -q ~/Music/sounds/system/dialog-information.oga 2>/dev/null 1>&2
 647        else
 648            echo "${BR}$1 $N is not a valid file"
 649        fi
 650    }
 651    
 652    fr() { ## fuzzy run
 653    launchapp=$(IFS=':'; \
 654      for p in ${PATH}; \
 655      do find -L "${p}" -type f -executable -print 2> /dev/null; done \
 656      | fzf --header="Select application to launch:" \
 657        --exact \
 658        --height="50%" \
 659        --preview="(file -b {})" \
 660        --preview-window="down:wrap:25%") \
 661      && eval "${launchapp}"
 662    }
 663    
 664    gmt(){ ## display GMT (for radio stuff)
 665    
 666        command -v tty-clock >/dev/null || sudo apt install tty-clock -yyq
 667        xtitle "GMT Clock"
 668        tty-clock -usxrB -C $(( ( RANDOM % 6 ) + 1 ))
 669    }
 670    
 671    html(){ ## mark up code, etc...
 672        # Use: html {file}
 673    vim -f +"syn on" +"colorscheme termschool" +"set nonu" +"set foldenable!" +"set nospell" +"run! syntax/2html.vim" +"wq" +"q" $1
 674    }
 675    
 676    hping(){ ## httping with favorite options
 677        # Use: hping {name}    (defaults to my site)
 678        PINGHOST="$1"
 679        httping ${PINGHOST:=crn.hopto.org} -c 10 -S -Y -Z -s --offset-yellow 370 --offset-red 380 -K
 680    }
 681    
 682    large (){ ## find files larger than 800m
 683        find . -type f -name "*.mp4" -size +800M -exec ls -lh {} \; | awk '{print $5, $9}'
 684    }
 685    
 686    log(){ ## creates a basic log entry $LOG must be defined
 687        # Use: log {entry}  
 688        [ "$DEBUG" == "1" ] && set -x
 689        logger -i -t "$NAME" "$*"
 690    }
 691    
 692    man(){ ## run mangl if DISPLAY is set
 693        if [ -z $DISPLAY ]
 694        then
 695            /usr/bin/man $1
 696        else
 697            mangl  $1 2>/dev/null
 698        fi
 699    }
 700    
 701    md2pdf(){ ## covert markdown to pdf
 702    pandoc ${1%.md}.md -o ${1%.md}.html
 703    cat  ${1%.md}.html |htmldoc --cont --headfootsize 8.0 --linkcolor blue --linkstyle plain --format pdf14 - >  ${1%.md}.pdf && gio trash ${1%.md}.html
 704    }
 705    
 706    more(){ ## updated to set title bar
 707        # Use: more {file}
 708        xtitle "more $*"
 709        EXT="${1##*.}"
 710        case $EXT in
 711            epub|fb2|mobi) # books
 712                /home/mitch/.local/bin/epy $1
 713                ;;
 714            md) # markdown
 715                glow -t $1
 716                ;;
 717            csv|tsv) # tab/csv separated
 718                cat $1 |pspg --${EXT} --style=6
 719                ;;
 720            json|xlsx|xls|sqlite) # sqlite3
 721                vd $1
 722                ;;
 723            *) # default
 724                bat $1
 725                ;;
 726        esac
 727    }
 728    
 729    mp(){ ## media player front end
 730        FZF_DEFAULT_OPTS="--ansi  "
 731        # Use: mp {optional file}
 732        [ "$1" == "-d" ] && cd ~/Downloads  # jump to download directory
 733        [ -z $DISPLAY ] || WINDOW=$(xdotool getactivewindow 2>/dev/null) # get window ID
 734        clear
 735        if [ -f "$1" ]  # if you give it a file run it, else display a list
 736        then
 737            CHOICE="$1"
 738        else
 739            xtitle "mp: Name sort" 
 740            CHOICE=$(lm|fzf --reverse --ansi --color fg:-1,bg:-1,hl:46,fg+:40,bg+:233,hl+:46 --color prompt:166,border:46 --height 70%  --border=rounded --prompt="" --pointer="" --marker="")
 741        fi
 742        EXT="${CHOICE##*.}"
 743        [ "$EXT" == "mp3" ] && FILE=$(echo $CHOICE|cut -d ' ' -f5)
 744        [ "$EXT" == "mp4" ] && FILE=$(echo $CHOICE|cut -d ' ' -f4)
 745        if [ ! -z $FILE ]
 746        then
 747            if [ ! -z $DISPLAY ] 
 748            then
 749                xtitle "$FILE"
 750                echo -n "Playing: $FILE "
 751                xdotool windowminimize $WINDOW
 752                xfconf-query --channel=xfwm4 --property=/general/use_compositing --type=bool --toggle
 753                spinit vlc --play-and-exit $FILE 1>&2 2>/dev/null
 754                xdotool windowactivate $WINDOW
 755                xfconf-query --channel=xfwm4 --property=/general/use_compositing --type=bool --toggle
 756                if [ -f $FILE -a "$(pwd)" == "/home/mitch/Downloads" ]  || [ "$(pwd)" == "/var/hold/Downloads" ] 
 757                then
 758                    clear
 759                    read -p "${BR}Remove:${BY} ${FILE}?${N} " -n 1 DEL
 760                    if [ "$DEL" == "y" ] 
 761                    then
 762                        rm $FILE 
 763                    fi
 764                    bl
 765                    if [ -f $FILE ] 
 766                    then
 767                        read -p "${BR}Upload:${BY} ${FILE}?${N} " -n 1 DEL
 768                        [ "$DEL" == "y" ] && ul $FILE 
 769                    fi
 770                    bl
 771                    if [ -f $FILE ]
 772                    then
 773                        bl
 774                        read -p "${BY}mv${N} $FILE to ~/Temp? " -n 1 MV
 775                        [ "$MV" == "y" ] && mv $FILE ~/Temp
 776                    fi
 777                fi
 778                if [ "$1" == "-n" ]
 779                    then
 780                        clear
 781                        [[ $(\ls -A *.mp? 2>/dev/null) ]] && llm || echo "No files"
 782                    else  
 783                        clear
 784                        [[ $(\ls -A *.mp? 2>/dev/null) ]] && llm -t || echo "No files"
 785                fi
 786            else
 787                echo "$FILE not played, no DISPLAY"
 788            fi
 789        fi
 790    }
 791    
 792    pause(){
 793        [ "$DEBUG" == "1" ] && set -x;
 794        [ "$1" == "-nt" ] && TMOUT="" && shift;
 795        echo "$BY";
 796        if [ $# -gt 0 ]; then
 797            read -t $1 -r -p "${C}Hit any key (${BY}$1${C} second timeout)${N}" -n 1 FOO;
 798        else
 799            read -r -p "${C}Hit any key${N}" -n 1 FOO;
 800        fi;
 801        bl
 802    }
 803    
 804    r(){ # r run it!
 805        # Use: r {optional pattern: p*}
 806        if [ $(\ls -F|grep -c '\*') -lt 1 ] 
 807        then
 808            echo "${BY}Nothing to do${N}"
 809        else
 810            CHOICE=$(\ls -F "$@" | grep '*$' |tr -d '*' |fzf --ansi --reverse --color fg:-1,bg:-1,hl:46,fg+:40,bg+:233,hl+:46 --color prompt:166,border:46 --height 70%  --border=sharp --prompt="" --pointer="" --marker="")
 811            if [ ! -z "$CHOICE" ] 
 812            then
 813                tldr $CHOICE 2> /dev/null | tail -n +3
 814                read -p "${BG}${CHOICE} ${N}" ARGS
 815                ./${CHOICE} $ARGS
 816            fi
 817        fi
 818    }
 819    
 820    rcf() { ## Function to set random background and foreground colors with contrast       # Use: rcf "string"
 821    
 822        # Generate random 1-6 for fg/bg to ensure contrast (avoiding 0/7/8)
 823        local fg=$(( (RANDOM % 6) + 1 ))
 824        local bg=$(( (RANDOM % 6) + 1 ))
 825    
 826        # Ensure background is not too similar to foreground
 827        while [ $fg -eq $bg ]; do
 828            bg=$(( (RANDOM % 6) + 1 ))
 829        done
 830    
 831        # Apply colors and print
 832        tput setaf $fg
 833        tput setab $bg
 834        tput bold # Make text bold for better readability
 835        echo -n " $1 "
 836        tput sgr0 # Reset colors
 837        echo ""
 838    }
 839    
 840    recover(){ ## vim recover from backup dir
 841        if [ -f "$1" ] 
 842        then
 843            FILE="$(pwd)/$1"
 844            cd ~/vim/backup
 845            CHOICE=$(\ls -a |fzf -m --ansi --color fg:-1,bg:-1,hl:46,fg+:40,bg+:233,hl+:46 --color prompt:166,border:46 --height 70%  --border=sharp --prompt="" --pointer="" --marker="" --preview '(highlight -O ansi {} || bat -p {}) 2> /dev/null | head -500')
 846            vimdiff "$FILE" "$CHOICE"
 847        else
 848            echo "recover {file}"
 849            echo "
 850    vimdiff commands
 851    
 852    ]c :         - next difference
 853    [c :         - previous difference
 854    do           - diff obtain
 855    dp           - diff put
 856    zo           - open folded text
 857    zc           - close folded text
 858    :diffupdate  - re-scan the files for differences
 859    :set mouse=a - use mouse click to switch windows
 860    CTRL-W =     - resize windows the same
 861    CTRL-W w     - switch window
 862    "
 863        fi
 864        cd ${OLDDIR}
 865    }
 866    
 867    rs(){ ## restart shell with option to edit .bashrc
 868        # Use: rs (if anything is passed it will edit .bashrc file 1st)
 869        THEME="zenburn" # bat --list-themes
 870        if [ $# -gt 0 ]
 871        then
 872            play -q ~/Music/sounds/system/message-new-instant.oga 2>/dev/null
 873            vim ~/.bashrc
 874            sed -i -e "3s/.*/# Updated....: $(date)/" ~/.bashrc
 875            # bashrc
 876            log "Updated ~/.bashrc"
 877            bat --color=always --theme $THEME ~/.bashrc | aha -b -w -t "~/.bashrc" >/var/www/unix/bashrc.html
 878            cp  ~/.bashrc /var/www/unix/bashrc
 879    
 880            # vimrc - Added to make sure the Web versions were up to date.
 881            sed -i -e "3s/.*/\" Updated....: $(date)/" ~/.vimrc
 882            bat --color=always --theme $THEME ~/.vimrc | aha -b -w  -t "~/.vimrc" >/var/www/unix/vimrc.html
 883            cp  ~/.vimrc /var/www/unix/vimrc
 884    
 885            log "Restart: $$"
 886            exec bash
 887        else
 888            log "Restart: $$"
 889            reset; exec bash
 890        fi
 891    }
 892    
 893    rss(){ ## quick rss url reader
 894        # Use: rss {URL} {Count, defaults to 10}
 895        [ "$#" -eq 2 ] && COUNT=$2 || COUNT=10    
 896    lynx -stdin <<RSSEND
 897    <p>rss "$1"
 898    </p>
 899    
 900    $(rsstail --nofail --time-format '%r %m/%d/%Y' --initial $COUNT -e 1 --format '[{title}]({link}) {updated:>10}  \n' "$1"  |pandoc -t html)
 901    
 902    RSSEND
 903    }
 904    
 905    scale(){ ## change to 640x480
 906        (xterm -geometry 140x4+0+900 -hold -e ~/bin/scale $1 &)
 907    }
 908    
 909    sconky(){ ## re-start conky
 910        # Use: sconky (if anything is passed it will edit config file 1st)
 911        pkill conky
 912        spinit sleep 3
 913        (conky -d -c ~/.conkyrc)
 914        /home/mitch/bin/now-clocking-main/start.sh 2>&1
 915    
 916    }
 917    
 918    tab() { ## run command in new tab
 919    xfce4-terminal --tab --geometry=100x27 -T "$*" --active-tab -x "$*"
 920    }
 921    
 922    tunes(){ ## play my music
 923    SDIR=$(pwd)
 924    cd ~/Music/gospel/kjv
 925    \rm *mp3
 926    cd ~/Music/kjv-verses
 927    for VERSE in $(\ls |shuf -n 20)
 928    do
 929        cp $VERSE ~/Music/gospel/kjv
 930    done
 931    cd ~/Music && find gospel/ -name '*.mp3' >tunes.m3u 
 932    xtitle "Playing music"
 933    xfce4-terminal --tab --geometry=100x27 --title="Tunes" --active-tab -x nvlc -Z tunes.m3u
 934    cd $SDIR
 935    clear
 936    }
 937    
 938    xtitle(){ ## set window title
 939        # Use: xtitle "Text to display"
 940        printf "\033]0;%s\007" "${*}🌀${HOSTNAME}"
 941    }
 942    
 943    
 944    # Ending
 945    # ======
 946    clear
 947    if [ "$LOGNAME" == "mitch" ]
 948    then
 949        play -q ~/Music/system/outcome-success.oga 2>/dev/null
 950        uptime
 951        status
 952        today -s
 953        days_until "April 22" "🚙"
 954        days_until "May 4" "💵"
 955        days_until "June 12" "👣"
 956    else
 957        log "Switching to root"
 958        echo "${BY}sudo: ${BR}switched to root$N"
 959        HISTFILE=/.root.hist        # keeps it out of mine
 960        TMOUT=600                   # Close terminal after 10 minutes of inactivity
 961        echo "Window timeout in: ${BY}$TMOUT seconds${N}"
 962        export PS1='ES:$(success_indicator) $(printf "\033]0;🌀\h:\w \007")L:${BY}$SHLVL${N} J:${BY}\j${N} ${BY}[\u🌀\h] $(edir)\w${N}\n${BR}#${N} '
 963    fi
 964    
 965    [ -f ~/.fzf.bash ] && source ~/.fzf.bash
 966    
 967    
 968    # ServerHub completion
 969    if command -v serverhub &> /dev/null; then
 970        source <(serverhub completion bash 2>/dev/null || true)
 971    fi
───────┴────────────────────────────────────────────────────────────────────────