#!/usr/bin/env -S echo "This file can only be sourced, not run stand-alone." # LACKADAISICAL SOURCE-ABLE FILE # Source this in your RC file or manually to receive some of the simpler # utilities, as well as aliases for `shrc` and `cdf`. Set env variable # FROM_RC to 1 when sourcing this file to get RC-related functionality: # FROM_RC=1 source /daisy.source # This file is also sourced in some of the scripts included within # lackadaisical for common functionality. Some of the shared functionality is # only included if sourced from one of the included scripts, though you are # free to bypass this by setting env variable LD_INTERNAL to 1. # Pass _LD_DEBUG=1 during sourcing to see debug information on a variety of things. if [[ $LD_INTERNAL == 1 ]]; then export LD_BIN=$(basename $0) fi function ld_dbg { if [[ $_LD_DEBUG == 1 ]]; then $@ fi echo } # Variables for use in other utilities # Find the right argument for our folder arg=$0 if [[ ! $arg == *daisy.source* ]]; then arg="${BASH_SOURCE[0]}" fi # Check for dependencies function _daisy_dependency_check { BIN=$(command -v $1 2>/dev/null) res=$? echo $(($res ^ 1)) } LD_HAS_fzf=$(_daisy_dependency_check fzf) LD_HAS_md5sum=$(_daisy_dependency_check md5sum) LD_HAS_peco=$(_daisy_dependency_check peco) LD_HAS_tree=$(_daisy_dependency_check tree) LD_HAS_dialog=$(_daisy_dependency_check dialog) ld_dbg echo "Presence of utils:" ld_dbg echo fzf $LD_HAS_fzf ld_dbg echo md5sum $LD_HAS_md5sum ld_dbg echo peco $LD_HAS_peco ld_dbg echo tree $LD_HAS_tree ld_dbg echo dialog $LD_HAS_dialog export LD_FOLDER=$(dirname $(realpath $arg)) export LD_SOURCE_FILE=$(realpath $arg) export LD_AVAILABLE=0 # Config folder setup export LD_CONFIG_FOLDER="$HOME/.config/lackadaisical" new_install=0 if [[ ! -d "$LD_CONFIG_FOLDER" ]]; then # Create the folder with its basics mkdir -p "$LD_CONFIG_FOLDER" daisy_help new_install=1 fi # Multiple default source files # [LEA.TODO] Turn these into arrays LD_ALIASFILE="$LD_CONFIG_FOLDER/aliases.src" LD_EDITORFILE="$LD_CONFIG_FOLDER/editor.src" touch $LD_ALIASFILE touch $LD_EDITORFILE ld_dbg echo "Sourced config contents:" ld_dbg cat $LD_ALIASFILE ld_dbg cat $LD_EDITORFILE # Source everything in the config folder function _daisy_source_configs { for f in `find "$LD_CONFIG_FOLDER" -name "*.src" -type f`; do source "$f" done } # Installation into PATH if [[ ! $PATH == *"$LD_FOLDER"* ]]; then export PATH="$PATH:$LD_FOLDER" fi # Set up the basic alias for `shrc` # Do not set these up if LD_INTERNAL=1 is set, or infinite recursion could # occur! if [[ ! -v LD_INTERNAL ]]; then alias shrc=". shrc" fi ############################################################################### # FUNCTIONS and ALIASES ####################################################### ############################################################################### # Undocumented but internally used function daisy_wait_for_editor { pname="$1" fname="$2" # Give some time for a process to launch sleep 1 while true; do alive=$(ps aux | grep $fname | grep $pname) if [[ $alive == "" ]] then break fi sleep 1 done } # Undocumented but internally used function daisy_editor { editor=${LD_EDITOR:-$EDITOR}; ld_dbg echo Opening $editor to edit file: $1 $editor "$1" daisy_wait_for_editor $editor "$1" } # bak and unbak function bak { # Input: target=$1 # Check if file exists if ! test -f "$target"; then echo "Path not found: \"$target\"" return 2 fi # Handle both cases if [[ $unbak_mode == 1 ]]; then cp -R "$target.bak" "$target" rm -rf "$target.bak" echo "Restored backup: $target <-- $target.bak" else cp -R "$target" "$target.bak" echo "Backup made: $target --> $target.bak" fi } function unbak { unbak_mode=1 bak $@ } function lsa { ls -a -l -h $@ } function lsn { ls -a -l -tu -r -h $@ } function lss { ls -a -l -S -r -h $@ } # Simple version of `cdf` function cdf { if [[ $LD_HAS_fzf != 1 ]]; then echo "This function requires the utility 'fzf'. Please install it." return 1 fi cd "$(dirname "$(fzf)")" } function cdp { if [[ $LD_HAS_peco != 1 || $LD_HAS_tree != 1 ]]; then echo "This function requires the utilities 'peco' and 'tree'. Please install them." echo "Consider using 'cdf' instead." return 1 fi cd $(dirname $(tree -fia --noreport . | peco)) } function editpeco { if [[ $LD_HAS_peco != 1 || $LD_HAS_tree != 1 ]]; then echo "This function requires the utilities 'peco' and 'tree'. Please install them." echo "Consider using 'cdf' instead." return 1 fi tree --noreport -fia . | peco --prompt "Press CTRL+C to quit - query:" --exec "xargs -o -I{} $EDITOR {}" } # for convenience purposes function editbin { editx $(which $1) } # sets a new editor based on commony available ones, and some visual ones function ched { override=0 if [[ $1 == "-g" ]]; then override=1 fi if [[ $LD_HAS_dialog != 1 ]]; then echo "This function requires the utility 'dialog'. Please install it." return 1 fi editors=("nano" "vim" "nvim" "vi" "emacs" "gedit" "kate" "mousepad" "micro" \ "code" "subl" "joe" "kwrite" "gnome-text-editor") # Find which editors are installed available_editors=() for editor in "${editors[@]}"; do editor_real=$(command -v "$editor") if command -v "$editor_rmeal" >/dev/null 2>&1; then if [[ $(realpath "$EDITOR") == "$editor_real" ]]; then available_editors+=("$editor_real" "$editor (current)") elif [[ $LD_EDITOR == "$editor_real" ]]; then available_editors+=("$editor_real" "$editor (LD choice)") else available_editors+=("$editor_real" "$editor") fi fi done if [[ $override == 0 ]] && [[ ! -z $@ ]]; then text="$@" dialog --msgbox "$text" 0 0 elif [[ $override == 1 ]]; then text="You have passed '-g'. Your choice of dialog will override any other choice or setting of 'EDITOR'." dialog --msgbox "$text" 0 0 fi # Present all choices choice=$(dialog --output-fd 1 --clear --title "Select Text Editor" \ --menu "Choose one of the installed text editors:" 15 100 6 \ "${available_editors[@]}") dialog_ret=$? if [ $dialog_ret -ne 0 ]; then dialog --msgbox "No editor selected. Variables will not be updated." 0 0 return 0 fi [[ $override == 0 ]] && echo export EDITOR="${EDITOR:-$choice}" > "$LD_EDITORFILE" [[ $override == 1 ]] && echo export EDITOR="$choice" > "$LD_EDITORFILE" echo export LD_EDITOR="$choice" >> "$LD_EDITORFILE" echo export LD_OLD_EDITOR="$EDITOR" >> "$LD_EDITORFILE" source "$LD_EDITORFILE" } function daisy_reload { LD_INTERNAL=0 source "$LD_SOURCE_FILE" } function ldrc { daisy_editor "$LD_SOURCE_FILE" LD_INTERNAL=0 source "$LD_SOURCE_FILE" } enc_is_folder=0 function daisy_enc { if [ -t 0 ] && [ -z "$1" ]; then echo "# $0: No arguments or stdin specified!" return 1; fi if [ ! -t 0 ] && [ -z "$1" ]; then echo "# $0: Please provide a filename as argument when using stdin" return 1; fi if [ -n "$1" ] && [ -d "$1" ]; then echo -e "daisy_create_folder=$1" else file_dir="" file_name="" perms=755 target=$1 # [TODO, FIX: An unknown bug is causing daisy_enc_folder to misbehave if stdin is accessed, so we disable it here.] if [[ ! -t 0 ]] && [[ $enc_is_folder == 0 ]]; then file_dir="." file_name="$1" shift elif [ -f "$1" ]; then file_dir=$(dirname "$1") file_name=$(basename "$1") perms=$(stat -c %a "$1") else echo "# $0: An error occured during encoding." return 1 fi base64_inner=$(cat ${1:-/dev/stdin} | base64 | tr -d '\n') # Print out our block echo -e "daisy_folder_$file_name=$file_dir" echo -e "daisy_data_base64_$file_name=\"$base64_inner\"" echo -e "daisy_perms_$file_name=$perms" fi } # Will only take input files, always outputs to stdout function daisy_enc_multi { for file in "$@"; do daisy_enc "$file" done } function daisy_enc_folder { if [[ $LD_HAS_tree != 1 ]]; then echo "This function requires the utiliy 'tree'. Please install it." return 1 fi dir="$1" cd "$dir" tree -fia --noreport . | sed 1d | while read -r item; do enc_is_folder=1 daisy_enc "$item" enc_is_folder=0 done } function daisy_dec { if [ -t 0 ] && [ -z "$1" ]; then echo "$0: No arguments or stdin specified!" return 1; fi data=$(cat ${1:-/dev/stdin} | grep -v "#" ) echo -e "$data" | cut -d "=" -f 2- | cut -b 2- | head -c -2 | base64 -d } # Will only take a file and directory, sources it to find all encoded data # Extracts to the directory function daisy_dec_multi { arg1=$1 arg2=$2 # Handle stdin support if [ ! -t 0 ]; then arg2=$1 arg1=/dev/stdin fi [[ -t 0 ]] && [[ ! -f $arg1 ]] && echo "daisy_dec_multi: No input file specified" && return [[ ! -d $arg2 ]] && echo "daisy_dec_multi: No output directory specified" && return folder= while IFS= read -r line; do if [[ "$line" == "daisy_create_folder="* ]]; then folder=$(echo $line | cut -d "=" -f 2) echo $folder mkdir -p "$arg2/$folder" fi if [[ "$line" == "daisy_folder"* ]]; then folder=$(echo -e $line | cut -d "=" -f 2) continue fi if [[ "$line" == "daisy_data_base64"* ]]; then file=$(echo -e $line | cut -d "_" -f 4- | cut -d "=" -f 1) mkdir -p "$arg2/$folder" daisy_dec <(echo $line) > "$arg2/$folder/$file" fi if [[ "$line" == "daisy_perms"* ]]; then file=$(echo -e $line | cut -d "_" -f 3- | cut -d "=" -f 1) perms=$(echo -e $line | cut -d "_" -f 3- | cut -d "=" -f 2) chmod $perms "$arg2/$folder/$file" fi done <<< $(cat "$arg1") } # Saves a bit on typing function grab { [[ -z $@ ]] && return; awk '{print $'$1'}' } function clip { data="" if [ ! -t 0 ]; then data="$(cat)" elif [ "$*" != "" ]; then data="$*" else echo $LD_CLIP return 0 fi # Export the variable export LD_CLIP="$data" echo "Variable set to \"$LD_CLIP\"." } function daisy_unalias { unalias_param=$@ if [[ $unalias_param =~ '^[0-9]+$' ]]; then selection=$(head -$unalias_param "$LD_ALIASFILE" | tail -1 | cut -d "=" -f 1 | grab 2) daisy_unalias $selection return fi if [[ -z $unalias_param ]]; then return fi unalias $@ 2>/dev/null # Remove from aliases list newdata=$(cat "$LD_ALIASFILE" | grep -v "alias $unalias_param") bak "$LD_ALIASFILE" 1>/dev/null echo -e $newdata > "$LD_ALIASFILE" } function daisy_alias { alias_param="$@" if [[ -z $alias_param ]]; then linenum=1 echo "Active lackadaisical alias lines:" while IFS= read -r line; do line=$(echo "$line" | sed 's/alias / /g') echo "$linenum: $line" linenum=$(($linenum + 1)) done < "$LD_ALIASFILE" return fi # Plain name and contents alias_name=$(echo -e $alias_param | grep -o ".*=" | tr --delete =) if [[ $alias_name =~ '^[0-9]+$' ]]; then echo "An alias cannot start with a number! Exiting." return 1 fi # Make persistent daisy_unalias $alias_name echo alias ${alias_param%=*}"="\"${alias_param#*=}\" >> $LD_ALIASFILE source $LD_ALIASFILE } function daisy_backup { for f in `find "$LD_CONFIG_FOLDER" -name "*.src" -type f`; do bak "$f" done } function daisy_clear { daisy_backup for f in `find "$LD_CONFIG_FOLDER" -name "*.src" -type f`; do echo "Removing config file: $f" rm -rf "$f" done echo "Config cleared. Use 'daisy_restore' if you would like to undo this." daisy_reload } function daisy_restore { for f in `find "$LD_CONFIG_FOLDER" -name "*.src" -type f`; do unbak "$f" bak "$f" 1>/dev/null done echo "Config restored. Backups have been retained." daisy_reload } # Aliases for front-facing daisy_ functions function _daisy_def_alias { alias ld_$1=daisy_$1 } _daisy_def_alias reload _daisy_def_alias enc _daisy_def_alias enc_multi _daisy_def_alias enc_folder _daisy_def_alias dec _daisy_def_alias dec_multi _daisy_def_alias alias _daisy_def_alias unalias _daisy_def_alias backup _daisy_def_alias clear _daisy_def_alias restore _daisy_def_alias help _daisy_def_alias list _daisy_source_configs ############################################################################### # end of FUNCTIONS and ALIASES ################################################ ############################################################################### # End of user section! export LD_AVAILABLE=1 [ -d "$LD_FOLDER" ] && export LD_AVAILABLE=1 # Help function, courtesy of Google Gemini function daisy_help() { local target_tool="$1" local file="$LD_FOLDER/README.md" # 1. Extract the block between the new headers sed -n '/--- BEGIN OF DAISY HELP ---/,/--- END OF DAISY HELP ---/{//!p;}' "$file" | if [ -z "$target_tool" ]; then # If no argument, print the whole help text cat else # 2. Parse specific tool awk -v query="$target_tool" ' BEGIN { found=0; printing=0 } # Match lines defining tools (e.g., " - calm:" or " - bak/unbak:") $0 ~ /^[[:space:]]*- / { printing=0 # Stop printing previous tool # Clean the line to get the "signature" # " - bak/unbak:" becomes "bak/unbak" sig = $0 sub(/^[[:space:]]*- /, "", sig) sub(/:[[:space:]]*$/, "", sig) # Check for exact match OR match within a slash-separated list # This handles "bak", "unbak", and "daisy_alias" split(sig, names, "/") is_match = 0 if (sig == query) is_match = 1 else { for (i in names) { if (names[i] == query) { is_match = 1; break } } } if (is_match) { printing=1 found=1 print $0 # Print the header line (e.g., " - bak/unbak:") next } } # Print description lines if we are in a "found" block printing { # Stop if we hit the start of the NEXT tool if ($0 ~ /^[[:space:]]*- /) { printing=0; next } print } END { if (found == 0) { print "Tool '"'"'" query "'"'"' not found in README.md." } } ' fi } # Courtesy of Google Gemini daisy_list() { local file="$LD_FOLDER/README.md" echo "Available LACKADAISICAL commands:" # Extract block -> Find tool lines -> Clean formatting -> Print sed -n '/--- BEGIN OF DAISY HELP ---/,/--- END OF DAISY HELP ---/{//!p;}' "$file" | awk ' /^[[:space:]]*- / { # Remove indentation and "- " sub(/^[[:space:]]*- /, ""); # Remove trailing ":" sub(/:[[:space:]]*$/, ""); print " " $0 } ' | sort }