Complete overhaul of how daisy core tools are called, as well as changes to the help system

This commit is contained in:
Sam Hardeman 2026-03-30 01:48:18 +02:00
parent 74ed7582f9
commit 3fa3daacba
9 changed files with 1263 additions and 682 deletions

486
README.md
View file

@ -17,211 +17,287 @@ At first use, `lackadaisical` will provide you with information via `daisy_help`
## Getting started ## Getting started
``` ```
--- BEGIN OF DAISY HELP --- <!-- --- BEGIN OF DAISY HELP --- -->
===============================================================================
Thanks for installing LACKADAISICAL!
This project aims to provide useful utilities as well as learning material.
It is still under heavy development, not all of the things on this
list are present/implemented. Utils marked with * are incomplete.
This suite provides a number of functions, aliases and scripts.
They are all aimed at enhancing your efficiency.
To uninstall LACKADAISICAL, simply remove the source line from your
shell RC, and reload it. This does not remove the files!
You will also need to manually clear the configuration data in '<home>/.config/
lackadaisical` if you so desire.
To read this notice again, call the function 'daisy_help'.
=============================================================================== ===============================================================================
These are the included binaries and utilities: These are the included binaries and utilities:
- cdz: ················································································
This utility extracts an archive to /tmp and changes < cdz >
directory to it in a new shell instance. Upon exit, ················································································
the files are wiped. If `archivemount` is present, This utility extracts an archive to /tmp and changes directory to it in a new
it will be used to mount the archive instead! You can shell instance. Upon exit, the files are wiped. If `archivemount` is present,
bypass this behavior by specifying an env value of; it will be used to mount the archive instead! You can bypass this behavior by
NO_ARCHIVEMOUNT=1. The standard script supports zip, specifying an env value of; NO_ARCHIVEMOUNT=1. The standard script supports
tarballs, and rar archives. We recommend relying on zip, tarballs, and rar archives. We recommend relying on archivemount` if you
archivemount` if you have it installed. have it installed. Use "--check" to only check if a file is an archive. It
Use "--check" to only check if a file is an archive. returns 0 if it is, 1 otherwise.
It returns 0 if it is, 1 otherwise. ················································································
- squasher:
These convenient set of tools allow you to easily create < squasher >
XZ-compressed SquashFS images from existing folders to save ················································································
disk space. The resulting folder is still writable since it is These convenient set of tools allow you to easily create XZ-compressed SquashFS
mounted using an 'overlay' system. You can use 'squasher make' images from existing folders to save disk space. The resulting folder is still
to compresses an existing folder. These folders are automatically writable since it is mounted using an 'overlay' system. You can use 'squasher
mounted when you use 'cd' to navigate to them (via an alias). make' to compresses an existing folder. These folders are automatically mounted
When using 'make-squash-image' on an already mounted folder, it will when you use 'cd' to navigate to them (via the 'multicd' alias). When using
instead update the existing image. As of writing, we do not have a 'make-squash-image' on an already mounted folder, it will update the existing
SystemD service to auto-mount, however, you cam easily add auto-mount image. As of writing, we do not have a SystemD service to auto-mount, however,
as a cron job. Here is a list of tools, they all take the same you cam easily add auto-mount as a cron job. Here is a list of tools, they all
folder argument: take the same folder argument:
> squasher make: Converts the folder into a hidden image - squasher make: Converts the folder into an image on the same disk as the
on the same disk as the folder. folder.
> squasher mount: Sets up a mount for the XZ image alongside - squasher mount: Sets up a mount for the XZ image alongside
directories for changes OverlayFS for changes.
> squasher umount: Self-explanatory. - squasher umount: Fully unmounts the image and overlay filesystem.
> squasher destroy: Extracts the image and essentially reverts - squasher destroy: Extracts the image and essentially reverts everything.
everything. File changes are kept, however. All file changes are kept and applied to the unextracted
- editx: data.
Uses your standard CLI editor to create/modify a ················································································
file and make it executable.
- filewait: < editx >
This tool is given a filename of a file that does ················································································
not exist yet. When the file appears on disk, the Uses your standard CLI editor to create/modify a file and make it executable.
tool quits and simply returns the filename. This ················································································
can be used in personal workflows to stall a longer
command that relies on the existence of said file. < filewait >
- agenda: ················································································
Sets up a folder that is backed by a date-based tree This tool is given a filename of a file that does not exist yet. When the file
directory structure. Requires an argument for the name appears on disk, the tool quits and simply returns the filename. This can be
of the folder to generate. Generates a symlink in this used in personal workflows to stall a longer command that relies on the
name to a date sub-folder in a local '.daisy' folder. existence of said file.
Format is <dir> -> .daisy/<dir>/<year>/<month>/<day>. ················································································
Recommended to run via crontab - automatically cleans
up empty folders. < agenda >
A symbolic link to the base of the folder's tree, ".tree", ················································································
is created in the root of the specified directly. Sets up a folder that is backed by a date-based tree directory structure.
Can be used for everything you'd like to sort by date. Requires an argument for the name of the folder to generate. Generates a
For example; a diary, browser downloads, backups, code. symlink in this name to a date sub-folder in a local '.daisy' folder. Format is
- own: <dir> -> .daisy/<dir>/<year>/<month>/<day>. Recommended to run via crontab -
A simple utility. It's effectively an alias for automatically cleans up empty folders. A symbolic link to the base of the
"sudo chown -R user:user" on the target dir/file. folder's tree, ".tree", is created in the root of the specified directly. Can
Root permissions required! be used for everything you'd like to sort by date. For example; a diary,
- sshp: browser downloads, backups, code.
This is a wrapper for `ssh`, the meaning of the 'p' is "Plus". ················································································
Integrates SSHFS support. If both client and host have SSHFS,
this wrapper can be used to connect their file systems. < own >
For example, if you need to move files from one machine to ················································································
another, you could do something like this: A simple utility. It's effectively an alias for "sudo chown -R user:user" on
"sshp -m /:/mnt/pc -m /home/claire:/home/claire claire@pyon.net" the target dir/file. Root permissions required!
If privilege escalation is necessary for FS access, you will ················································································
be asked for a password.
- shrc: < sshp >
This tool allows you to edit the RC file for your ················································································
shell in your preferred editor. After saving, the This is a wrapper for `ssh`, the meaning of the 'p' is "Plus". Integrates SSHFS
file is sourced by your shell if modified. support. If both client and host have SSHFS, this wrapper can be used to
- sw: connect their file systems. For example, if you need to move files from one
A basic function that swaps two files by content. machine to another, you could do something like this: "sshp -m /:/mnt/pc -m
Useful for restoring backups. /home/claire:/home/claire claire@pyon.net" If privilege escalation is necessary
- what: for FS access, you will be asked for a password.
This is a tool similar to which and others, the key ················································································
difference is that it returns partial matches. It can
be used to search for binaries. < shrc >
- scripbox: ················································································
This tool can be used to pack bash scripts into one This tool allows you to edit the RC file for your shell in your preferred
big megascript, much like how `busybox` works. editor. After saving, the file is sourced by your shell if modified.
You can also make symlinks to it to invoke a specific ················································································
script.
- bak/unbak: < sw >
These small utilities make backups of files by making ················································································
a copy with a .bak suffix. Unbak reverses the process A basic function that swaps two files by content. Useful for restoring backups.
by using sw and removes the backup. ················································································
- lsa:
A simple alias for `ls -lah`. < what >
- lsn: ················································································
A simple alias for `ls -lah --sort=time --reverse`. This is a tool similar to which and others, the key difference is that it
- lss: returns partial matches. It can be used to search for binaries.
A simple alias for `ls -lah --sort=size --reverse`. ················································································
- editbin:
An alias for `editx $\(which <x>\)`. Saves on typing. < scriptbox >
- editpeco: ················································································
This function uses peco+tree like `cdp`, but opens This tool can be used to pack bash scripts into one big megascript, much like
your editor on the selected file(s). After you exit how `busybox` works. You can also make symlinks to it to invoke a specific
your editor(s), you are returned to peco where you left script.
off. ················································································
- ched:
Like chsh but for your editor (EDITOR env). A list < bak/unbak >
from which you can choose an installed editor ················································································
(CLI or GUI) is shown. This list is by no means complete. These small utilities make backups of files by making a copy with a .bak
The editor for LACKADAISICAL and the global editor are suffix. Unbak reverses the process by using sw and removes the backup.
separate. If the EDITOR variable is already defined, ················································································
only LD_EDITOR will be changed. LD_EDITOR is the editor
used by LACKADAISICAL utilities. < lsa >
To override the global EDITOR variable, pass "-g". ················································································
To restore the normal behavior of checking for an earlier A simple alias for `ls -lah`.
definition of EDITOR after passing "-g", run `ched` without ················································································
arguments.
- cdf: < lsn >
Use fzf to find a file and then cd to its location. ················································································
- cdp: A simple alias for `ls -lah --sort=time --reverse`.
Similar to `cdf` but uses tree+peco for the query. ················································································
- clip:
An extremely simple utility that functions as a clipboard of sorts. < lss >
To set the variable, run "clip <data>" or provide data via stdin. ················································································
To get the variable, simply run clip without any arguments. A simple alias for `ls -lah --sort=size --reverse`.
The variable is stored locally in the shell as "LD_CLIP". ················································································
- ldrc:
Edits daisy.source and re-sources it, similarly to shrc. < editbin >
Append "-e" to edit "extra.src", to add custom functions in the ················································································
lackadaisical namespace. An alias for `editx $\(which <x>\)`. Saves on typing.
- daisy_reload: ················································································
Re-sources daisy.source. Essentially `ldrc` without
editing. < editpeco >
- grab: ················································································
Alias for `awk '{print $x}'`, where x is a number. This function uses peco+tree like `cdp`, but opens your editor on the selected
E.g. `echo 'a b c' | grab 2` returns 'b'. file(s). After you exit your editor(s), you are returned to peco where you left
- daisy_cbin: off.
Contains the name of the current LACKADAISICAL ················································································
binary being run.
- daisy_enc: < ched >
Converts a file/stdin to a base64 block that can be ················································································
decoded by passing the output(s) to `daisy_dec`. Like chsh but for your editor (EDITOR env). A list from which you can choose an
The output of `daisy_enc` can be concatenated with the installed editor (CLI or GUI) is shown. This list is by no means complete. The
output of another encoded file to create a multi-file editor for LACKADAISICAL and the global editor are separate. If the EDITOR
base64 archive similar to `daisy_enc_multi`. variable is already defined, only LD_EDITOR will be changed. LD_EDITOR is the
Does not support symlinks yet, and will instead treat it editor used by LACKADAISICAL utilities. To override the global EDITOR variable,
as a full input file (reads the data of the linked file). pass "-g". To restore the normal behavior of checking for an earlier definition
When using stdin, please provide a filename as argument. of EDITOR after passing "-g", run `ched` without arguments.
- daisy_enc_multi: ················································································
A version of `daisy_enc` that encodes multiple
files and outputs `daisy_base64_data` blocks to a file < cdf >
or stdout. These outputs can be concatenated as well. ················································································
- daisy_dec: Use fzf to find a file and then cd to its location.
Converts `daisy_base64_data` blocks back to the form ················································································
it was in originally.
- daisy_dec_multi: < cdp >
A version of daisy_dec that runs on multiple input ················································································
blocks that are either stored in a file or stdin. Similar to `cdf` but uses tree+peco for the query.
- daisy_alias: ················································································
This utility sets persistent user aliases stored in
"~/.config/lackadaisical/aliases.src". < clip >
They will remain persistent until unaliased with ················································································
daisy_unalias. An extremely simple utility that functions as a clipboard of sorts. To set the
Call daisy_alias using '' instead of "". variable, run "clip <data>" or provide data via stdin. To get the variable,
E.g. `daisy_alias hello='echo "Hello!"' - instead of simply run clip without any arguments. The variable is stored locally in the
`daisy_alias hello="echo \"Hello!\"". shell as "LD_CLIP".
This prevents an early invocation of possible nested ················································································
aliases.
Call this function without arguments to get a list of < ldrc >
registered aliases as well as indices for easy unaliasing ················································································
using `daisy_unalias`. Edits daisy.source and re-sources it, similarly to shrc. Append "-e" to edit
- daisy_unalias: "extra.src", to add custom functions in the lackadaisical namespace.
This utility removes an alias from those registered with ················································································
daisy_alias. It accepts either an alias name, or an index
given by calling `daisy_alias` without arguments. < daisy reload >
In case of a mistake, a backup is made under the filename ················································································
"~/.config/lackadaisical/aliases.src.bak". Re-sources daisy.source. Essentially `ldrc` without editing.
To restore this file, you can use these commands: ················································································
`unbak ~/.config/lackadaisical/aliases.src'
`daisy_reload` < grab >
- daisy_list: ················································································
List all available commands without description. Alias for `awk '{print $x}'`, where x is a number. E.g. `echo 'a b c' | grab 2`
- daisy_clear: returns 'b'.
Removes all configuration, including aliases. ················································································
A backup is made and can be restored using "daisy_restore".
Triggers a reload. < daisy cbin >
- daisy_backup: ················································································
Backs up all config files. These can be restored using Contains the name of the current LACKADAISICAL binary being run.
"daisy_restore". ················································································
- daisy_restore:
Undoes "daisy_clear" by restoring config files. < daisy enc >
Triggers a reload. ················································································
- ld_*: Converts a file/stdin to a base64 block that can be decoded by passing the
All functions prefixed by "daisy_" are also available with output(s) to `daisy_dec`. The output of `daisy_enc` can be concatenated with
the prefix "ld_" via aliases provided in daisy.source. the output of another encoded file to create a multi-file base64 archive
--- END OF DAISY HELP --- similar to `daisy_enc_multi`. Does not support symlinks yet, and will instead
treat it as a full input file (reads the data of the linked file). When using
stdin, please provide a filename as argument.
················································································
< daisy enc multi >
················································································
A version of `daisy_enc` that encodes multiple files and outputs
`daisy_base64_data` blocks to a file or stdout. These outputs can be
concatenated as well.
················································································
< daisy enc folder >
················································································
A version of `daisy_enc` that encodes a whole folder into a series of
`daisy_base64_data` blocks. Requires the `tree` utility.
················································································
< daisy dec >
················································································
Converts `daisy_base64_data` blocks back to the form it was in originally.
················································································
< daisy dec multi >
················································································
A version of daisy_dec that runs on multiple input blocks that are either
stored in a file or stdin.
················································································
< daisy alias >
················································································
This utility sets persistent user aliases stored in
"~/.config/lackadaisical/aliases.src". They will remain persistent until
unaliased with daisy_unalias. Call daisy_alias using '' instead of "". E.g.
`daisy_alias hello='echo "Hello!"' - instead of `daisy_alias hello="echo
\"Hello!\"". This prevents an early invocation of possible nested aliases. Call
this function without arguments to get a list of registered aliases as well as
indices for easy unaliasing using `daisy_unalias`.
················································································
< daisy unalias >
················································································
This utility removes an alias from those registered with daisy_alias. It
accepts either an alias name, or an index given by calling `daisy_alias`
without arguments. In case of a mistake, a backup is made under the filename
"~/.config/lackadaisical/aliases.src.bak". To restore this file, you can use
these commands: `unbak ~/.config/lackadaisical/aliases.src' `daisy_reload`
················································································
< daisy list >
················································································
List all available commands, utilities and tools - without description.
················································································
< daisy clear >
················································································
Removes all configuration, including aliases. A backup is made and can be
restored using "daisy_restore". Triggers a reload.
················································································
< daisy backup >
················································································
Backs up all config files. These can be restored using "daisy_restore".
················································································
< daisy restore >
················································································
Undoes "daisy_clear" by restoring config files. Triggers a reload.
················································································
< daisy_* /ld_* >
················································································
All functions prefixed by "daisy" are also available as alias with the prefixes
"daisy_" and "ld_".
················································································
< daisy help >
················································································
Shows this exact interface for information about different utilities and
functions provided by `lackadaisical`.
- help <command|utility|tool>: Shows a help entry for a particular command,
including allternative function names.
- help Shows fully formatted help entries for every
function listed by "daisy list" (recommended
for first-time users).
················································································
< daisy welcome >
················································································
Showws some basic post-installation information about LACKADAISICAL and how to
use it.
················································································
<!-- --- END OF DAISY HELP --- -->
``` ```

48
check.sh Executable file
View file

@ -0,0 +1,48 @@
#!/usr/bin/env bash
# Lackadaisical Dependency Checker
echo "Checking dependencies for LACKADAISICAL..."
echo "=========================================="
check_bin()
{
if [[ $1 ]];
then echo -e "[ \e[32mOK\e[0m ] $1 ($2)"
return 0
else
echo -e "[ \e[31m--\e[0m ] $1 ($2)"
return 1
fi
}
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."

351
daisy Executable file
View file

@ -0,0 +1,351 @@
#!/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
}
# --- Internal Utils ---
_daisy_wait_for_editor()
{
local pname="$1"
local fname="$2"
sleep 1
while true; do
alive=$(pgrep -f "$pname.*$fname")
if [[ $alive == "" ]]; then
break
fi
sleep 1
done
}
_daisy_editor()
{
local editor=${LD_EDITOR:-$EDITOR}
ld_dbg "Opening $editor to edit file: $1"
$editor "$1"
sleep 1
_daisy_wait_for_editor "$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
}
# --- Command Functions ---
cmd_help()
{
local target_tool="$1"
local file="$LD_FOLDER/README.md"
if [[ ! -f "$file" ]]; then
echo "README.md not found at $file"
exit 1
fi
sed -n '/--- BEGIN OF DAISY HELP ---/,/--- END OF DAISY HELP ---/{//!p;}' "$file" |
if [ -z "$target_tool" ]; then
cat
else
awk -v query="$target_tool" '
BEGIN { found=0; printing=0 }
$0 ~ /^[[:space:]]*- / {
printing=0
sig = $0
sub(/^[[:space:]]*- /, "", sig)
sub(/:[[:space:]]*$/, "", sig)
split(sig, names, "/")
is_match = 0
if (sig == query) is_match = 1
else {
for (i in names);
do
if (names[i] == query) { is_match = 1; break }
done
}
if (is_match) {
printing=1
found=1
print $0
next
}
}
printing {
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
}
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_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 ---
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 ;;
restore) shift; cmd_restore ;;
combine) shift; cmd_combine ;;
editor) shift; _daisy_editor "$@" ;;
wait)
if [[ "$2" == "for" && "$3" == "editor" ]]; then
shift 3
_daisy_wait_for_editor "$@"
else
echo "Unknown daisy command: wait $*"
exit 1
fi
;;
reload)
LD_INTERNAL=0 source "$LD_SOURCE_FILE"
;;
*)
echo "Unknown daisy command: $1"
exit 1
;;
esac

424
daisy.command.source Executable file
View file

@ -0,0 +1,424 @@
#!/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
}

View file

@ -1,3 +1,4 @@
#!/usr/bin/env bash
#!/usr/bin/env -S echo "This file can only be sourced, not run stand-alone." #!/usr/bin/env -S echo "This file can only be sourced, not run stand-alone."
# LACKADAISICAL SOURCE-ABLE FILE # LACKADAISICAL SOURCE-ABLE FILE
@ -63,21 +64,20 @@ export LD_AVAILABLE=0
# Config folder setup # Config folder setup
export LD_CONFIG_FOLDER="$HOME/.config/lackadaisical" export LD_CONFIG_FOLDER="$HOME/.config/lackadaisical"
new_install=0 export LD_NEW_INSTALL=
if [[ ! -d "$LD_CONFIG_FOLDER" ]]; if [[ ! -d "$LD_CONFIG_FOLDER" ]];
then then
# Create the folder with its basics # Create the folder with its basics
mkdir -p "$LD_CONFIG_FOLDER" mkdir -p "$LD_CONFIG_FOLDER"
daisy_help
new_install=1 new_install=1
fi fi
# Multiple default source files # Multiple default source files
# [LEA.TODO] Turn these into arrays # [LEA.TODO] Turn these into arrays
LD_ALIASFILE="$LD_CONFIG_FOLDER/aliases.src" export LD_ALIASFILE="$LD_CONFIG_FOLDER/aliases.src"
LD_EDITORFILE="$LD_CONFIG_FOLDER/editor.src" export LD_EDITORFILE="$LD_CONFIG_FOLDER/editor.src"
LD_ESOURCEFILE="$LD_CONFIG_FOLDER/extra.src" export LD_ESOURCEFILE="$LD_CONFIG_FOLDER/extra.src"
touch $LD_ALIASFILE touch $LD_ALIASFILE
touch $LD_EDITORFILE touch $LD_EDITORFILE
touch $LD_ESOURCEFILE touch $LD_ESOURCEFILE
@ -90,8 +90,8 @@ ld_dbg cat $LD_ESOURCEFILE
# Source everything in the config folder # Source everything in the config folder
function _daisy_source_configs function _daisy_source_configs
{ {
while IFS= read -r -d '' f; do while IFS= read -r -d '' f; do
source "$f" source "$f"
done < <(find "$LD_CONFIG_FOLDER" -name "*.src" -type f -print0) done < <(find "$LD_CONFIG_FOLDER" -name "*.src" -type f -print0)
} }
@ -101,7 +101,6 @@ then
export PATH="$PATH:$LD_FOLDER" export PATH="$PATH:$LD_FOLDER"
fi fi
# Set up the basic alias for `shrc` # Set up the basic alias for `shrc`
# Do not set these up if LD_INTERNAL=1 is set, or infinite recursion could # Do not set these up if LD_INTERNAL=1 is set, or infinite recursion could
# occur! # occur!
@ -114,26 +113,6 @@ fi
# FUNCTIONS and ALIASES ####################################################### # 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=$(pgrep -f "$pname.*$fname")
if [[ $alive == "" ]]
then
break
fi
sleep 1
done
}
function multicd function multicd
{ {
cdpath="$@" cdpath="$@"
@ -158,16 +137,6 @@ function multicd
alias cd=multicd alias cd=multicd
# Undocumented but internally used
function daisy_editor
{
editor=${LD_EDITOR:-$EDITOR};
ld_dbg echo Opening $editor to edit file: $1
$editor "$1"
sleep 1
daisy_wait_for_editor $editor "$1"
}
# bak and unbak # bak and unbak
function bak function bak
{ {
@ -319,158 +288,16 @@ function ched
source "$LD_EDITORFILE" source "$LD_EDITORFILE"
} }
function daisy_reload
{
LD_INTERNAL=0 source "$LD_SOURCE_FILE"
}
function ldrc function ldrc
{ {
ARG=$1 ARG=$1
SOURCE="$LD_SOURCE_FILE" SOURCE="$LD_SOURCE_FILE"
[[ "$ARG" == "-e" ]] && SOURCE="$LD_ESOURCEFILE" [[ "$ARG" == "-e" ]] && SOURCE="$LD_ESOURCEFILE"
daisy_editor "$SOURCE" daisy editor "$SOURCE"
LD_INTERNAL=0 source "$SOURCE" LD_INTERNAL=0 source "$SOURCE"
} }
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 # Saves a bit on typing
function grab function grab
{ {
@ -496,95 +323,13 @@ function clip
echo "Variable set to \"$LD_CLIP\"." echo "Variable set to \"$LD_CLIP\"."
} }
function daisy_unalias # Aliases for front-facing daisy commands
{
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")
[[ NO_BAK -lt 1 ]] && 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 function _daisy_def_alias
{ {
alias ld_$1=daisy_$1 # Map underscores in name to spaces in command if needed
local cmd=$(echo $1 | tr '_' ' ')
alias ld_$1="daisy $cmd"
alias daisy_$1="daisy $cmd"
} }
_daisy_def_alias reload _daisy_def_alias reload
@ -598,97 +343,31 @@ _daisy_def_alias unalias
_daisy_def_alias backup _daisy_def_alias backup
_daisy_def_alias clear _daisy_def_alias clear
_daisy_def_alias restore _daisy_def_alias restore
_daisy_def_alias combine
_daisy_def_alias help _daisy_def_alias help
_daisy_def_alias list _daisy_def_alias list
_daisy_source_configs _daisy_source_configs
###############################################################################
# check for dependencies @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
###############################################################################
if [[ $new_install -eq 1 ]];
then
daisy check
fi
############################################################################### ###############################################################################
# end of FUNCTIONS and ALIASES ################################################ # end of FUNCTIONS and ALIASES ################################################
############################################################################### ###############################################################################
source "/etc/lackadaisical/daisy.command.source"
###############################################################################
# Autocomplete for `daisy` command ############################################
###############################################################################
# End of user section! # End of user section!
export LD_AVAILABLE=1 export LD_AVAILABLE=1
[ -d "$LD_FOLDER" ] && 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
}

View file

@ -36,7 +36,8 @@ case_cm2=" exec help_fn"
case_cm3=" ;;" case_cm3=" ;;"
case_p2="esac" case_p2="esac"
func_p1="function $OPTION_fn() {" func_p1="function $OPTION_fn()
{"
func_p2=" exit($?)" func_p2=" exit($?)"
func_p3="}" func_p3="}"

2
shrc
View file

@ -14,5 +14,5 @@ shellname=$(basename $SHELL)
rc_name="."$shellname"rc" rc_name="."$shellname"rc"
rc_path="$HOME/$rc_name" rc_path="$HOME/$rc_name"
daisy_editor "$rc_path" daisy editor "$rc_path"
source "$rc_path" source "$rc_path"

249
squasher
View file

@ -6,13 +6,14 @@ BIN_DIR=$(dirname "$(readlink -f "$0")")
COMMAND="$1" COMMAND="$1"
DIR="$2" DIR="$2"
usage() { usage()
echo "Usage: $0 {make|mount|umount|destroy} <directory>" {
exit 1 echo "Usage: $0 {make|mount|umount|destroy} <directory>"
exit 1
} }
if [[ -z "$COMMAND" || -z "$DIR" ]]; then if [[ -z "$COMMAND" || -z "$DIR" ]]; then
usage usage
fi fi
DIR=$(readlink -f "$DIR") DIR=$(readlink -f "$DIR")
@ -24,156 +25,156 @@ OVERLAY_WORK="$OVERLAY_ROOT/work"
OVERLAY_TARG="$DIR" OVERLAY_TARG="$DIR"
case "$COMMAND" in case "$COMMAND" in
make) make)
if [[ ! -d "$DIR" ]]; then if [[ ! -d "$DIR" ]]; then
echo "Error: Directory \"$DIR\" does not exist!" echo "Error: Directory \"$DIR\" does not exist!"
exit 1 exit 1
fi fi
echo "Checking system requirements (FUSE, SquashFS, OverlayFS)..." echo "Checking system requirements (FUSE, SquashFS, OverlayFS)..."
for fs in fuse squashfs overlay; do for fs in fuse squashfs overlay; do
if ! grep -q "$fs" /proc/filesystems; then if ! grep -q "$fs" /proc/filesystems; then
echo "Attempting to load $fs module..." echo "Attempting to load $fs module..."
sudo modprobe "$fs" || { echo "Error: $fs is not supported."; exit 1; } sudo modprobe "$fs" || { echo "Error: $fs is not supported."; exit 1; }
fi fi
done done
DIRSIZE=$(du -sh "$DIR" | cut -f1) DIRSIZE=$(du -sh "$DIR" | cut -f1)
RECREATE=false RECREATE=false
mkdir -p "$OVERLAY_ROOT" mkdir -p "$OVERLAY_ROOT"
if [[ -f "${OVERLAY_ROOT}.img" ]]; then if [[ -f "${OVERLAY_ROOT}.img" ]]; then
echo "Existing image found, updating..." echo "Existing image found, updating..."
"$0" mount "$DIR" "$0" mount "$DIR"
DIRSIZE=$(du -sh "$DIR" | cut -f1) DIRSIZE=$(du -sh "$DIR" | cut -f1)
RECREATE=true RECREATE=true
fi fi
echo "Compressing \"$DIR\"..." echo "Compressing \"$DIR\"..."
sudo mksquashfs "$DIR" "${OVERLAY_ROOT}.img.1" -noappend -comp xz || exit 1 sudo mksquashfs "$DIR" "${OVERLAY_ROOT}.img.1" -noappend -comp xz || exit 1
if [[ "$RECREATE" == "true" ]]; then if [[ "$RECREATE" == "true" ]]; then
echo "Cleaning up old layers..." echo "Cleaning up old layers..."
"$0" umount "$DIR" "$0" umount "$DIR"
if [[ -n "$OVERLAY_ROOT" && "$OVERLAY_ROOT" != "/" ]]; then if [[ -n "$OVERLAY_ROOT" && "$OVERLAY_ROOT" != "/" ]]; then
sudo rm -rf "$OVERLAY_UPPER" "$OVERLAY_LOWER" "$OVERLAY_WORK" sudo rm -rf "$OVERLAY_UPPER" "$OVERLAY_LOWER" "$OVERLAY_WORK"
rm -f "${OVERLAY_ROOT}.img" rm -f "${OVERLAY_ROOT}.img"
fi fi
fi fi
mv "${OVERLAY_ROOT}.img.1" "${OVERLAY_ROOT}.img" mv "${OVERLAY_ROOT}.img.1" "${OVERLAY_ROOT}.img"
mkdir -p "$OVERLAY_UPPER" "$OVERLAY_LOWER" "$OVERLAY_WORK" "$OVERLAY_TARG" mkdir -p "$OVERLAY_UPPER" "$OVERLAY_LOWER" "$OVERLAY_WORK" "$OVERLAY_TARG"
sudo rm -rf "$DIR" sudo rm -rf "$DIR"
mkdir -p "$DIR" mkdir -p "$DIR"
touch "$DIR/.needs_mount" touch "$DIR/.needs_mount"
echo "-------------------------------------------------------------------------" echo "-------------------------------------------------------------------------"
echo "Storage Stats:" echo "Storage Stats:"
echo " Original size: $DIRSIZE" echo " Original size: $DIRSIZE"
echo " Compressed: $(du -sh "${OVERLAY_ROOT}.img" | cut -f1)" echo " Compressed: $(du -sh "${OVERLAY_ROOT}.img" | cut -f1)"
echo "-------------------------------------------------------------------------" echo "-------------------------------------------------------------------------"
SERVICE_CONTENT="[Unit] SERVICE_CONTENT="[Unit]
Description=SquashFS Mount for %I Description=SquashFS Mount for %I
After=local-fs.target After=local-fs.target
[Service] [Service]
Type=oneshot Type=oneshot
RemainAfterExit=yes RemainAfterExit=yes
ExecStart=${BIN_DIR}/squasher mount %I ExecStart=${BIN_DIR}/squasher mount %I
ExecStop=${BIN_DIR}/squasher umount %I ExecStop=${BIN_DIR}/squasher umount %I
[Install] [Install]
WantedBy=multi-user.target" WantedBy=multi-user.target"
echo "$SERVICE_CONTENT" | sudo tee /etc/systemd/system/squash-mount@.service > /dev/null echo "$SERVICE_CONTENT" | sudo tee /etc/systemd/system/squash-mount@.service > /dev/null
sudo systemctl daemon-reload sudo systemctl daemon-reload
ESC_PATH=$(systemd-escape -p "$DIR") ESC_PATH=$(systemd-escape -p "$DIR")
SERVICE_NAME="squash-mount@$ESC_PATH.service" SERVICE_NAME="squash-mount@$ESC_PATH.service"
read -p "Enable auto-mount service ($SERVICE_NAME)? [y/N] " yn read -p "Enable auto-mount service ($SERVICE_NAME)? [y/N] " yn
if [[ "$yn" =~ ^[Yy]$ ]]; then if [[ "$yn" =~ ^[Yy]$ ]]; then
sudo systemctl enable --now "$SERVICE_NAME" sudo systemctl enable --now "$SERVICE_NAME"
sudo systemctl stop "$SERVICE_NAME" sudo systemctl stop "$SERVICE_NAME"
sudo systemctl start "$SERVICE_NAME" sudo systemctl start "$SERVICE_NAME"
else else
echo "Manual mount command: sudo systemctl start $SERVICE_NAME" echo "Manual mount command: sudo systemctl start $SERVICE_NAME"
fi fi
;; ;;
mount) mount)
if [[ ! -f "${OVERLAY_ROOT}.img" ]]; then if [[ ! -f "${OVERLAY_ROOT}.img" ]]; then
echo "Error: SquashFS image \"${OVERLAY_ROOT}.img\" not found." >&2 echo "Error: SquashFS image \"${OVERLAY_ROOT}.img\" not found." >&2
exit 1 exit 1
fi fi
"$0" umount "$DIR" 2>/dev/null "$0" umount "$DIR" 2>/dev/null
mkdir -p "$OVERLAY_LOWER" mkdir -p "$OVERLAY_LOWER"
sudo mount "${OVERLAY_ROOT}.img" "$OVERLAY_LOWER" -t squashfs -o loop sudo mount "${OVERLAY_ROOT}.img" "$OVERLAY_LOWER" -t squashfs -o loop
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
echo "Error: Failed to mount squashfs image." >&2 echo "Error: Failed to mount squashfs image." >&2
exit 1 exit 1
fi fi
sudo mount -t overlay none "$OVERLAY_TARG" \ sudo mount -t overlay none "$OVERLAY_TARG" \
-o lowerdir="$OVERLAY_LOWER",upperdir="$OVERLAY_UPPER",workdir="$OVERLAY_WORK" -o lowerdir="$OVERLAY_LOWER",upperdir="$OVERLAY_UPPER",workdir="$OVERLAY_WORK"
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
echo "Error: Failed to mount overlay." >&2 echo "Error: Failed to mount overlay." >&2
sudo umount "$OVERLAY_LOWER" 2>/dev/null sudo umount "$OVERLAY_LOWER" 2>/dev/null
exit 1 exit 1
fi fi
echo "SquashFS filesystem is mounted and ready." echo "SquashFS filesystem is mounted and ready."
;; ;;
umount) umount)
sudo umount -l -R "$OVERLAY_TARG" 2>/dev/null sudo umount -l -R "$OVERLAY_TARG" 2>/dev/null
sudo umount -l -R "$OVERLAY_LOWER" 2>/dev/null sudo umount -l -R "$OVERLAY_LOWER" 2>/dev/null
if mountpoint -q "$OVERLAY_TARG" || mountpoint -q "$OVERLAY_LOWER"; then if mountpoint -q "$OVERLAY_TARG" || mountpoint -q "$OVERLAY_LOWER"; then
echo "Warning: Filesystem is still mounted. Check for open processes." echo "Warning: Filesystem is still mounted. Check for open processes."
exit 1 exit 1
fi fi
echo "SquashFS filesystem has been unmounted." echo "SquashFS filesystem has been unmounted."
;; ;;
destroy) destroy)
if [[ ! -f "$DIR/.needs_mount" ]]; then if [[ ! -f "$DIR/.needs_mount" ]]; then
if ! mountpoint -q "$DIR"; then if ! mountpoint -q "$DIR"; then
echo "Error: $DIR is not a SquashFS directory." echo "Error: $DIR is not a SquashFS directory."
exit 1 exit 1
fi fi
fi fi
ESC_PATH=$(systemd-escape -p "$DIR") ESC_PATH=$(systemd-escape -p "$DIR")
SERVICE_NAME="squash-mount@$ESC_PATH.service" SERVICE_NAME="squash-mount@$ESC_PATH.service"
echo "Disabling service ($SERVICE_NAME)..." echo "Disabling service ($SERVICE_NAME)..."
sudo systemctl stop "$SERVICE_NAME" 2>/dev/null sudo systemctl stop "$SERVICE_NAME" 2>/dev/null
sudo systemctl disable "$SERVICE_NAME" 2>/dev/null sudo systemctl disable "$SERVICE_NAME" 2>/dev/null
echo "Ensuring image is mounted to preserve data..." echo "Ensuring image is mounted to preserve data..."
"$0" mount "$DIR" 1>/dev/null 2>/dev/null "$0" mount "$DIR" 1>/dev/null 2>/dev/null
echo "Destroying image and restoring data..." echo "Destroying image and restoring data..."
TEMP_DIR=$(mktemp -d /tmp/squash-dest.XXXXXXXX) TEMP_DIR=$(mktemp -d /tmp/squash-dest.XXXXXXXX)
sudo rsync -aX "$DIR/" "$TEMP_DIR/" || { echo "Error: Failed to copy data."; exit 1; } sudo rsync -aX "$DIR/" "$TEMP_DIR/" || { echo "Error: Failed to copy data."; exit 1; }
"$0" umount "$DIR" "$0" umount "$DIR"
sudo rm -rf "$DIR" sudo rm -rf "$DIR"
sudo mv "$TEMP_DIR" "$DIR" sudo mv "$TEMP_DIR" "$DIR"
sudo rm -rf "$OVERLAY_ROOT" "${OVERLAY_ROOT}.img" sudo rm -rf "$OVERLAY_ROOT" "${OVERLAY_ROOT}.img"
echo "Success: SquashFS image destroyed and data restored to \"$DIR\"." echo "Success: SquashFS image destroyed and data restored to \"$DIR\"."
;; ;;
*) *)
usage usage
;; ;;
esac esac

3
sshp
View file

@ -6,7 +6,8 @@ ssh_args=()
remote_port=$((10000 + RANDOM % 10000)) remote_port=$((10000 + RANDOM % 10000))
local_user=$(whoami) local_user=$(whoami)
usage() { usage()
{
echo "Usage: sshp -m <local>:<remote> [user@]host [ssh_options]" echo "Usage: sshp -m <local>:<remote> [user@]host [ssh_options]"
exit 1 exit 1
} }