#!/usr/bin/env bash # LACKADAISICAL CLI # Environment Setup LD_CONFIG_FOLDER="${LD_CONFIG_FOLDER:-$HOME/.config/lackadaisical}" LD_ALIASFILE="${LD_ALIASFILE:-$LD_CONFIG_FOLDER/aliases.src}" LD_EDITORFILE="${LD_EDITORFILE:-$LD_CONFIG_FOLDER/editor.src}" LD_ESOURCEFILE="${LD_ESOURCEFILE:-$LD_CONFIG_FOLDER/extra.src}" if [[ -z "$LD_FOLDER" ]]; then LD_FOLDER=$(dirname "$(realpath "$0")") fi ld_dbg() { if [[ $_LD_DEBUG == 1 ]]; then echo "DEBUG: $*" >&2 fi } cmd_editor() { local editor=${LD_EDITOR:-$EDITOR} ld_dbg "Opening $editor to edit file: $1" $editor "$1" } _daisy_enc() { local enc_is_folder="${ENC_IS_FOLDER:-0}" 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 local file_dir="" local file_name="" local perms=755 local target=$1 if [[ ! -t 0 ]] && [[ $enc_is_folder == 0 ]]; then file_dir="." file_name="$1" 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 local base64_inner base64_inner=$(cat "${1:-/dev/stdin}" | base64 | tr -d '\n') 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 } _daisy_dec() { if [ -t 0 ] && [ -z "$1" ]; then echo "$0: No arguments or stdin specified!" return 1 fi local data data=$(cat "${1:-/dev/stdin}" | grep -v "#") echo -e "$data" | cut -d "=" -f 2- | cut -b 2- | head -c -2 | base64 -d } _daisy_unalias() { local unalias_param="$1" if [[ $unalias_param =~ ^[0-9]+$ ]]; then unalias_param=$(head -"$unalias_param" "$LD_ALIASFILE" 2>/dev/null | tail -1 | cut -d "=" -f 1 | awk '{print $2}') fi if [[ -z "$unalias_param" ]]; then return fi if [[ -f "$LD_ALIASFILE" ]]; then local newdata newdata=$(grep -v "alias $unalias_param" "$LD_ALIASFILE" 2>/dev/null) cp "$LD_ALIASFILE" "$LD_ALIASFILE.bak" 2>/dev/null echo -e "$newdata" > "$LD_ALIASFILE" fi source "$LD_ALIASFILE" echo "Sourcing..." } # --- Command Functions --- cmd_help() { local target_tool="$1" local file="$LD_FOLDER/README.md" # 1. Extract the block between the help markers 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., "< cdz >" or "< daisy welcome >") $0 ~ /^[[:space:]]*< .* >/ { printing=0 # Stop printing previous tool # Clean the line to get the "signature" # "< daisy welcome >" becomes "daisy welcome" sig = $0 sub(/^[[:space:]]*< /, "", sig) sub(/ >[[:space:]]*$/, "", sig) # Check for exact match OR match within a slash/space-separated list 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 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 } cmd_list() { local file="$LD_FOLDER/README.md" if [[ ! -f "$file" ]]; then echo "README.md not found at $file" exit 1 fi echo "Available LACKADAISICAL commands:" sed -n '/--- BEGIN OF DAISY HELP ---/,/--- END OF DAISY HELP ---/{//!p;}' "$file" | awk ' /^[[:space:]]*< / { sub(/^[[:space:]]*< /, ""); sub(/ >[[:space:]]*$/, ""); print " " $0 } ' | sort } cmd_enc() { case "$1" in multi) shift for f in "$@"; do _daisy_enc "$f"; done ;; folder) if ! command -v tree >/dev/null 2>&1; then echo "This function requires the utility 'tree'. Please install it." exit 1 fi shift local dir="$1" if [[ ! -d "$dir" ]]; then echo "Directory not found: $dir" exit 1 fi cd "$dir" || exit 1 tree -fia --noreport . | sed 1d | while read -r item; do ENC_IS_FOLDER=1 _daisy_enc "$item" done ;; *) _daisy_enc "$@" ;; esac } cmd_dec() { case "$1" in multi) shift local arg1=$1 local arg2=$2 if [ ! -t 0 ]; then arg2=$1 arg1=/dev/stdin fi [[ -t 0 ]] && [[ ! -f "$arg1" ]] && echo "daisy dec multi: No input file specified" && exit 1 [[ ! -d "$arg2" ]] && echo "daisy dec multi: No output directory specified" && exit 1 local folder="" while IFS= read -r line; do if [[ "$line" == "daisy_create_folder="* ]]; then folder=$(echo "$line" | cut -d "=" -f 2) mkdir -p "$arg2/$folder" elif [[ "$line" == "daisy_folder"* ]]; then folder=$(echo "$line" | cut -d "=" -f 2) elif [[ "$line" == "daisy_data_base64"* ]]; then local file=$(echo "$line" | cut -d "_" -f 4- | cut -d "=" -f 1) mkdir -p "$arg2/$folder" _daisy_dec <(echo "$line") > "$arg2/$folder/$file" elif [[ "$line" == "daisy_perms"* ]]; then local file=$(echo "$line" | cut -d "_" -f 3- | cut -d "=" -f 1) local perms=$(echo "$line" | cut -d "_" -f 3- | cut -d "=" -f 2) chmod "$perms" "$arg2/$folder/$file" fi done < "$arg1" ;; *) _daisy_dec "$@" ;; esac } cmd_alias() { local alias_param="$*" if [[ -z "$alias_param" ]]; then echo "Active lackadaisical alias lines:" local linenum=1 if [[ -f "$LD_ALIASFILE" ]]; then while IFS= read -r line; do line=$(echo "$line" | sed 's/alias //g') echo "$linenum: $line" ((linenum++)) done < "$LD_ALIASFILE" fi else local alias_name=$(echo "$alias_param" | grep -o ".*=" | tr -d =) if [[ $alias_name =~ ^[0-9]+$ ]]; then echo "An alias cannot start with a number! Exiting." exit 1 fi _daisy_unalias "$alias_name" echo "alias ${alias_param%=*}=\"${alias_param#*=}\"" >> "$LD_ALIASFILE" fi source "$LD_ALIASFILE" echo "Sourcing..." } cmd_clear() { find "$LD_CONFIG_FOLDER" -name "*.src" -type f | while read -r f; do cp -R "$f" "$f.bak" echo "Removing config file: $f" rm -rf "$f" done echo "Config cleared. Use 'daisy restore' if you would like to undo this." } cmd_restore() { find "$LD_CONFIG_FOLDER" -name "*.src.bak" -type f | while read -r f; do local target="${f%.bak}" cp -R "$f" "$target" rm -rf "$f" echo "Restored backup: $target <-- $f" done echo "Config restored. Backups have been retained." } cmd_check() { local failed=0 local output="" check_bin() { str="" if command -v "$1" >/dev/null 2>&1; then str="[ \e[32mOK\e[0m ] $1\n" else str="[ \e[31m--\e[0m ] $1 ($2)\n" failed=1 fi output+=$str echo -e -n "$str" } echo "--- Core (Essential) ---" check_bin bash "Shell environment" check_bin awk "Text processing" check_bin sed "Stream editing" check_bin grep "Pattern matching" check_bin base64 "Data encoding" check_bin realpath "Path resolution" echo -e "\n--- Enhanced UI & Navigation ---" check_bin fzf "Used by cdf" check_bin peco "Used by cdp, editpeco" check_bin tree "Used by cdp, editpeco, daisy_enc_folder" check_bin dialog "Used by ched" echo -e "\n--- Archives & Filesystems ---" check_bin archivemount "Used by cdz (mounting)" check_bin tar "Used by cdz (extraction)" check_bin unzip "Used by cdz (extraction)" check_bin unrar "Used by cdz (extraction)" check_bin mksquashfs "Used by squasher make" check_bin rsync "Used by squasher destroy" check_bin sshfs "Used by sshp" check_bin fusermount "Used by sshp, cdz" echo -e "\n--- System ---" check_bin sudo "Privilege escalation" check_bin systemctl "Service management" check_bin perl "Advanced error reporting" echo "==========================================" echo "Check complete." echo "==========================================" [[ DAISY_IGNORE_DEPS -eq 1 ]] && return 0 [[ $failed -eq 1 ]] && ex_msg="Missing packages. DAISY_IGNORE_DEPS=1 to override" if [ $failed -eq 1 ] ; then echo "Dependency report for LACKADAISICAL..." echo "==========================================" echo -e "$output" echo "==========================================" echo "Missing packages. DAISY_IGNORE_DEPS=1 to override" return 1 fi return 0 } cmd_combine() { local cmds=() local args=() local separator_found=false for item in "$@"; do if [[ "$item" == "--" ]]; then separator_found=true continue fi if $separator_found; then args+=("$item") else cmds+=("$item") fi done for cmd in "${cmds[@]}"; do set -- "${args[@]}" eval "$cmd" done } # --- Dispatcher --- daisy () { case "$1" in help) shift; cmd_help "$@" ;; list) shift; cmd_list "$@" ;; enc) shift; cmd_enc "$@" ;; dec) shift; cmd_dec "$@" ;; alias) shift; cmd_alias "$@" ;; unalias) shift; _daisy_unalias "$@" ;; backup) shift; find "$LD_CONFIG_FOLDER" -name "*.src" -type f | while read -r f; do cp -R "$f" "$f.bak"; echo "Backup made: $f --> $f.bak"; done ;; clear) shift; cmd_clear ;; check) shift; cmd_check ;; restore) shift; cmd_restore ;; combine) shift; cmd_combine ;; editor) shift; cmd_editor "$@" ;; wait) if [[ "$2" == "for" && "$3" == "editor" ]]; then shift 3 _daisy_wait_for_editor "$@" else echo "Unknown daisy command: wait $*" return 1 fi ;; reload) LD_INTERNAL=0 source "$LD_SOURCE_FILE" ;; *) echo "Unknown daisy command: $1" return 1 ;; esac }