- Change shebangs to use 'env' in prep for NixOS (and others) - `agenda` now has a symlink to the underlying tree structure named ".tree" - `binbox` has more checking, being prepped for automatic export of symlinks. - `cdz` now symlinks to the archive directory using "<archive name>.tmp". This is less confusing than a randomized extraction folder, and adds the benefit of being in the FS-tree of the original archive. - daisy.source changes: > Added basic debug mode. > `ched` can now set global (all of the shell) editor or local (lackadaisical utils only) editor. > The encoding/decoding functions have been expanded and improved. Extra checking, permissions are stored as well, `daisy_dec` can handle stdin. - Added new functions to manage lackadaisical config: daisy_backup (runs `bak` on config files), daisy_clear (removes config files), and daisy_restore (restores config files archived using bak). - All functions/tools prefixed with 'daisy_' are now also available prefixed with 'ld_' instead - Added a new utility, `clip`. This utility keeps a local variable that can be set if given args and get if no args are supplied. Basically a simple clipboard local to the ptty. Supports stdin.
663 lines
15 KiB
Text
Executable file
663 lines
15 KiB
Text
Executable file
#!/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 <lackadaisical-root>/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: <file>
|
|
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
|
|
}
|